Skip to content

Commit efe2657

Browse files
authored
fix(dart): Deserialize maps with integral keys (#3285)
1 parent 07dd3ac commit efe2657

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

internal/sidekick/dart/annotate.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,29 @@ func (annotate *annotateModel) decoder(typez api.Typez, typeid string, state *ap
833833
typeName := annotate.resolveMessageName(state.MessageByID[typeid], true)
834834
return fmt.Sprintf("%s.fromJson", typeName)
835835
default:
836-
panic("unknown type")
836+
panic(fmt.Sprintf("unsupported type: %d", typez))
837+
}
838+
}
839+
840+
func (annotate *annotateModel) keyDecoder(typez api.Typez) string {
841+
// JSON objects can only contain string keys so non-String types need to use specialized decoders.
842+
// Supported key types are defined here:
843+
// https://protobuf.dev/programming-guides/proto3/#maps
844+
switch typez {
845+
case api.STRING_TYPE:
846+
return "decodeString"
847+
case api.INT32_TYPE, // Integer types that can be decoded as Dart `int`.
848+
api.FIXED32_TYPE,
849+
api.SFIXED32_TYPE,
850+
api.SINT32_TYPE,
851+
api.UINT32_TYPE,
852+
api.INT64_TYPE,
853+
api.SINT64_TYPE,
854+
api.SFIXED64_TYPE:
855+
return "decodeIntKey"
856+
default:
857+
// TODO(https://github.com/googleapis/google-cloud-dart/issues/95): Support all key types.
858+
panic(fmt.Sprintf("unsupported key type: %d", typez))
837859
}
838860
}
839861

@@ -867,8 +889,7 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
867889
case field.Map:
868890
message := state.MessageByID[field.TypezID]
869891
keyType := message.Fields[0].Typez
870-
keyTypeID := message.Fields[0].TypezID
871-
keyDecoder := annotate.decoder(keyType, keyTypeID, state)
892+
keyDecoder := annotate.keyDecoder(keyType)
872893
valueType := message.Fields[1].Typez
873894
valueTypeID := message.Fields[1].TypezID
874895
valueDecoder := annotate.decoder(valueType, valueTypeID, state)
@@ -880,7 +901,6 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
880901
}
881902

882903
decoder := annotate.decoder(field.Typez, field.TypezID, state)
883-
// No decoding necessary.
884904
return fmt.Sprintf("switch (%s) { null => %s, Object $1 => %s($1)}", data, defaultValue, decoder)
885905
}
886906

internal/sidekick/dart/annotate_test.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,21 @@ func TestCreateFromJsonLine(t *testing.T) {
844844
},
845845
},
846846
}
847+
mapInt32ToBytes := &api.Message{
848+
Name: "$Int32ToBytes",
849+
ID: "..$Int32ToBytes",
850+
IsMap: true,
851+
Fields: []*api.Field{
852+
{
853+
Name: "key",
854+
Typez: api.INT32_TYPE,
855+
},
856+
{
857+
Name: "value",
858+
Typez: api.BYTES_TYPE,
859+
},
860+
},
861+
}
847862

848863
for _, test := range []struct {
849864
field *api.Field
@@ -993,11 +1008,18 @@ func TestCreateFromJsonLine(t *testing.T) {
9931008
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: ".google.protobuf.Duration"},
9941009
"switch (json['message']) { null => null, Object $1 => Duration.fromJson($1)}",
9951010
},
1011+
1012+
// maps
9961013
{
997-
// Map of bytes.
1014+
// string -> bytes
9981015
&api.Field{Name: "message", JSONName: "message", Map: true, Typez: api.MESSAGE_TYPE, TypezID: mapStringToBytes.ID},
9991016
"switch (json['message']) { null => {}, Map<String, Object?> $1 => {for (final e in $1.entries) decodeString(e.key): decodeBytes(e.value)}, _ => throw const FormatException('\"message\" is not an object') }",
10001017
},
1018+
{
1019+
// int32 -> bytes
1020+
&api.Field{Name: "message", JSONName: "message", Map: true, Typez: api.MESSAGE_TYPE, TypezID: mapInt32ToBytes.ID},
1021+
"switch (json['message']) { null => {}, Map<String, Object?> $1 => {for (final e in $1.entries) decodeIntKey(e.key): decodeBytes(e.value)}, _ => throw const FormatException('\"message\" is not an object') }",
1022+
},
10011023
} {
10021024
t.Run(test.field.Name, func(t *testing.T) {
10031025
message := &api.Message{
@@ -1006,7 +1028,10 @@ func TestCreateFromJsonLine(t *testing.T) {
10061028
Package: sample.Package,
10071029
Fields: []*api.Field{test.field},
10081030
}
1009-
model := api.NewTestAPI([]*api.Message{message, secret, foreignMessage, mapStringToBytes}, []*api.Enum{enumState, foreignEnumState}, []*api.Service{})
1031+
model := api.NewTestAPI([]*api.Message{message,
1032+
secret, foreignMessage, mapStringToBytes, mapInt32ToBytes},
1033+
[]*api.Enum{enumState, foreignEnumState},
1034+
[]*api.Service{})
10101035
annotate := newAnnotateModel(model)
10111036
annotate.annotateModel(map[string]string{
10121037
"prefix:google.cloud.foo": "foo",

0 commit comments

Comments
 (0)