Skip to content

Commit

Permalink
feat: respect custom JSON unmarshaler logic in protobuf resource specs
Browse files Browse the repository at this point in the history
If a protobuf spec implements `json.Unmarshaler`, use that function to unmarshal it from JSON, as the default `protojson.UnmarshalOptions` ignores it.

Add a test for both JSON and YAML custom unmarshaling logic.

Co-authored-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
Signed-off-by: Utku Ozdemir <utku.ozdemir@siderolabs.com>
  • Loading branch information
utkuozdemir and Unix4ever committed Sep 10, 2024
1 parent 950adb1 commit 8911486
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/resource/protobuf/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func (spec *ResourceSpec[T, S]) UnmarshalYAML(node *yaml.Node) error {
func (spec *ResourceSpec[T, S]) UnmarshalJSON(bytes []byte) error {
spec.Value = new(T)

if unmarshaler, ok := any(spec.Value).(json.Unmarshaler); ok {
return unmarshaler.UnmarshalJSON(bytes)
}

opts := protojson.UnmarshalOptions{}

return opts.Unmarshal(bytes, spec.Value)
Expand Down
81 changes: 81 additions & 0 deletions pkg/resource/protobuf/spec_unmarshal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package protobuf_test

import (
"encoding/json"
"strings"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/reflect/protoreflect"
"gopkg.in/yaml.v3"

"github.com/cosi-project/runtime/pkg/resource/protobuf"
)

type rawSpec struct {
Str string
Num int
}

type customUnmarshalerSpec struct {
Str string
Num int
}

func (spec *customUnmarshalerSpec) ProtoReflect() protoreflect.Message { return nil }

// UnmarshalJSON uppercases the string and doubles the number.
func (spec *customUnmarshalerSpec) UnmarshalJSON(data []byte) error {
var raw rawSpec

err := json.Unmarshal(data, &raw)
if err != nil {
return err
}

spec.Str = strings.ToUpper(raw.Str)
spec.Num = raw.Num * 2

return nil
}

// UnmarshalYAML lowercases the string and halves the number.
func (spec *customUnmarshalerSpec) UnmarshalYAML(node *yaml.Node) error {
var raw rawSpec

err := node.Decode(&raw)
if err != nil {
return err
}

spec.Str = strings.ToLower(raw.Str)
spec.Num = raw.Num / 2

return nil
}

func TestCustomJSONUnmarshal(t *testing.T) {
spec := protobuf.NewResourceSpec(&customUnmarshalerSpec{})

err := json.Unmarshal([]byte(`{"str":"aaaa","num":2222}`), &spec)
require.NoError(t, err)

assert.Equal(t, "AAAA", spec.Value.Str)
assert.Equal(t, 4444, spec.Value.Num)
}

func TestCustomYAMLUnmarshal(t *testing.T) {
spec := protobuf.NewResourceSpec(&customUnmarshalerSpec{})

err := yaml.Unmarshal([]byte(`str: AAAA
num: 2222`), &spec)
require.NoError(t, err)

assert.Equal(t, "aaaa", spec.Value.Str)
assert.Equal(t, 1111, spec.Value.Num)
}

0 comments on commit 8911486

Please sign in to comment.