protogen: Always marshal empty fields
This is how it was done previously:
a0a9b765f3/rpc/message/encoding.go (L31)
The tricky part is `[]byte` which is marshaled as `null` by easyjson
helper, but as `""` by protojson.
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
This commit is contained in:
parent
3e705a3cbe
commit
1ec1762d3a
20 changed files with 53 additions and 15 deletions
BIN
accounting/grpc/service_frostfs.pb.go
generated
BIN
accounting/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
accounting/grpc/types_frostfs.pb.go
generated
BIN
accounting/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
acl/grpc/types_frostfs.pb.go
generated
BIN
acl/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
ape/grpc/types_frostfs.pb.go
generated
BIN
ape/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
apemanager/grpc/service_frostfs.pb.go
generated
BIN
apemanager/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
container/grpc/service_frostfs.pb.go
generated
BIN
container/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
container/grpc/types_frostfs.pb.go
generated
BIN
container/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
lock/grpc/types_frostfs.pb.go
generated
BIN
lock/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
netmap/grpc/service_frostfs.pb.go
generated
BIN
netmap/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
netmap/grpc/types_frostfs.pb.go
generated
BIN
netmap/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
object/grpc/service_frostfs.pb.go
generated
BIN
object/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
object/grpc/types_frostfs.pb.go
generated
BIN
object/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
refs/grpc/types_frostfs.pb.go
generated
BIN
refs/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
session/grpc/service_frostfs.pb.go
generated
BIN
session/grpc/service_frostfs.pb.go
generated
Binary file not shown.
BIN
session/grpc/types_frostfs.pb.go
generated
BIN
session/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
status/grpc/types_frostfs.pb.go
generated
BIN
status/grpc/types_frostfs.pb.go
generated
Binary file not shown.
BIN
tombstone/grpc/types_frostfs.pb.go
generated
BIN
tombstone/grpc/types_frostfs.pb.go
generated
Binary file not shown.
|
@ -26,11 +26,37 @@ func nonZero[T protoInt]() T {
|
||||||
|
|
||||||
func TestStableMarshalSingle(t *testing.T) {
|
func TestStableMarshalSingle(t *testing.T) {
|
||||||
t.Run("empty", func(t *testing.T) {
|
t.Run("empty", func(t *testing.T) {
|
||||||
input := &generated.Primitives{}
|
t.Run("proto", func(t *testing.T) {
|
||||||
require.Zero(t, input.StableSize())
|
input := &generated.Primitives{}
|
||||||
|
require.Zero(t, input.StableSize())
|
||||||
|
|
||||||
r := input.MarshalProtobuf(nil)
|
r := input.MarshalProtobuf(nil)
|
||||||
require.Empty(t, r)
|
require.Empty(t, r)
|
||||||
|
})
|
||||||
|
t.Run("json", func(t *testing.T) {
|
||||||
|
input := &generated.Primitives{}
|
||||||
|
r, err := input.MarshalJSON()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, r)
|
||||||
|
|
||||||
|
var actual test.Primitives
|
||||||
|
require.NoError(t, protojson.Unmarshal(r, &actual))
|
||||||
|
|
||||||
|
t.Run("protojson compatibility", func(t *testing.T) {
|
||||||
|
data, err := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(&actual)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.JSONEq(t, string(data), string(r))
|
||||||
|
})
|
||||||
|
|
||||||
|
var actualFrostfs generated.Primitives
|
||||||
|
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
|
||||||
|
if len(actualFrostfs.FieldA) == 0 {
|
||||||
|
actualFrostfs.FieldA = nil
|
||||||
|
}
|
||||||
|
require.Equal(t, input, &actualFrostfs)
|
||||||
|
|
||||||
|
primitivesEqual(t, input, &actual)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
marshalCases := []struct {
|
marshalCases := []struct {
|
||||||
|
@ -77,13 +103,16 @@ func TestStableMarshalSingle(t *testing.T) {
|
||||||
require.NoError(t, protojson.Unmarshal(r, &actual))
|
require.NoError(t, protojson.Unmarshal(r, &actual))
|
||||||
|
|
||||||
t.Run("protojson compatibility", func(t *testing.T) {
|
t.Run("protojson compatibility", func(t *testing.T) {
|
||||||
data, err := protojson.Marshal(&actual)
|
data, err := protojson.MarshalOptions{EmitUnpopulated: true}.Marshal(&actual)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.JSONEq(t, string(data), string(r))
|
require.JSONEq(t, string(data), string(r))
|
||||||
})
|
})
|
||||||
|
|
||||||
var actualFrostfs generated.Primitives
|
var actualFrostfs generated.Primitives
|
||||||
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
|
require.NoError(t, actualFrostfs.UnmarshalJSON(r))
|
||||||
|
if len(actualFrostfs.FieldA) == 0 {
|
||||||
|
actualFrostfs.FieldA = nil
|
||||||
|
}
|
||||||
require.Equal(t, tc.input, &actualFrostfs)
|
require.Equal(t, tc.input, &actualFrostfs)
|
||||||
|
|
||||||
primitivesEqual(t, tc.input, &actual)
|
primitivesEqual(t, tc.input, &actual)
|
||||||
|
@ -94,7 +123,10 @@ func TestStableMarshalSingle(t *testing.T) {
|
||||||
|
|
||||||
func primitivesEqual(t *testing.T, a *generated.Primitives, b *test.Primitives) {
|
func primitivesEqual(t *testing.T, a *generated.Primitives, b *test.Primitives) {
|
||||||
// Compare each field directly, because proto-generated code has private fields.
|
// Compare each field directly, because proto-generated code has private fields.
|
||||||
require.Equal(t, a.FieldA, b.FieldA)
|
require.Equal(t, len(a.FieldA), len(b.FieldA))
|
||||||
|
if len(a.FieldA) != 0 {
|
||||||
|
require.Equal(t, a.FieldA, b.FieldA)
|
||||||
|
}
|
||||||
require.Equal(t, a.FieldB, b.FieldB)
|
require.Equal(t, a.FieldB, b.FieldB)
|
||||||
require.Equal(t, a.FieldC, b.FieldC)
|
require.Equal(t, a.FieldC, b.FieldC)
|
||||||
require.Equal(t, a.FieldD, b.FieldD)
|
require.Equal(t, a.FieldD, b.FieldD)
|
||||||
|
|
BIN
util/proto/test/custom/test_frostfs.pb.go
generated
BIN
util/proto/test/custom/test_frostfs.pb.go
generated
Binary file not shown.
|
@ -192,14 +192,17 @@ func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name strin
|
||||||
|
|
||||||
selector := name + "." + f.GoName
|
selector := name + "." + f.GoName
|
||||||
|
|
||||||
isNotDefault := notNil
|
// This code is responsible for ignoring default values.
|
||||||
if f.Desc.IsList() {
|
// We will restore it after having parametrized JSON marshaling.
|
||||||
isNotDefault = notEmpty
|
//
|
||||||
} else if f.Desc.Kind() != protoreflect.MessageKind {
|
// isNotDefault := notNil
|
||||||
_, isNotDefault = easyprotoKindInfo(f.Desc.Kind())
|
// if f.Desc.IsList() {
|
||||||
}
|
// isNotDefault = notEmpty
|
||||||
g.P("if ", isNotDefault(selector), "{")
|
// } else if f.Desc.Kind() != protoreflect.MessageKind {
|
||||||
defer g.P("}")
|
// _, isNotDefault = easyprotoKindInfo(f.Desc.Kind())
|
||||||
|
// }
|
||||||
|
// g.P("if ", isNotDefault(selector), "{")
|
||||||
|
// defer g.P("}")
|
||||||
|
|
||||||
g.P("if !first { out.RawByte(','); } else { first = false; }")
|
g.P("if !first { out.RawByte(','); } else { first = false; }")
|
||||||
g.P("const prefix string = ", `"\"`, fieldJSONName(f), `\":"`)
|
g.P("const prefix string = ", `"\"`, fieldJSONName(f), `\":"`)
|
||||||
|
@ -247,7 +250,10 @@ func emitJSONFieldWrite(g *protogen.GeneratedFile, f *protogen.Field, name strin
|
||||||
case protoreflect.StringKind:
|
case protoreflect.StringKind:
|
||||||
template = "out.String(%s)"
|
template = "out.String(%s)"
|
||||||
case protoreflect.BytesKind:
|
case protoreflect.BytesKind:
|
||||||
template = "out.Base64Bytes(%s)"
|
g.P("if ", selector, "!= nil {")
|
||||||
|
g.P("out.Base64Bytes(", selector, ")")
|
||||||
|
g.P("} else { out.String(\"\") }")
|
||||||
|
return
|
||||||
case protoreflect.MessageKind:
|
case protoreflect.MessageKind:
|
||||||
template = "%s.MarshalEasyJSON(out)"
|
template = "%s.MarshalEasyJSON(out)"
|
||||||
case protoreflect.GroupKind:
|
case protoreflect.GroupKind:
|
||||||
|
|
Loading…
Reference in a new issue