Skip to content

Commit 7331816

Browse files
fix(dart): enums referenced outside the package (#2952)
Fixes a bug where enums referenced from a package other than the current one did not have a package prefix. --------- Signed-off-by: Brian Quinlan <[email protected]> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 2c8803e commit 7331816

File tree

3 files changed

+158
-34
lines changed

3 files changed

+158
-34
lines changed

internal/sidekick/dart/annotate.go

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,8 @@ func (annotate *annotateModel) annotateMethod(method *api.Method) {
630630
Parent: method,
631631
Name: strcase.ToLowerCamel(method.Name),
632632
RequestMethod: strings.ToLower(method.PathInfo.Bindings[0].Verb),
633-
RequestType: annotate.resolveTypeName(state.MessageByID[method.InputTypeID], true),
634-
ResponseType: annotate.resolveTypeName(state.MessageByID[method.OutputTypeID], true),
633+
RequestType: annotate.resolveMessageName(state.MessageByID[method.InputTypeID], true),
634+
ResponseType: annotate.resolveMessageName(state.MessageByID[method.OutputTypeID], true),
635635
DocLines: formatDocComments(method.Documentation, state),
636636
ReturnsValue: !method.ReturnsEmpty,
637637
BodyMessageName: bodyMessageName,
@@ -647,8 +647,8 @@ func (annotate *annotateModel) annotateOperationInfo(operationInfo *api.Operatio
647647
metadata := annotate.state.MessageByID[operationInfo.MetadataTypeID]
648648

649649
operationInfo.Codec = &operationInfoAnnotation{
650-
ResponseType: annotate.resolveTypeName(response, false),
651-
MetadataType: annotate.resolveTypeName(metadata, false),
650+
ResponseType: annotate.resolveMessageName(response, false),
651+
MetadataType: annotate.resolveMessageName(metadata, false),
652652
}
653653
}
654654

@@ -741,7 +741,7 @@ func (annotate *annotateModel) annotateField(field *api.Field) {
741741
case field.Typez == api.ENUM_TYPE:
742742
// The default value for enums are the generated MyEnum.$default field,
743743
// always set to the first value of that enum.
744-
typeName := enumName(annotate.state.EnumByID[field.TypezID])
744+
typeName := annotate.resolveEnumName(annotate.state.EnumByID[field.TypezID])
745745
defaultValue = fmt.Sprintf("%s.$default", typeName)
746746
default:
747747
defaultValue = defaultValues[field.Typez]
@@ -777,7 +777,7 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
777777
bang = " ?? {}"
778778
case field.Typez == api.ENUM_TYPE:
779779
// 'ExecutableCode_Language.$default'
780-
typeName := enumName(annotate.state.EnumByID[field.TypezID])
780+
typeName := annotate.resolveEnumName(annotate.state.EnumByID[field.TypezID])
781781
bang = fmt.Sprintf(" ?? %s.$default", typeName)
782782
default:
783783
defaultValues := map[api.Typez]string{
@@ -807,11 +807,11 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
807807
case api.BYTES_TYPE:
808808
return fmt.Sprintf("decodeListBytes(%s)%s", data, bang)
809809
case api.ENUM_TYPE:
810-
typeName := enumName(state.EnumByID[field.TypezID])
810+
typeName := annotate.resolveEnumName(state.EnumByID[field.TypezID])
811811
return fmt.Sprintf("decodeListEnum(%s, %s.fromJson)%s", data, typeName, bang)
812812
case api.MESSAGE_TYPE:
813813
_, hasCustomEncoding := usesCustomEncoding[field.TypezID]
814-
typeName := annotate.resolveTypeName(state.MessageByID[field.TypezID], true)
814+
typeName := annotate.resolveMessageName(state.MessageByID[field.TypezID], true)
815815
if hasCustomEncoding {
816816
return fmt.Sprintf("decodeListMessageCustom(%s, %s.fromJson)%s", data, typeName, bang)
817817
} else {
@@ -827,11 +827,11 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
827827
case api.BYTES_TYPE:
828828
return fmt.Sprintf("decodeMapBytes(%s)%s", data, bang)
829829
case api.ENUM_TYPE:
830-
typeName := enumName(state.EnumByID[valueField.TypezID])
830+
typeName := annotate.resolveEnumName(state.EnumByID[valueField.TypezID])
831831
return fmt.Sprintf("decodeMapEnum(%s, %s.fromJson)%s", data, typeName, bang)
832832
case api.MESSAGE_TYPE:
833833
_, hasCustomEncoding := usesCustomEncoding[valueField.TypezID]
834-
typeName := annotate.resolveTypeName(state.MessageByID[valueField.TypezID], true)
834+
typeName := annotate.resolveMessageName(state.MessageByID[valueField.TypezID], true)
835835
if hasCustomEncoding {
836836
return fmt.Sprintf("decodeMapMessageCustom(%s, %s.fromJson)%s", data, typeName, bang)
837837
} else {
@@ -855,11 +855,11 @@ func (annotate *annotateModel) createFromJsonLine(field *api.Field, state *api.A
855855
case field.Typez == api.BYTES_TYPE:
856856
return fmt.Sprintf("decodeBytes(%s)%s", data, bang)
857857
case field.Typez == api.ENUM_TYPE:
858-
typeName := enumName(state.EnumByID[field.TypezID])
858+
typeName := annotate.resolveEnumName(state.EnumByID[field.TypezID])
859859
return fmt.Sprintf("decodeEnum(%s, %s.fromJson)%s", data, typeName, bang)
860860
case field.Typez == api.MESSAGE_TYPE:
861861
_, hasCustomEncoding := usesCustomEncoding[field.TypezID]
862-
typeName := annotate.resolveTypeName(state.MessageByID[field.TypezID], true)
862+
typeName := annotate.resolveMessageName(state.MessageByID[field.TypezID], true)
863863
if hasCustomEncoding {
864864
return fmt.Sprintf("decodeCustom(%s, %s.fromJson)", data, typeName)
865865
} else {
@@ -1080,20 +1080,15 @@ func (annotate *annotateModel) fieldType(f *api.Field) string {
10801080
val := annotate.fieldType(message.Fields[1])
10811081
out = "Map<" + key + ", " + val + ">"
10821082
} else {
1083-
out = annotate.resolveTypeName(message, true)
1083+
out = annotate.resolveMessageName(message, true)
10841084
}
10851085
case api.ENUM_TYPE:
10861086
e, ok := annotate.state.EnumByID[f.TypezID]
10871087
if !ok {
10881088
slog.Error("unable to lookup type", "id", f.TypezID)
10891089
return ""
10901090
}
1091-
annotate.updateUsedPackages(e.Package)
1092-
out = enumName(e)
1093-
importPrefix, needsImportPrefix := annotate.packagePrefixes[e.Package]
1094-
if needsImportPrefix {
1095-
out = importPrefix + "." + out
1096-
}
1091+
out = annotate.resolveEnumName(e)
10971092
default:
10981093
slog.Error("unhandled fieldType", "type", f.Typez, "id", f.TypezID)
10991094
}
@@ -1105,7 +1100,18 @@ func (annotate *annotateModel) fieldType(f *api.Field) string {
11051100
return out
11061101
}
11071102

1108-
func (annotate *annotateModel) resolveTypeName(message *api.Message, returnVoidForEmpty bool) string {
1103+
func (annotate *annotateModel) resolveEnumName(enum *api.Enum) string {
1104+
annotate.updateUsedPackages(enum.Package)
1105+
1106+
ref := enumName(enum)
1107+
importPrefix, needsImportPrefix := annotate.packagePrefixes[enum.Package]
1108+
if needsImportPrefix {
1109+
ref = importPrefix + "." + ref
1110+
}
1111+
return ref
1112+
}
1113+
1114+
func (annotate *annotateModel) resolveMessageName(message *api.Message, returnVoidForEmpty bool) string {
11091115
if message == nil {
11101116
slog.Error("unable to lookup type")
11111117
return ""

internal/sidekick/dart/annotate_test.go

Lines changed: 125 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,14 +565,27 @@ func TestBuildQueryLinesEnums(t *testing.T) {
565565
r := sample.Replication()
566566
a := sample.Automatic()
567567
enum := sample.EnumState()
568+
foreignEnumState := &api.Enum{
569+
Name: "ForeignEnum",
570+
Package: "google.cloud.foo",
571+
ID: "google.cloud.foo.ForeignEnum",
572+
Values: []*api.EnumValue{
573+
{
574+
Name: "Enabled",
575+
Number: 1,
576+
},
577+
},
578+
}
579+
568580
model := api.NewTestAPI(
569581
[]*api.Message{r, a, sample.CustomerManagedEncryption()},
570-
[]*api.Enum{enum},
582+
[]*api.Enum{enum, foreignEnumState},
571583
[]*api.Service{})
572584
model.PackageName = "test"
573585
annotate := newAnnotateModel(model)
574-
annotate.annotateModel(map[string]string{})
575-
586+
annotate.annotateModel(map[string]string{
587+
"prefix:google.cloud.foo": "foo",
588+
})
576589
for _, test := range []struct {
577590
enumField *api.Field
578591
want []string
@@ -594,6 +607,15 @@ func TestBuildQueryLinesEnums(t *testing.T) {
594607
Optional: true},
595608
[]string{"if (result.optionalEnum != null) 'optionalJsonEnum': result.optionalEnum!.value"},
596609
},
610+
{
611+
&api.Field{
612+
Name: "enumName",
613+
JSONName: "jsonEnumName",
614+
Typez: api.ENUM_TYPE,
615+
TypezID: foreignEnumState.ID,
616+
Optional: false},
617+
[]string{"if (result.enumName.isNotDefault) 'jsonEnumName': result.enumName.value"},
618+
},
597619
} {
598620
t.Run(test.enumField.Name, func(t *testing.T) {
599621
got := annotate.buildQueryLines([]string{}, "result.", "", test.enumField, model.State)
@@ -715,6 +737,41 @@ func TestBuildQueryLinesMessages(t *testing.T) {
715737

716738
func TestCreateFromJsonLine(t *testing.T) {
717739
secret := sample.Secret()
740+
enumState := sample.EnumState()
741+
742+
foreignMessage := &api.Message{
743+
Name: "Foo",
744+
Package: "google.cloud.foo",
745+
ID: "google.cloud.foo.Foo",
746+
Enums: []*api.Enum{},
747+
Fields: []*api.Field{},
748+
}
749+
foreignEnumState := &api.Enum{
750+
Name: "ForeignEnum",
751+
Package: "google.cloud.foo",
752+
ID: "google.cloud.foo.ForeignEnum",
753+
Values: []*api.EnumValue{
754+
{
755+
Name: "Enabled",
756+
Number: 1,
757+
},
758+
},
759+
}
760+
mapStringToBytes := &api.Message{
761+
Name: "$StringToBytes",
762+
ID: "..$StringToBytes",
763+
IsMap: true,
764+
Fields: []*api.Field{
765+
{
766+
Name: "key",
767+
Typez: api.STRING_TYPE,
768+
},
769+
{
770+
Name: "value",
771+
Typez: api.BYTES_TYPE,
772+
},
773+
},
774+
}
718775

719776
for _, test := range []struct {
720777
field *api.Field
@@ -780,11 +837,35 @@ func TestCreateFromJsonLine(t *testing.T) {
780837
"decodeListBytes(json['bytesList'])",
781838
},
782839

840+
// enums
841+
{
842+
&api.Field{Name: "message", JSONName: "message", Typez: api.ENUM_TYPE, TypezID: enumState.ID},
843+
"decodeEnum(json['message'], State.fromJson) ?? State.$default",
844+
},
845+
{
846+
&api.Field{Name: "message", JSONName: "message", Typez: api.ENUM_TYPE, TypezID: foreignEnumState.ID},
847+
"decodeEnum(json['message'], foo.ForeignEnum.fromJson) ?? foo.ForeignEnum.$default",
848+
},
849+
783850
// messages
784851
{
785852
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: secret.ID},
786853
"decode(json['message'], Secret.fromJson)",
787854
},
855+
{
856+
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: foreignMessage.ID},
857+
"decode(json['message'], foo.Foo.fromJson)",
858+
},
859+
{
860+
// Custom encoding.
861+
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: ".google.protobuf.Duration"},
862+
"decodeCustom(json['message'], Duration.fromJson)",
863+
},
864+
{
865+
// Map of bytes.
866+
&api.Field{Name: "message", JSONName: "message", Map: true, Typez: api.MESSAGE_TYPE, TypezID: mapStringToBytes.ID},
867+
"decodeMapBytes(json['message']) ?? {}",
868+
},
788869
} {
789870
t.Run(test.field.Name, func(t *testing.T) {
790871
message := &api.Message{
@@ -793,9 +874,11 @@ func TestCreateFromJsonLine(t *testing.T) {
793874
Package: sample.Package,
794875
Fields: []*api.Field{test.field},
795876
}
796-
model := api.NewTestAPI([]*api.Message{message, secret}, []*api.Enum{}, []*api.Service{})
877+
model := api.NewTestAPI([]*api.Message{message, secret, foreignMessage, mapStringToBytes}, []*api.Enum{enumState, foreignEnumState}, []*api.Service{})
797878
annotate := newAnnotateModel(model)
798-
annotate.annotateModel(map[string]string{})
879+
annotate.annotateModel(map[string]string{
880+
"prefix:google.cloud.foo": "foo",
881+
})
799882
codec := test.field.Codec.(*fieldAnnotation)
800883

801884
got := annotate.createFromJsonLine(test.field, model.State, codec.Required)
@@ -810,6 +893,25 @@ func TestCreateToJsonLine(t *testing.T) {
810893
secret := sample.Secret()
811894
enum := sample.EnumState()
812895

896+
foreignMessage := &api.Message{
897+
Name: "Foo",
898+
Package: "google.cloud.foo",
899+
ID: "google.cloud.foo.Foo",
900+
Enums: []*api.Enum{},
901+
Fields: []*api.Field{},
902+
}
903+
foreignEnumState := &api.Enum{
904+
Name: "ForeignEnum",
905+
Package: "google.cloud.foo",
906+
ID: "google.cloud.foo.ForeignEnum",
907+
Values: []*api.EnumValue{
908+
{
909+
Name: "Enabled",
910+
Number: 1,
911+
},
912+
},
913+
}
914+
813915
for _, test := range []struct {
814916
field *api.Field
815917
want string
@@ -874,11 +976,25 @@ func TestCreateToJsonLine(t *testing.T) {
874976
"encodeListBytes(bytesList)",
875977
},
876978

979+
// enums
980+
{
981+
&api.Field{Name: "message", JSONName: "message", Typez: api.ENUM_TYPE, TypezID: enum.ID},
982+
"message.toJson()",
983+
},
984+
{
985+
&api.Field{Name: "message", JSONName: "message", Typez: api.ENUM_TYPE, TypezID: foreignEnumState.ID},
986+
"message.toJson()",
987+
},
988+
877989
// messages
878990
{
879991
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: secret.ID},
880992
"message!.toJson()",
881993
},
994+
{
995+
&api.Field{Name: "message", JSONName: "message", Typez: api.MESSAGE_TYPE, TypezID: foreignMessage.ID},
996+
"message!.toJson()",
997+
},
882998
} {
883999
t.Run(test.field.Name, func(t *testing.T) {
8841000
message := &api.Message{
@@ -887,9 +1003,11 @@ func TestCreateToJsonLine(t *testing.T) {
8871003
Package: sample.Package,
8881004
Fields: []*api.Field{test.field},
8891005
}
890-
model := api.NewTestAPI([]*api.Message{message, secret}, []*api.Enum{enum}, []*api.Service{})
1006+
model := api.NewTestAPI([]*api.Message{message, secret, foreignMessage}, []*api.Enum{enum, foreignEnumState}, []*api.Service{})
8911007
annotate := newAnnotateModel(model)
892-
annotate.annotateModel(map[string]string{})
1008+
annotate.annotateModel(map[string]string{
1009+
"prefix:google.cloud.foo": "foo",
1010+
})
8931011
codec := test.field.Codec.(*fieldAnnotation)
8941012

8951013
got := createToJsonLine(test.field, model.State, codec.Required)

internal/sidekick/dart/dart_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func TestEnumValues(t *testing.T) {
141141
}
142142
}
143143

144-
func TestResolveTypeName(t *testing.T) {
144+
func TestResolveMessageName(t *testing.T) {
145145
message := sample.CreateRequest()
146146
model := api.NewTestAPI([]*api.Message{
147147
message, {
@@ -169,14 +169,14 @@ func TestResolveTypeName(t *testing.T) {
169169
{".google.protobuf.Timestamp", "Timestamp"},
170170
{".google.protobuf.Duration", "Duration"},
171171
} {
172-
got := annotate.resolveTypeName(state.MessageByID[test.typeId], true)
172+
got := annotate.resolveMessageName(state.MessageByID[test.typeId], true)
173173
if got != test.want {
174174
t.Errorf("unexpected type name, got: %s want: %s", got, test.want)
175175
}
176176
}
177177
}
178178

179-
func TestResolveTypeName_ImportsMessages(t *testing.T) {
179+
func TestResolveMessageName_ImportsMessages(t *testing.T) {
180180
model := api.NewTestAPI([]*api.Message{
181181
{
182182
ID: ".google.protobuf.Any",
@@ -213,14 +213,14 @@ func TestResolveTypeName_ImportsMessages(t *testing.T) {
213213
{".google.type.Expr", "package:google_cloud_type/type.dart"},
214214
} {
215215
annotate.imports = map[string]bool{}
216-
annotate.resolveTypeName(state.MessageByID[test.typeId], true)
216+
annotate.resolveMessageName(state.MessageByID[test.typeId], true)
217217
if _, ok := annotate.imports[test.want]; !ok {
218218
t.Errorf("import not added, got: %v want: %s", annotate.imports, test.want)
219219
}
220220
}
221221
}
222222

223-
func TestResolveTypeName_ImportsEnum(t *testing.T) {
223+
func TestFieldType_EnumImports(t *testing.T) {
224224
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{
225225
{
226226
ID: ".google.type.DayOfWeek",
@@ -252,7 +252,7 @@ func TestResolveTypeName_ImportsEnum(t *testing.T) {
252252
}
253253
}
254254

255-
func TestResolveTypeNameImportPrefixes(t *testing.T) {
255+
func TestResolveMessageNameImportPrefixes(t *testing.T) {
256256
model := api.NewTestAPI([]*api.Message{
257257
{
258258
ID: ".google.protobuf.Timestamp",
@@ -290,7 +290,7 @@ func TestResolveTypeNameImportPrefixes(t *testing.T) {
290290
{".google.type.DayOfWeek", "type.DayOfWeek"},
291291
} {
292292
t.Run(test.want, func(t *testing.T) {
293-
got := annotate.resolveTypeName(state.MessageByID[test.typeId], true)
293+
got := annotate.resolveMessageName(state.MessageByID[test.typeId], true)
294294
if got != test.want {
295295
t.Errorf("unexpected type name, got: %s want: %s", got, test.want)
296296
}

0 commit comments

Comments
 (0)