Skip to content

Commit f3e593f

Browse files
committed
chore: add json.Marshaler/Unmarshaler to protobuf.ResourceSpec
Simplify JSON encoded resource encoding and decoding. Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
1 parent e5898d1 commit f3e593f

3 files changed

Lines changed: 50 additions & 7 deletions

File tree

pkg/resource/protobuf/spec.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
package protobuf
66

7-
import "google.golang.org/protobuf/proto"
7+
import (
8+
"encoding/json"
9+
10+
"google.golang.org/protobuf/proto"
11+
)
812

913
// Spec should be proto.Message and pointer.
1014
type Spec[T any] interface {
@@ -28,11 +32,23 @@ func (spec ResourceSpec[T, S]) DeepCopy() ResourceSpec[T, S] {
2832
}
2933
}
3034

35+
// MarshalJSON implements json.Marshaler.
36+
func (spec *ResourceSpec[T, S]) MarshalJSON() ([]byte, error) {
37+
return json.Marshal(spec.Value)
38+
}
39+
3140
// MarshalProto implements ProtoMarshaler.
32-
func (spec ResourceSpec[T, S]) MarshalProto() ([]byte, error) {
41+
func (spec *ResourceSpec[T, S]) MarshalProto() ([]byte, error) {
3342
return ProtoMarshal(spec.Value)
3443
}
3544

45+
// UnmarshalJSON implements json.Unmarshaler.
46+
func (spec *ResourceSpec[T, S]) UnmarshalJSON(bytes []byte) error {
47+
spec.Value = new(T)
48+
49+
return json.Unmarshal(bytes, &spec.Value)
50+
}
51+
3652
// UnmarshalProto implements protobuf.ResourceUnmarshaler.
3753
func (spec *ResourceSpec[T, S]) UnmarshalProto(protoBytes []byte) error {
3854
spec.Value = new(T)
@@ -41,13 +57,13 @@ func (spec *ResourceSpec[T, S]) UnmarshalProto(protoBytes []byte) error {
4157
}
4258

4359
// GetValue returns wrapped protobuf object.
44-
func (spec ResourceSpec[T, S]) GetValue() proto.Message { //nolint:ireturn
60+
func (spec *ResourceSpec[T, S]) GetValue() proto.Message { //nolint:ireturn
4561
return spec.Value
4662
}
4763

4864
// Equal implements spec equality check.
49-
func (spec ResourceSpec[T, S]) Equal(other interface{}) bool {
50-
otherSpec, ok := other.(ResourceSpec[T, S])
65+
func (spec *ResourceSpec[T, S]) Equal(other interface{}) bool {
66+
otherSpec, ok := other.(*ResourceSpec[T, S])
5167
if !ok {
5268
return false
5369
}

pkg/resource/typed/typed_resource.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func (t *Resource[T, RD]) Metadata() *resource.Metadata {
3838

3939
// Spec implements resource.Resource.
4040
func (t *Resource[T, RD]) Spec() interface{} {
41-
return t.spec
41+
return &t.spec
4242
}
4343

4444
// TypedSpec returns a pointer to spec field.
@@ -58,7 +58,7 @@ func (t *Resource[T, RD]) ResourceDefinition() spec.ResourceDefinitionSpec {
5858
return zero.ResourceDefinition(t.md, t.spec)
5959
}
6060

61-
// UnmarshalProto impelements protobuf.Unmarshaler interface in a generic way.
61+
// UnmarshalProto implements protobuf.Unmarshaler interface in a generic way.
6262
//
6363
// UnmarshalProto requires that the spec implements the protobuf.ProtoUnmarshaller interface.
6464
func (t *Resource[T, RD]) UnmarshalProto(md *resource.Metadata, protoBytes []byte) error {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
package typed_test
66

77
import (
8+
"encoding/json"
89
"testing"
910

1011
"github.com/stretchr/testify/assert"
1112

13+
"github.com/cosi-project/runtime/api/v1alpha1"
1214
"github.com/cosi-project/runtime/pkg/resource"
1315
"github.com/cosi-project/runtime/pkg/resource/meta"
16+
"github.com/cosi-project/runtime/pkg/resource/protobuf"
1417
"github.com/cosi-project/runtime/pkg/resource/typed"
1518
)
1619

@@ -67,3 +70,27 @@ func TestTypedResource(t *testing.T) {
6770
// check that getting resource definition actually works on phantom types
6871
asrt.Equal(res.ResourceDefinition().DisplayType, "test definition")
6972
}
73+
74+
type jsonResSpec = protobuf.ResourceSpec[v1alpha1.Metadata, *v1alpha1.Metadata]
75+
76+
//nolint:unused
77+
type jsonResRD struct{}
78+
79+
// ResourceDefinition ...
80+
//nolint:unused
81+
func (jsonResRD) ResourceDefinition(md resource.Metadata, spec jsonResSpec) meta.ResourceDefinitionSpec {
82+
return meta.ResourceDefinitionSpec{
83+
DisplayType: "test definition",
84+
}
85+
}
86+
87+
func TestUnmarshalJSON(t *testing.T) {
88+
res := typed.NewResource[jsonResSpec, jsonResRD](
89+
resource.NewMetadata("default", "type", "1", resource.VersionUndefined),
90+
jsonResSpec{},
91+
)
92+
93+
assert.NoError(t, json.Unmarshal([]byte(`{"id": "1"}`), res.Spec()))
94+
assert.NotNil(t, res.TypedSpec().Value)
95+
assert.Equal(t, "1", res.TypedSpec().Value.Id)
96+
}

0 commit comments

Comments
 (0)