Repository: goadesign/goa
Pull request #3412 (2023-11-07 15:28:40)

Add openapi:typename Meta support to openapi v2
Related to https://github.com/goadesign/goa/pull/3203


Patch:
diff --git a/expr/http_body_types.go b/expr/http_body_types.go
index ec37ed7e..4651b42c 100644
--- a/expr/http_body_types.go
+++ b/expr/http_body_types.go
@@ -189,6 +189,13 @@ func httpRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
 	}
 	appendSuffix(ut.Attribute().Type, suffix)
 
+	// Remember openapi typename for example to generate friendly OpenAPI specs.
+	if t, ok := payload.Type.(UserType); ok {
+		if m, ok := t.Attribute().Meta["openapi:typename"]; ok {
+			ut.AttributeExpr.AddMeta("openapi:typename", m...)
+		}
+	}
+
 	return &AttributeExpr{
 		Type:         ut,
 		Validation:   att.Validation,
@@ -318,10 +325,13 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
 		UID:           concat(svc.Name(), "#", name),
 	}
 
-	// Remember original type name for example to generate friendly OpenAPI
-	// specs.
+	// Remember original type name and openapi typename for example
+	// to generate friendly OpenAPI specs.
 	if t, ok := attr.Type.(UserType); ok {
 		userType.AttributeExpr.AddMeta("name:original", t.Name())
+		if m, ok := t.Attribute().Meta["openapi:typename"]; ok {
+			userType.AttributeExpr.AddMeta("openapi:typename", m...)
+		}
 	}
 
 	appendSuffix(userType.Attribute().Type, suffix)
