Repository: goadesign/goa
Pull request #3027 (2022-03-08 20:55:09)

Properly handle alias type used in many payloads
Server side already dedupes but client side did not.
Added tests for both.


Patch:
diff --git a/grpc/codegen/client_types.go b/grpc/codegen/client_types.go
index c6ec71b2..b7759f0e 100644
--- a/grpc/codegen/client_types.go
+++ b/grpc/codegen/client_types.go
@@ -35,10 +35,16 @@ func clientType(genpkg string, svc *expr.GRPCServiceExpr, seen map[string]struct
 		sd = GRPCServices.Get(svc.Name())
 	)
 	{
+		seen := make(map[string]struct{})
 		collect := func(c *ConvertData) {
-			if c.Init != nil {
-				initData = append(initData, c.Init)
+			if c.Init == nil {
+				return
 			}
+			if _, ok := seen[c.Init.Name]; ok {
+				return
+			}
+			seen[c.Init.Name] = struct{}{}
+			initData = append(initData, c.Init)
 		}
 		for _, a := range svc.GRPCEndpoints {
 			ed := sd.Endpoint(a.Name())


Test patch:
diff --git a/grpc/codegen/client_types_test.go b/grpc/codegen/client_types_test.go
index 0291faaa..4565b1c2 100644
--- a/grpc/codegen/client_types_test.go
+++ b/grpc/codegen/client_types_test.go
@@ -16,6 +16,7 @@ func TestClientTypeFiles(t *testing.T) {
 		Code string
 	}{
 		{"payload-with-nested-types", testdata.PayloadWithNestedTypesDSL, testdata.PayloadWithNestedTypesClientTypeCode},
+		{"payload-with-duplicate-use", testdata.PayloadWithMultipleUseTypesDSL, testdata.PayloadWithMultipleUseTypesClientTypeCode},
 		{"payload-with-alias-type", testdata.PayloadWithAliasTypeDSL, testdata.PayloadWithAliasTypeClientTypeCode},
 		{"result-collection", testdata.ResultWithCollectionDSL, testdata.ResultWithCollectionClientTypeCode},
 		{"with-errors", testdata.UnaryRPCWithErrorsDSL, testdata.WithErrorsClientTypeCode},
diff --git a/grpc/codegen/server_types_test.go b/grpc/codegen/server_types_test.go
index 707710a1..53e5a3e3 100644
--- a/grpc/codegen/server_types_test.go
+++ b/grpc/codegen/server_types_test.go
@@ -16,6 +16,7 @@ func TestServerTypeFiles(t *testing.T) {
 		Code string
 	}{
 		{"payload-with-nested-types", testdata.PayloadWithNestedTypesDSL, testdata.PayloadWithNestedTypesServerTypeCode},
+		{"payload-with-duplicate-use", testdata.PayloadWithMultipleUseTypesDSL, testdata.PayloadWithMultipleUseTypesServerTypeCode},
 		{"payload-with-alias-type", testdata.PayloadWithAliasTypeDSL, testdata.PayloadWithAliasTypeServerTypeCode},
 		{"payload-with-mixed-attributes", testdata.PayloadWithMixedAttributesDSL, testdata.PayloadWithMixedAttributesServerTypeCode},
 		{"result-collection", testdata.ResultWithCollectionDSL, testdata.ResultWithCollectionServerTypeCode},
diff --git a/grpc/codegen/testdata/client_type_code.go b/grpc/codegen/testdata/client_type_code.go
index bffbb012..c5e7cb4c 100644
--- a/grpc/codegen/testdata/client_type_code.go
+++ b/grpc/codegen/testdata/client_type_code.go
@@ -102,6 +102,16 @@ func svcServicepayloadwithnestedtypesBParamsToServicePayloadWithNestedTypespbBPa
 }
 `
 
+const PayloadWithMultipleUseTypesClientTypeCode = `// NewDupePayload builds the gRPC request type from the payload of the
+// "MethodPayloadDuplicateA" endpoint of the "ServicePayloadWithNestedTypes"
+// service.
+func NewDupePayload(payload servicepayloadwithnestedtypes.DupePayload) *service_payload_with_nested_typespb.DupePayload {
+	message := &service_payload_with_nested_typespb.DupePayload{}
+	message.Field = string(payload)
+	return message
+}
+`
+
 const PayloadWithAliasTypeClientTypeCode = `// NewMethodMessageUserTypeWithAliasRequest builds the gRPC request type from
 // the payload of the "MethodMessageUserTypeWithAlias" endpoint of the
 // "ServiceMessageUserTypeWithAlias" service.
diff --git a/grpc/codegen/testdata/dsls.go b/grpc/codegen/testdata/dsls.go
index 376db68c..fa27edf9 100644
--- a/grpc/codegen/testdata/dsls.go
+++ b/grpc/codegen/testdata/dsls.go
@@ -573,6 +573,20 @@ var PayloadWithNestedTypesDSL = func() {
 	})
 }
 
+var PayloadWithMultipleUseTypesDSL = func() {
+	var DupePayload = Type("DupePayload", String)
+	Service("ServicePayloadWithNestedTypes", func() {
+		Method("MethodPayloadDuplicateA", func() {
+			Payload(DupePayload)
+			GRPC(func() {})
+		})
+		Method("MethodPayloadDuplicateB", func() {
+			Payload(DupePayload)
+			GRPC(func() {})
+		})
+	})
+}
+
 var PayloadWithAliasTypeDSL = func() {
 	var IntAlias = Type("IntAlias", Int)
 	var PayloadAliasT = Type("PayloadAliasT", func() {
diff --git a/grpc/codegen/testdata/server_type_code.go b/grpc/codegen/testdata/server_type_code.go
index c00e5fb6..1ba632eb 100644
--- a/grpc/codegen/testdata/server_type_code.go
+++ b/grpc/codegen/testdata/server_type_code.go
@@ -110,6 +110,39 @@ func svcServicepayloadwithnestedtypesBParamsToServicePayloadWithNestedTypespbBPa
 }
 `
 
+const PayloadWithMultipleUseTypesServerTypeCode = `// NewMethodPayloadDuplicateAPayload builds the payload of the
+// "MethodPayloadDuplicateA" endpoint of the "ServicePayloadWithNestedTypes"
+// service from the gRPC request type.
+func NewMethodPayloadDuplicateAPayload(message *service_payload_with_nested_typespb.DupePayload) servicepayloadwithnestedtypes.DupePayload {
+	v := servicepayloadwithnestedtypes.DupePayload(message.Field)
+	return v
+}
+
+// NewMethodPayloadDuplicateAResponse builds the gRPC response type from the
+// result of the "MethodPayloadDuplicateA" endpoint of the
+// "ServicePayloadWithNestedTypes" service.
+func NewMethodPayloadDuplicateAResponse() *service_payload_with_nested_typespb.MethodPayloadDuplicateAResponse {
+	message := &service_payload_with_nested_typespb.MethodPayloadDuplicateAResponse{}
+	return message
+}
+
+// NewMethodPayloadDuplicateBPayload builds the payload of the
+// "MethodPayloadDuplicateB" endpoint of the "ServicePayloadWithNestedTypes"
+// service from the gRPC request type.
+func NewMethodPayloadDuplicateBPayload(message *service_payload_with_nested_typespb.DupePayload) servicepayloadwithnestedtypes.DupePayload {
+	v := servicepayloadwithnestedtypes.DupePayload(message.Field)
+	return v
+}
+
+// NewMethodPayloadDuplicateBResponse builds the gRPC response type from the
+// result of the "MethodPayloadDuplicateB" endpoint of the
+// "ServicePayloadWithNestedTypes" service.
+func NewMethodPayloadDuplicateBResponse() *service_payload_with_nested_typespb.MethodPayloadDuplicateBResponse {
+	message := &service_payload_with_nested_typespb.MethodPayloadDuplicateBResponse{}
+	return message
+}
+`
+
 const PayloadWithAliasTypeServerTypeCode = `// NewMethodMessageUserTypeWithAliasPayload builds the payload of the
 // "MethodMessageUserTypeWithAlias" endpoint of the
 // "ServiceMessageUserTypeWithAlias" service from the gRPC request type.