Repository: goadesign/goa
Pull request #3104 (2022-07-27 05:42:24)

Properly dup the fact that an attribute is finalized
This commit fixes a bug where the expr.DupAtt function wouldn't set
the finalized bit on an attribute causing it to be finalized multiple
times. This caused issues because attribute types can be renamed
when creating HTTP request and response body types. Finalizing
post-rename would cause base types to be merged which could override
the rename.


Patch:
diff --git a/expr/dup.go b/expr/dup.go
index 1a184b52..f14c0e47 100644
--- a/expr/dup.go
+++ b/expr/dup.go
@@ -57,6 +57,7 @@ func (d *dupper) DupAttribute(att *AttributeExpr) *AttributeExpr {
 		ZeroValue:    att.ZeroValue,
 		DSLFunc:      att.DSLFunc,
 		UserExamples: att.UserExamples,
+		finalized:    att.finalized,
 	}
 	d.ats[&dup] = struct{}{}
 	return &dup


Test patch:
diff --git a/http/codegen/server_encode_test.go b/http/codegen/server_encode_test.go
index 3aaeb263..165f61ce 100644
--- a/http/codegen/server_encode_test.go
+++ b/http/codegen/server_encode_test.go
@@ -106,14 +106,24 @@ func TestEncode(t *testing.T) {
 
 func TestEncodeMarshallingAndUnmarshalling(t *testing.T) {
 	cases := []struct {
-		Name string
-		DSL  func()
-		Code []string
+		Name           string
+		DSL            func()
+		Code           []string
+		SectionsOffset int
 	}{
-		{"result-with-embedded-custom-pkg-type", testdata.EmbeddedCustomPkgTypeDSL, []string{testdata.EmbeddedCustomPkgTypeUnmarshalCode, testdata.EmbeddedCustomPkgTypeMarshalCode}},
-		{"result-with-array-alias-extended", testdata.ArrayAliasExtendedDSL, []string{testdata.ArrayAliasExtendedUnmarshalCode, testdata.ArrayAliasExtendedMarshalCode}},
+		{"embedded-custom-pkg-type", testdata.EmbeddedCustomPkgTypeDSL, []string{
+			testdata.EmbeddedCustomPkgTypeUnmarshalCode,
+			testdata.EmbeddedCustomPkgTypeMarshalCode}, 3},
+		{"array-alias-extended", testdata.ArrayAliasExtendedDSL, []string{
+			testdata.ArrayAliasExtendedUnmarshalCode,
+			testdata.ArrayAliasExtendedMarshalCode}, 3},
+		{"extension-with-alias", testdata.ExtensionWithAliasDSL, []string{
+			testdata.ExtensionWithAliasUnmarshalExtensionCode,
+			testdata.ExtensionWithAliasUnmarshalBarCode,
+			testdata.ExtensionWithAliasMarshalResultCode,
+			testdata.ExtensionWithAliasMarshalExtensionCode,
+			testdata.ExtensionWithAliasMarshalBarCode}, 4},
 	}
-	const totalSectionsOffset = 3
 	for _, c := range cases {
 		t.Run(c.Name, func(t *testing.T) {
 			RunHTTPDSL(t, c.DSL)
@@ -122,14 +132,14 @@ func TestEncodeMarshallingAndUnmarshalling(t *testing.T) {
 				t.Fatalf("got %d files, expected two", len(fs))
 			}
 			sections := fs[1].SectionTemplates
-			totalSectionsExpected := totalSectionsOffset + len(c.Code)
+			totalSectionsExpected := c.SectionsOffset + len(c.Code)
 			if len(sections) != totalSectionsExpected {
 				t.Fatalf("got %d sections, expected %d", len(sections), totalSectionsExpected)
 			}
 			for i := 0; i < len(c.Code); i++ {
-				code := codegen.SectionCode(t, sections[totalSectionsOffset+i])
+				code := codegen.SectionCode(t, sections[c.SectionsOffset+i])
 				if code != c.Code[i] {
-					t.Errorf("%s %d invalid code, got:\n%s\ngot vs. expected:\n%s", sections[totalSectionsOffset+i].Name, i, code, codegen.Diff(t, code, c.Code[i]))
+					t.Errorf("%s %d invalid code, got:\n%s\ngot vs. expected:\n%s", sections[c.SectionsOffset+i].Name, i, code, codegen.Diff(t, code, c.Code[i]))
 				}
 			}
 		})
diff --git a/http/codegen/testdata/result_dsls.go b/http/codegen/testdata/result_dsls.go
index 2ee44c3d..90dfa11b 100644
--- a/http/codegen/testdata/result_dsls.go
+++ b/http/codegen/testdata/result_dsls.go
@@ -824,6 +824,35 @@ var ArrayAliasExtendedDSL = func() {
 
 }
 
+var ExtensionWithAliasDSL = func() {
+	var Bar = Type("Bar", func() {
+		Attribute("Bar", UInt)
+		Required("Bar")
+	})
+
+	var TypeWithAlias = Type("TypeWithAlias", func() {
+		Attribute("Bar", Bar)
+	})
+
+	var Extension = Type("Extension", func() {
+		Extend(TypeWithAlias)
+	})
+
+	var ResultType = Type("ResultType", func() {
+		Attribute("Extension", Extension)
+	})
+
+	var _ = Service("FooService", func() {
+		Method("FooMethod", func() {
+			Payload(ArrayOf(ResultType))
+			Result(ArrayOf(ResultType))
+			HTTP(func() {
+				GET("/")
+			})
+		})
+	})
+}
+
 var EmptyErrorResponseBodyDSL = func() {
 	Service("ServiceEmptyErrorResponseBody", func() {
 		Method("MethodEmptyErrorResponseBody", func() {
diff --git a/http/codegen/testdata/result_encode_types.go b/http/codegen/testdata/result_encode_types.go
index 036961ca..3c1e2601 100644
--- a/http/codegen/testdata/result_encode_types.go
+++ b/http/codegen/testdata/result_encode_types.go
@@ -28,6 +28,19 @@ func marshalFooFooToFooResponseBody(v *foo.Foo) *FooResponseBody {
 }
 `
 
+var ArrayAliasExtendedUnmarshalCode = `// unmarshalResultTypeRequestBodyToFooserviceResultType builds a value of type
+// *fooservice.ResultType from a value of type *ResultTypeRequestBody.
+func unmarshalResultTypeRequestBodyToFooserviceResultType(v *ResultTypeRequestBody) *fooservice.ResultType {
+	res := &fooservice.ResultType{}
+	if v.Foo != nil {
+		foo := fooservice.Foo(*v.Foo)
+		res.Foo = &foo
+	}
+
+	return res
+}
+`
+
 var ArrayAliasExtendedMarshalCode = `// marshalFooserviceResultTypeToResultTypeResponse builds a value of type
 // *ResultTypeResponse from a value of type *fooservice.ResultType.
 func marshalFooserviceResultTypeToResultTypeResponse(v *fooservice.ResultType) *ResultTypeResponse {
@@ -41,13 +54,70 @@ func marshalFooserviceResultTypeToResultTypeResponse(v *fooservice.ResultType) *
 }
 `
 
-var ArrayAliasExtendedUnmarshalCode = `// unmarshalResultTypeRequestBodyToFooserviceResultType builds a value of type
-// *fooservice.ResultType from a value of type *ResultTypeRequestBody.
-func unmarshalResultTypeRequestBodyToFooserviceResultType(v *ResultTypeRequestBody) *fooservice.ResultType {
-	res := &fooservice.ResultType{}
-	if v.Foo != nil {
-		foo := fooservice.Foo(*v.Foo)
-		res.Foo = &foo
+var ExtensionWithAliasUnmarshalExtensionCode = `// unmarshalExtensionRequestBodyToFooserviceExtension builds a value of type
+// *fooservice.Extension from a value of type *ExtensionRequestBody.
+func unmarshalExtensionRequestBodyToFooserviceExtension(v *ExtensionRequestBody) *fooservice.Extension {
+	if v == nil {
+		return nil
+	}
+	res := &fooservice.Extension{}
+	if v.Bar != nil {
+		res.Bar = unmarshalBarRequestBodyToFooserviceBar(v.Bar)
+	}
+
+	return res
+}
+`
+
+var ExtensionWithAliasUnmarshalBarCode = `// unmarshalBarRequestBodyToFooserviceBar builds a value of type
+// *fooservice.Bar from a value of type *BarRequestBody.
+func unmarshalBarRequestBodyToFooserviceBar(v *BarRequestBody) *fooservice.Bar {
+	if v == nil {
+		return nil
+	}
+	res := &fooservice.Bar{
+		Bar: *v.Bar,
+	}
+
+	return res
+}
+`
+
+var ExtensionWithAliasMarshalResultCode = `// marshalFooserviceResultTypeToResultTypeResponse builds a value of type
+// *ResultTypeResponse from a value of type *fooservice.ResultType.
+func marshalFooserviceResultTypeToResultTypeResponse(v *fooservice.ResultType) *ResultTypeResponse {
+	res := &ResultTypeResponse{}
+	if v.Extension != nil {
+		res.Extension = marshalFooserviceExtensionToExtensionResponse(v.Extension)
+	}
+
+	return res
+}
+`
+
+var ExtensionWithAliasMarshalExtensionCode = `// marshalFooserviceExtensionToExtensionResponse builds a value of type
+// *ExtensionResponse from a value of type *fooservice.Extension.
+func marshalFooserviceExtensionToExtensionResponse(v *fooservice.Extension) *ExtensionResponse {
+	if v == nil {
+		return nil
+	}
+	res := &ExtensionResponse{}
+	if v.Bar != nil {
+		res.Bar = marshalFooserviceBarToBarResponse(v.Bar)
+	}
+
+	return res
+}
+`
+
+var ExtensionWithAliasMarshalBarCode = `// marshalFooserviceBarToBarResponse builds a value of type *BarResponse from a
+// value of type *fooservice.Bar.
+func marshalFooserviceBarToBarResponse(v *fooservice.Bar) *BarResponse {
+	if v == nil {
+		return nil
+	}
+	res := &BarResponse{
+		Bar: v.Bar,
 	}
 
 	return res