mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-04-17 11:42:38 +00:00
encoding/textpb: unmarshal Any
Also fix marshaling Any in expanded form to contain the correct type_url value. Change-Id: I4b467e74bb1d73255effd9cc4cfff9cf4558940f Reviewed-on: https://go-review.googlesource.com/c/156342 Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
0dcfb9aa6a
commit
66c365cf72
@ -8,10 +8,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
protoV1 "github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/v2/internal/encoding/text"
|
||||
"github.com/golang/protobuf/v2/internal/errors"
|
||||
"github.com/golang/protobuf/v2/internal/pragma"
|
||||
"github.com/golang/protobuf/v2/internal/set"
|
||||
pvalue "github.com/golang/protobuf/v2/internal/value"
|
||||
"github.com/golang/protobuf/v2/proto"
|
||||
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
|
||||
"github.com/golang/protobuf/v2/reflect/protoregistry"
|
||||
@ -87,9 +89,15 @@ func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message)
|
||||
var nerr errors.NonFatal
|
||||
|
||||
msgType := m.Type()
|
||||
knownFields := m.KnownFields()
|
||||
|
||||
// Handle expanded Any message.
|
||||
if msgType.FullName() == "google.protobuf.Any" && isExpandedAny(tmsg) {
|
||||
return o.unmarshalAny(tmsg[0], knownFields)
|
||||
}
|
||||
|
||||
fieldDescs := msgType.Fields()
|
||||
reservedNames := msgType.ReservedNames()
|
||||
knownFields := m.KnownFields()
|
||||
xtTypes := knownFields.ExtensionTypes()
|
||||
var reqNums set.Ints
|
||||
var seenNums set.Ints
|
||||
@ -109,18 +117,21 @@ func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message)
|
||||
fd = fieldDescs.ByName(pref.Name(strings.ToLower(string(name))))
|
||||
}
|
||||
case text.String:
|
||||
// TODO: Handle Any expansions here as well.
|
||||
|
||||
// Handle extensions. Extensions have to be registered first in the message's
|
||||
// Handle extensions only. This code path is not for Any.
|
||||
if msgType.FullName() == "google.protobuf.Any" {
|
||||
break
|
||||
}
|
||||
// Extensions have to be registered first in the message's
|
||||
// ExtensionTypes before setting a value to it.
|
||||
xtName := pref.FullName(tkey.String())
|
||||
// Check first if it is already registered. This is the case for repeated fields.
|
||||
// Check first if it is already registered. This is the case for
|
||||
// repeated fields.
|
||||
xt := xtTypes.ByName(xtName)
|
||||
if xt == nil {
|
||||
var err error
|
||||
xt, err = o.Resolver.FindExtensionByName(xtName)
|
||||
if err != nil && err != protoregistry.NotFound {
|
||||
return err
|
||||
return errors.New("unable to resolve [%v]: %v", xtName, err)
|
||||
}
|
||||
if xt != nil {
|
||||
xtTypes.Register(xt)
|
||||
@ -274,12 +285,11 @@ func unmarshalScalar(input text.Value, fd pref.FieldDescriptor) (pref.Value, err
|
||||
// If input is int32, use directly.
|
||||
if n, ok := input.Int(b32); ok {
|
||||
return pref.ValueOf(pref.EnumNumber(n)), nil
|
||||
} else {
|
||||
if name, ok := input.Name(); ok {
|
||||
// Lookup EnumNumber based on name.
|
||||
if enumVal := fd.EnumType().Values().ByName(name); enumVal != nil {
|
||||
return pref.ValueOf(enumVal.Number()), nil
|
||||
}
|
||||
}
|
||||
if name, ok := input.Name(); ok {
|
||||
// Lookup EnumNumber based on name.
|
||||
if enumVal := fd.EnumType().Values().ByName(name); enumVal != nil {
|
||||
return pref.ValueOf(enumVal.Number()), nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
@ -327,7 +337,7 @@ func (o UnmarshalOptions) unmarshalMap(input []text.Value, fd pref.FieldDescript
|
||||
|
||||
// Determine ahead whether map entry is a scalar type or a message type in order to call the
|
||||
// appropriate unmarshalMapValue func inside the for loop below.
|
||||
unmarshalMapValue := o.unmarshalMapScalarValue
|
||||
unmarshalMapValue := unmarshalMapScalarValue
|
||||
switch valDesc.Kind() {
|
||||
case pref.MessageKind, pref.GroupKind:
|
||||
unmarshalMapValue = o.unmarshalMapMessageValue
|
||||
@ -418,7 +428,7 @@ func (o UnmarshalOptions) unmarshalMapMessageValue(input text.Value, pkey pref.M
|
||||
|
||||
// unmarshalMapScalarValue unmarshals given scalar-type text.Value into a protoreflect.Map
|
||||
// for the given MapKey.
|
||||
func (o UnmarshalOptions) unmarshalMapScalarValue(input text.Value, pkey pref.MapKey, fd pref.FieldDescriptor, mmap pref.Map) error {
|
||||
func unmarshalMapScalarValue(input text.Value, pkey pref.MapKey, fd pref.FieldDescriptor, mmap pref.Map) error {
|
||||
var val pref.Value
|
||||
if input.Type() == 0 {
|
||||
val = fd.Default()
|
||||
@ -432,3 +442,51 @@ func (o UnmarshalOptions) unmarshalMapScalarValue(input text.Value, pkey pref.Ma
|
||||
mmap.Set(pkey, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
// isExpandedAny returns true if given [][2]text.Value may be an expanded Any that contains only one
|
||||
// field with key type of text.String type and value type of text.Message.
|
||||
func isExpandedAny(tmsg [][2]text.Value) bool {
|
||||
if len(tmsg) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
field := tmsg[0]
|
||||
return field[0].Type() == text.String && field[1].Type() == text.Message
|
||||
}
|
||||
|
||||
// unmarshalAny unmarshals an expanded Any textproto. This method assumes that the given
|
||||
// tfield has key type of text.String and value type of text.Message.
|
||||
func (o UnmarshalOptions) unmarshalAny(tfield [2]text.Value, knownFields pref.KnownFields) error {
|
||||
var nerr errors.NonFatal
|
||||
|
||||
typeURL := tfield[0].String()
|
||||
value := tfield[1].Message()
|
||||
|
||||
mt, err := o.Resolver.FindMessageByURL(typeURL)
|
||||
if !nerr.Merge(err) {
|
||||
return errors.New("unable to resolve message [%v]: %v", typeURL, err)
|
||||
}
|
||||
// Create new message for the embedded message type and unmarshal the
|
||||
// value into it.
|
||||
m := mt.New()
|
||||
if err := o.unmarshalMessage(value, m); !nerr.Merge(err) {
|
||||
return err
|
||||
}
|
||||
// Serialize the embedded message and assign the resulting bytes to the value field.
|
||||
// TODO: Switch to V2 marshal and enable deterministic option when ready.
|
||||
var mv1 protoV1.Message
|
||||
if mtmp, ok := m.(pvalue.Unwrapper); ok {
|
||||
mv1 = mtmp.ProtoUnwrap().(protoV1.Message)
|
||||
} else {
|
||||
mv1 = m.Interface().(protoV1.Message)
|
||||
}
|
||||
b, err := protoV1.Marshal(mv1)
|
||||
if !nerr.Merge(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
knownFields.Set(pref.FieldNumber(1), pref.ValueOf(typeURL))
|
||||
knownFields.Set(pref.FieldNumber(2), pref.ValueOf(b))
|
||||
|
||||
return nerr.E
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
|
||||
protoV1 "github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/protoapi"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
"github.com/golang/protobuf/v2/encoding/textpb"
|
||||
"github.com/golang/protobuf/v2/internal/legacy"
|
||||
"github.com/golang/protobuf/v2/internal/scalar"
|
||||
@ -49,6 +50,7 @@ func registerExtension(xd *protoapi.ExtensionDesc) {
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
umo textpb.UnmarshalOptions
|
||||
inputMessage proto.Message
|
||||
inputText string
|
||||
wantMessage proto.Message
|
||||
@ -1122,13 +1124,186 @@ opt_int32: 42
|
||||
inputMessage: &pb2.Extensions{},
|
||||
inputText: "[pb2.invalid_message_field]: true",
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "Any not expanded",
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
type_url: "pb2.Nested"
|
||||
value: "some bytes"
|
||||
}
|
||||
`,
|
||||
wantMessage: &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "pb2.Nested",
|
||||
Value: []byte("some bytes"),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "Any not expanded missing value",
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
type_url: "pb2.Nested"
|
||||
}
|
||||
`,
|
||||
wantMessage: &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "pb2.Nested",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "Any not expanded missing type_url",
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
value: "some bytes"
|
||||
}
|
||||
`,
|
||||
wantMessage: &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
Value: []byte("some bytes"),
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "Any expanded",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[foobar/pb2.Nested]: {
|
||||
opt_string: "embedded inside Any"
|
||||
opt_nested: {
|
||||
opt_string: "inception"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
wantMessage: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
OptNested: &pb2.Nested{
|
||||
OptString: scalar.String("inception"),
|
||||
},
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "foobar/pb2.Nested",
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
}, {
|
||||
desc: "Any expanded with empty value",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[foo.com/pb2.Nested]: {}
|
||||
}
|
||||
`,
|
||||
wantMessage: &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "foo.com/pb2.Nested",
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "Any expanded with missing required error",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.PartialRequired{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[pb2.PartialRequired]: {
|
||||
opt_string: "embedded inside Any"
|
||||
}
|
||||
}
|
||||
`,
|
||||
wantMessage: func() proto.Message {
|
||||
m := &pb2.PartialRequired{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
// Ignore required not set error.
|
||||
if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "pb2.PartialRequired",
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "Any expanded with unregistered type",
|
||||
umo: textpb.UnmarshalOptions{Resolver: preg.NewTypes()},
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[SomeMessage]: {}
|
||||
}
|
||||
`,
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "Any expanded with invalid value",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[pb2.Nested]: 123
|
||||
}
|
||||
`,
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "Any expanded with unknown fields",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[pb2.Nested]: {}
|
||||
unknown: ""
|
||||
}
|
||||
`,
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "Any contains expanded and unexpanded fields",
|
||||
umo: func() textpb.UnmarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.UnmarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
inputMessage: &pb2.KnownTypes{},
|
||||
inputText: `opt_any: {
|
||||
[pb2.Nested]: {}
|
||||
type_url: "pb2.Nested"
|
||||
}
|
||||
`,
|
||||
wantErr: true,
|
||||
}}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := textpb.Unmarshal(tt.inputMessage, []byte(tt.inputText))
|
||||
err := tt.umo.Unmarshal(tt.inputMessage, []byte(tt.inputText))
|
||||
if err != nil && !tt.wantErr {
|
||||
t.Errorf("Unmarshal() returned error: %v\n\n", err)
|
||||
}
|
||||
|
@ -375,10 +375,10 @@ func (o MarshalOptions) marshalAny(m pref.Message) (text.Value, error) {
|
||||
}
|
||||
|
||||
knownFields := m.KnownFields()
|
||||
typeURL := knownFields.Get(tfd.Number())
|
||||
typeURL := knownFields.Get(tfd.Number()).String()
|
||||
value := knownFields.Get(vfd.Number())
|
||||
|
||||
emt, err := o.Resolver.FindMessageByURL(typeURL.String())
|
||||
emt, err := o.Resolver.FindMessageByURL(typeURL)
|
||||
if !nerr.Merge(err) {
|
||||
return text.Value{}, err
|
||||
}
|
||||
@ -393,12 +393,11 @@ func (o MarshalOptions) marshalAny(m pref.Message) (text.Value, error) {
|
||||
if !nerr.Merge(err) {
|
||||
return text.Value{}, err
|
||||
}
|
||||
// Expanded Any field value contains only a single field with the embedded
|
||||
// message type as the field name in [] and a text marshaled field value of
|
||||
// the embedded message.
|
||||
// Expanded Any field value contains only a single field with the type_url field value as the
|
||||
// field name in [] and a text marshaled field value of the embedded message.
|
||||
msgFields := [][2]text.Value{
|
||||
{
|
||||
text.ValueOf(string(emt.FullName())),
|
||||
text.ValueOf(typeURL),
|
||||
msg,
|
||||
},
|
||||
}
|
||||
|
@ -67,6 +67,10 @@ func setExtension(m proto.Message, xd *protoapi.ExtensionDesc, val interface{})
|
||||
knownFields.Set(wire.Number(xd.Field), pval)
|
||||
}
|
||||
|
||||
func wrapAnyPB(any *anypb.Any) proto.Message {
|
||||
return impl.Export{}.MessageOf(any).Interface()
|
||||
}
|
||||
|
||||
// dhex decodes a hex-string and returns the bytes and panics if s is invalid.
|
||||
func dhex(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
@ -980,8 +984,10 @@ opt_int32: 42
|
||||
`,
|
||||
*/
|
||||
}, {
|
||||
desc: "google.protobuf.Any message not expanded",
|
||||
mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
|
||||
desc: "Any message not expanded",
|
||||
mo: textpb.MarshalOptions{
|
||||
Resolver: preg.NewTypes(),
|
||||
},
|
||||
input: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
@ -994,21 +1000,19 @@ opt_int32: 42
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return impl.Export{}.MessageOf(&anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
return wrapAnyPB(&anypb.Any{
|
||||
TypeUrl: "pb2.Nested",
|
||||
Value: b,
|
||||
}).Interface()
|
||||
})
|
||||
}(),
|
||||
want: `type_url: "pb2.Nested"
|
||||
value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
`,
|
||||
}, {
|
||||
desc: "google.protobuf.Any message expanded",
|
||||
mo: func() textpb.MarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.MarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
desc: "Any message expanded",
|
||||
mo: textpb.MarshalOptions{
|
||||
Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
|
||||
},
|
||||
input: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
@ -1021,12 +1025,12 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return impl.Export{}.MessageOf(&anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
return wrapAnyPB(&anypb.Any{
|
||||
TypeUrl: "foo/pb2.Nested",
|
||||
Value: b,
|
||||
}).Interface()
|
||||
})
|
||||
}(),
|
||||
want: `[pb2.Nested]: {
|
||||
want: `[foo/pb2.Nested]: {
|
||||
opt_string: "embedded inside Any"
|
||||
opt_nested: {
|
||||
opt_string: "inception"
|
||||
@ -1034,12 +1038,10 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
}
|
||||
`,
|
||||
}, {
|
||||
desc: "google.protobuf.Any message expanded with missing required error",
|
||||
mo: func() textpb.MarshalOptions {
|
||||
m := &pb2.PartialRequired{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.MarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
desc: "Any message expanded with missing required error",
|
||||
mo: textpb.MarshalOptions{
|
||||
Resolver: preg.NewTypes((&pb2.PartialRequired{}).ProtoReflect().Type()),
|
||||
},
|
||||
input: func() proto.Message {
|
||||
m := &pb2.PartialRequired{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
@ -1050,10 +1052,10 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
if _, ok := err.(*protoV1.RequiredNotSetError); !ok {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return impl.Export{}.MessageOf(&anypb.Any{
|
||||
return wrapAnyPB(&anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: b,
|
||||
}).Interface()
|
||||
})
|
||||
}(),
|
||||
want: `[pb2.PartialRequired]: {
|
||||
opt_string: "embedded inside Any"
|
||||
@ -1061,83 +1063,16 @@ value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
`,
|
||||
wantErr: true,
|
||||
}, {
|
||||
desc: "google.protobuf.Any message with invalid value",
|
||||
mo: func() textpb.MarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.MarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
input: func() proto.Message {
|
||||
m := &pb2.Nested{}
|
||||
return impl.Export{}.MessageOf(&anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: dhex("80"),
|
||||
}).Interface()
|
||||
}(),
|
||||
want: `type_url: "pb2.Nested"
|
||||
desc: "Any message with invalid value",
|
||||
mo: textpb.MarshalOptions{
|
||||
Resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
|
||||
},
|
||||
input: wrapAnyPB(&anypb.Any{
|
||||
TypeUrl: "foo/pb2.Nested",
|
||||
Value: dhex("80"),
|
||||
}),
|
||||
want: `type_url: "foo/pb2.Nested"
|
||||
value: "\x80"
|
||||
`,
|
||||
}, {
|
||||
desc: "google.protobuf.Any field",
|
||||
mo: textpb.MarshalOptions{Resolver: preg.NewTypes()},
|
||||
input: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
OptNested: &pb2.Nested{
|
||||
OptString: scalar.String("inception"),
|
||||
},
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
want: `opt_any: {
|
||||
type_url: "pb2.Nested"
|
||||
value: "\n\x13embedded inside Any\x12\x0b\n\tinception"
|
||||
}
|
||||
`,
|
||||
}, {
|
||||
desc: "google.protobuf.Any field expanded using given types registry",
|
||||
mo: func() textpb.MarshalOptions {
|
||||
m := &pb2.Nested{}
|
||||
resolver := preg.NewTypes(m.ProtoReflect().Type())
|
||||
return textpb.MarshalOptions{Resolver: resolver}
|
||||
}(),
|
||||
input: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
OptNested: &pb2.Nested{
|
||||
OptString: scalar.String("inception"),
|
||||
},
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
want: `opt_any: {
|
||||
[pb2.Nested]: {
|
||||
opt_string: "embedded inside Any"
|
||||
opt_nested: {
|
||||
opt_string: "inception"
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
|
||||
|
@ -7,10 +7,13 @@ import (
|
||||
"github.com/golang/protobuf/v2/encoding/textpb"
|
||||
"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
|
||||
"github.com/golang/protobuf/v2/proto"
|
||||
preg "github.com/golang/protobuf/v2/reflect/protoregistry"
|
||||
|
||||
// The legacy package must be imported prior to use of any legacy messages.
|
||||
// TODO: Remove this when protoV1 registers these hooks for you.
|
||||
"github.com/golang/protobuf/v2/internal/impl"
|
||||
_ "github.com/golang/protobuf/v2/internal/legacy"
|
||||
"github.com/golang/protobuf/v2/internal/scalar"
|
||||
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
durpb "github.com/golang/protobuf/ptypes/duration"
|
||||
@ -22,8 +25,9 @@ import (
|
||||
|
||||
func TestRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
message proto.Message
|
||||
desc string
|
||||
resolver *preg.Types
|
||||
message proto.Message
|
||||
}{{
|
||||
desc: "well-known type fields set to empty messages",
|
||||
message: &pb2.KnownTypes{
|
||||
@ -88,7 +92,7 @@ func TestRoundTrip(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "well-known type struct field and different Value types",
|
||||
desc: "Struct field and different Value types",
|
||||
message: &pb2.KnownTypes{
|
||||
OptStruct: &stpb.Struct{
|
||||
Fields: map[string]*stpb.Value{
|
||||
@ -146,21 +150,101 @@ func TestRoundTrip(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
desc: "Any field without registered type",
|
||||
resolver: preg.NewTypes(),
|
||||
message: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
OptNested: &pb2.Nested{
|
||||
OptString: scalar.String("inception"),
|
||||
},
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
}, {
|
||||
desc: "Any field with registered type",
|
||||
resolver: preg.NewTypes((&pb2.Nested{}).ProtoReflect().Type()),
|
||||
message: func() proto.Message {
|
||||
m := &pb2.Nested{
|
||||
OptString: scalar.String("embedded inside Any"),
|
||||
OptNested: &pb2.Nested{
|
||||
OptString: scalar.String("inception"),
|
||||
},
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b, err := protoV1.Marshal(m)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: string(m.ProtoReflect().Type().FullName()),
|
||||
Value: b,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
}, {
|
||||
desc: "Any field containing Any message",
|
||||
resolver: func() *preg.Types {
|
||||
mt1 := (&pb2.Nested{}).ProtoReflect().Type()
|
||||
mt2 := impl.Export{}.MessageTypeOf(&anypb.Any{})
|
||||
return preg.NewTypes(mt1, mt2)
|
||||
}(),
|
||||
message: func() proto.Message {
|
||||
m1 := &pb2.Nested{
|
||||
OptString: scalar.String("message inside Any of another Any field"),
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b1, err := protoV1.Marshal(m1)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
m2 := &anypb.Any{
|
||||
TypeUrl: "pb2.Nested",
|
||||
Value: b1,
|
||||
}
|
||||
// TODO: Switch to V2 marshal when ready.
|
||||
b2, err := protoV1.Marshal(m2)
|
||||
if err != nil {
|
||||
t.Fatalf("error in binary marshaling message for Any.value: %v", err)
|
||||
}
|
||||
return &pb2.KnownTypes{
|
||||
OptAny: &anypb.Any{
|
||||
TypeUrl: "google.protobuf.Any",
|
||||
Value: b2,
|
||||
},
|
||||
}
|
||||
}(),
|
||||
}}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
b, err := textpb.Marshal(tt.message)
|
||||
mo := textpb.MarshalOptions{Resolver: tt.resolver}
|
||||
umo := textpb.UnmarshalOptions{Resolver: tt.resolver}
|
||||
|
||||
b, err := mo.Marshal(tt.message)
|
||||
if err != nil {
|
||||
t.Errorf("Marshal() returned error: %v\n\n", err)
|
||||
}
|
||||
gotMessage := tt.message.ProtoReflect().Type().New().Interface()
|
||||
err = textpb.Unmarshal(gotMessage, b)
|
||||
err = umo.Unmarshal(gotMessage, b)
|
||||
if err != nil {
|
||||
t.Errorf("Unmarshal() returned error: %v\n\n", err)
|
||||
}
|
||||
|
||||
if !protoV1.Equal(gotMessage.(protoV1.Message), tt.message.(protoV1.Message)) {
|
||||
t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", gotMessage, tt.message)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user