diff --git a/http/codegen/openapi/json_schema.go b/http/codegen/openapi/json_schema.go
index 112db5ff..82d1b3c5 100644
--- a/http/codegen/openapi/json_schema.go
+++ b/http/codegen/openapi/json_schema.go
@@ -235,8 +235,18 @@ func ResultTypeRefWithPrefix(api *expr.APIExpr, mt *expr.ResultTypeExpr, view st
 	if err != nil {
 		panic(fmt.Sprintf("failed to project media type %#v: %s", mt.Identifier, err)) // bug
 	}
+	var metaName string
+	if n, ok := mt.Meta["openapi:typename"]; ok {
+		metaName = codegen.Goify(n[0], true)
+	}
+	if metaName != "" {
+		projected.TypeName = metaName
+	}
 	if _, ok := Definitions[projected.TypeName]; !ok {
 		projected.TypeName = codegen.Goify(prefix, true) + codegen.Goify(projected.TypeName, true)
+		if metaName != "" {
+			projected.TypeName = metaName
+		}
 		GenerateResultTypeDefinition(api, projected, "default")
 	}
 	return fmt.Sprintf("#/definitions/%s", projected.TypeName)
@@ -254,6 +264,9 @@ func TypeRefWithPrefix(api *expr.APIExpr, ut *expr.UserTypeExpr, prefix string)
 	if prefix != "" {
 		typeName = codegen.Goify(prefix, true) + codegen.Goify(ut.TypeName, true)
 	}
+	if n, ok := ut.Meta["openapi:typename"]; ok {
+		typeName = codegen.Goify(n[0], true)
+	}
 	if _, ok := Definitions[typeName]; !ok {
 		GenerateTypeDefinitionWithName(api, ut, typeName)
 	}


Test patch:
diff --git a/http/codegen/openapi/v2/files_test.go b/http/codegen/openapi/v2/files_test.go
index e5b0f007..4c628b96 100644
--- a/http/codegen/openapi/v2/files_test.go
+++ b/http/codegen/openapi/v2/files_test.go
@@ -40,6 +40,7 @@ func TestSections(t *testing.T) {
 		{"with-spaces", testdata.WithSpacesDSL},
 		{"with-map", testdata.WithMapDSL},
 		{"path-with-wildcards", testdata.PathWithWildcardDSL},
+		{"typename", testdata.TypenameDSL},
 		{"not-generate-server", testdata.NotGenerateServerDSL},
 		{"not-generate-host", testdata.NotGenerateHostDSL},
 	}
diff --git a/http/codegen/openapi/v2/testdata/TestSections/typename_file0.golden b/http/codegen/openapi/v2/testdata/TestSections/typename_file0.golden
new file mode 100644
index 00000000..f451f2b8
--- /dev/null
+++ b/http/codegen/openapi/v2/testdata/TestSections/typename_file0.golden
@@ -0,0 +1,1 @@
+{"swagger":"2.0","info":{"title":"","version":""},"host":"goa.design","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/bar":{"post":{"tags":["testService"],"summary":"bar testService","operationId":"testService#bar","parameters":[{"name":"BarRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/BarPayload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/BarResult"}}},"schemes":["https"]}},"/baz":{"post":{"tags":["testService"],"summary":"baz testService","operationId":"testService#baz","parameters":[{"name":"BazRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/BazPayload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/BazResult"}}},"schemes":["https"]}},"/foo":{"post":{"tags":["testService"],"summary":"foo testService","operationId":"testService#foo","parameters":[{"name":"FooRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/FooPayload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/FooResult"}}},"schemes":["https"]}}},"definitions":{"BarPayload":{"title":"BarPayload","type":"object","properties":{"value":{"type":"string","example":""}},"example":{"value":""}},"BarResult":{"title":"Mediatype identifier: application/vnd.goa.example.bar; view=default","type":"object","properties":{"value":{"type":"string","example":""}},"description":"BarResponseBody result type (default view)","example":{"value":""}},"BazPayload":{"title":"BazPayload","type":"object","properties":{"value":{"type":"string","example":""}},"example":{"value":""}},"BazResult":{"title":"BazResult","type":"object","properties":{"value":{"type":"string","example":""}},"example":{"value":""}},"FooPayload":{"title":"FooPayload","type":"object","properties":{"value":{"type":"string","example":""}},"example":{"value":""}},"FooResult":{"title":"Mediatype identifier: application/vnd.goa.example.bar; view=default","type":"object","properties":{"value":{"type":"string","example":""}},"description":"FooResponseBody result type (default view)","example":{"value":""}}}}
\ No newline at end of file
diff --git a/http/codegen/openapi/v2/testdata/TestSections/typename_file1.golden b/http/codegen/openapi/v2/testdata/TestSections/typename_file1.golden
new file mode 100644
index 00000000..9df4bd24
--- /dev/null
+++ b/http/codegen/openapi/v2/testdata/TestSections/typename_file1.golden
@@ -0,0 +1,128 @@
+swagger: "2.0"
+info:
+    title: ""
+    version: ""
+host: goa.design
+consumes:
+    - application/json
+    - application/xml
+    - application/gob
+produces:
+    - application/json
+    - application/xml
+    - application/gob
+paths:
+    /bar:
+        post:
+            tags:
+                - testService
+            summary: bar testService
+            operationId: testService#bar
+            parameters:
+                - name: BarRequestBody
+                  in: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/BarPayload'
+            responses:
+                "200":
+                    description: OK response.
+                    schema:
+                        $ref: '#/definitions/BarResult'
+            schemes:
+                - https
+    /baz:
+        post:
+            tags:
+                - testService
+            summary: baz testService
+            operationId: testService#baz
+            parameters:
+                - name: BazRequestBody
+                  in: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/BazPayload'
+            responses:
+                "200":
+                    description: OK response.
+                    schema:
+                        $ref: '#/definitions/BazResult'
+            schemes:
+                - https
+    /foo:
+        post:
+            tags:
+                - testService
+            summary: foo testService
+            operationId: testService#foo
+            parameters:
+                - name: FooRequestBody
+                  in: body
+                  required: true
+                  schema:
+                    $ref: '#/definitions/FooPayload'
+            responses:
+                "200":
+                    description: OK response.
+                    schema:
+                        $ref: '#/definitions/FooResult'
+            schemes:
+                - https
+definitions:
+    BarPayload:
+        title: BarPayload
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        example:
+            value: ""
+    BarResult:
+        title: 'Mediatype identifier: application/vnd.goa.example.bar; view=default'
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        description: BarResponseBody result type (default view)
+        example:
+            value: ""
+    BazPayload:
+        title: BazPayload
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        example:
+            value: ""
+    BazResult:
+        title: BazResult
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        example:
+            value: ""
+    FooPayload:
+        title: FooPayload
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        example:
+            value: ""
+    FooResult:
+        title: 'Mediatype identifier: application/vnd.goa.example.bar; view=default'
+        type: object
+        properties:
+            value:
+                type: string
+                example: ""
+        description: FooResponseBody result type (default view)
+        example:
+            value: ""