Repository: goadesign/goa
Pull request #3250 (2023-02-25 23:45:26)

Fix OpenAPI for result types with multiple views


Patch:
diff --git a/go.mod b/go.mod
index 59a2f49c..1294db3a 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.20
 require (
 	github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598
 	github.com/dimfeld/httptreemux/v5 v5.5.0
-	github.com/getkin/kin-openapi v0.112.0
+	github.com/getkin/kin-openapi v0.114.0
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/websocket v1.5.0
 	github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d
@@ -26,9 +26,10 @@ require (
 	github.com/google/gxui v0.0.0-20151028112939-f85e0a97b3a4 // indirect
 	github.com/invopop/yaml v0.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
-	github.com/mailru/easyjson v0.7.6 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
+	github.com/perimeterx/marshmallow v1.1.4 // indirect
 	github.com/smartystreets/goconvey v1.7.2 // indirect
 	golang.org/x/mod v0.8.0 // indirect
 	golang.org/x/net v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index e4eb17ac..7a08a918 100644
--- a/go.sum
+++ b/go.sum
@@ -6,13 +6,15 @@ github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 h1:MGKhKyiYrvMDZs
 github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598/go.mod h1:0FpDmbrt36utu8jEmeU05dPC9AB5tsLYVVi+ZHfyuwI=
 github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
 github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
-github.com/getkin/kin-openapi v0.112.0 h1:lnLXx3bAG53EJVI4E/w0N8i1Y/vUZUEsnrXkgnfn7/Y=
-github.com/getkin/kin-openapi v0.112.0/go.mod h1:QtwUNt0PAAgIIBEvFWYfB7dfngxtAaqCX1zYHMZDeK8=
+github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8=
+github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
 github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
 github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
+github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
@@ -40,8 +42,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
 github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d h1:Zj+PHjnhRYWBK6RqCDBcAhLXoi3TzC27Zad/Vn+gnVQ=
 github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d/go.mod h1:WZy8Q5coAB1zhY9AOBJP0O6J4BuDfbupUDavKY+I3+s=
 github.com/manveru/gobdd v0.0.0-20131210092515-f1a17fdd710b h1:3E44bLeN8uKYdfQqVQycPnaVviZdBLbizFhU49mtbe4=
@@ -50,6 +53,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
+github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -70,6 +75,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
+github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
+github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
+github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea h1:CyhwejzVGvZ3Q2PSbQ4NRRYn+ZWv5eS1vlaEusT+bAI=
 github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea/go.mod h1:eNr558nEUjP8acGw8FFjTeWvSgU1stO7FAO6eknhHe4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
diff --git a/http/codegen/openapi/v3/types.go b/http/codegen/openapi/v3/types.go
index 5e5e9d0a..8605d075 100644
--- a/http/codegen/openapi/v3/types.go
+++ b/http/codegen/openapi/v3/types.go
@@ -122,23 +122,15 @@ func buildBodyTypes(api *expr.APIExpr) (map[string]map[string]*EndpointBodies, m
 				}
 				body := resp.Body
 				if view != "" {
-					// Static view
-					rt, err := expr.Project(body.Type.(*expr.ResultTypeExpr), view)
-					if err != nil { // bug
+					// Static view, dup and project
+					rt := expr.Dup(body.Type).(*expr.ResultTypeExpr)
+					rt, err := expr.Project(rt, view)
+					if err != nil {
 						panic(fmt.Sprintf("failed to project %q to view %q", body.Type.Name(), view))
 					}
 					body.Type = rt
 				}
 				js := sf.schemafy(body)
-				if rt, ok := resp.Body.Type.(*expr.ResultTypeExpr); ok && js != nil {
-					if view == "" && rt.HasMultipleViews() {
-						// Dynamic views
-						if len(js.Description) > 0 {
-							js.Description += "\n"
-						}
-						js.Description += sf.viewsNote(rt)
-					}
-				}
 				res[resp.StatusCode] = append(res[resp.StatusCode], js)
 			}
 			sbodies[e.Name()] = &EndpointBodies{req, res}
@@ -243,10 +235,23 @@ func (sf *schemafier) schemafy(attr *expr.AttributeExpr, noref ...bool) *openapi
 			} else if n, ok := t.Attribute().Meta["name:original"]; ok {
 				name = n[0]
 			}
-			typeName := sf.uniquify(codegen.Goify(name, true))
-			s.Ref = toRef(typeName)
-			sf.hashes[h] = append(sf.hashes[h], s.Ref)
-			sf.schemas[typeName] = sf.schemafy(t.Attribute(), true)
+			if rt, ok := t.(*expr.ResultTypeExpr); ok && rt.HasMultipleViews() {
+				// For result types with multiple views, we generate
+				// a AnyOf schema with a schema for each view.
+				s.AnyOf = make([]*openapi.Schema, len(rt.Views))
+				for i, v := range rt.Views {
+					pr, err := expr.Project(rt, v.Name)
+					if err != nil {
+						panic(fmt.Sprintf("failed to project %q with view %q", rt.Name(), v.Name)) // bug, DSL should have performed validations
+					}
+					s.AnyOf[i] = sf.schemafy(&expr.AttributeExpr{Type: pr})
+				}
+			} else {
+				typeName := sf.uniquify(codegen.Goify(name, true))
+				s.Ref = toRef(typeName)
+				sf.hashes[h] = append(sf.hashes[h], s.Ref)
+				sf.schemas[typeName] = sf.schemafy(t.Attribute(), true)
+			}
 			return s // All other schema properties are set in the reference
 		}
 		// Alias primitive type
@@ -319,31 +324,6 @@ func (sf *schemafier) uniquify(n string) string {
 	return n
 }
 
-// viewsNote returns a human friendly description of the different possible
-// response body shapes for the different views supported by the attribute type
-// which must be a ResultType.
-func (sf *schemafier) viewsNote(rt *expr.ResultTypeExpr) string {
-	var alts []string
-	for _, v := range rt.Views {
-		if v.Name != expr.DefaultView {
-			pr, err := expr.Project(rt, v.Name)
-			if err != nil {
-				panic(fmt.Sprintf("failed to project %q with view %q", rt.Name(), v.Name)) // bug, DSL should have performed validations
-			}
-			js := sf.schemafy(&expr.AttributeExpr{Type: pr})
-			alts = append(alts, js.Ref)
-		}
-	}
-	oneof := ""
-	last := ""
-	if len(alts) > 1 {
-		oneof = "one of "
-		last = " or " + alts[len(alts)-1]
-		alts = alts[:len(alts)-1]
-	}
-	return "Response body may alternatively be " + oneof + strings.Join(alts, ", ") + last
-}
-
 // toRef creates a relative JSON Schema reference from a type name that points
 // to the corresponding definition in the OpenAPI "components" field.
 func toRef(n string) string {


Test patch:
diff --git a/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file0.golden b/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file0.golden
index 1625a81d..22380706 100644
--- a/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file0.golden
+++ b/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file0.golden
@@ -1,1 +1,1 @@
-{"swagger":"2.0","info":{"title":"","version":""},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointDefault testService","operationId":"testService#testEndpointDefault","produces":["application/custom+json"],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/TestServiceTestEndpointDefaultResponseBody"}}},"schemes":["http"]}},"/tiny":{"get":{"tags":["testService"],"summary":"testEndpointTiny testService","operationId":"testService#testEndpointTiny","produces":["application/vnd.custom+json"],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/TestServiceTestEndpointTinyResponseBody"}}},"schemes":["http"]}}},"definitions":{"TestServiceTestEndpointDefaultResponseBody":{"title":"Mediatype identifier: application/json; view=default","type":"object","properties":{"int":{"type":"integer","example":1,"format":"int64"},"string":{"type":"string","example":""}},"description":"TestEndpointDefaultResponseBody result type (default view)","example":{"int":1,"string":""}},"TestServiceTestEndpointTinyResponseBody":{"title":"Mediatype identifier: application/json; view=default","type":"object","properties":{"int":{"type":"integer","example":1,"format":"int64"},"string":{"type":"string","example":""}},"description":"TestEndpointTinyResponseBody result type (default view)","example":{"int":1,"string":""}}}}
\ No newline at end of file
+{"swagger":"2.0","info":{"title":"","version":""},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointDefault testService","operationId":"testService#testEndpointDefault","produces":["application/custom+json"],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/TestServiceTestEndpointDefaultResponseBody"}}},"schemes":["http"]}},"/tiny":{"get":{"tags":["testService"],"summary":"testEndpointTiny testService","operationId":"testService#testEndpointTiny","responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/TestServiceTestEndpointTinyResponseBodyTiny"}}},"schemes":["http"]}}},"definitions":{"TestServiceTestEndpointDefaultResponseBody":{"title":"Mediatype identifier: application/json; view=default","type":"object","properties":{"int":{"type":"integer","example":1,"format":"int64"},"string":{"type":"string","example":""}},"description":"TestEndpointDefaultResponseBody result type (default view)","example":{"int":1,"string":""}},"TestServiceTestEndpointTinyResponseBodyTiny":{"title":"Mediatype identifier: application/json; view=tiny","type":"object","properties":{"string":{"type":"string","example":""}},"description":"TestEndpointTinyResponseBody result type (tiny view) (default view)","example":{"string":""}}}}
\ No newline at end of file
diff --git a/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file1.golden b/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file1.golden
index 08e38685..77f3e6e1 100644
--- a/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file1.golden
+++ b/http/codegen/openapi/v2/testdata/TestSections/multiple-views_file1.golden
@@ -33,13 +33,11 @@ paths:
                 - testService
             summary: testEndpointTiny testService
             operationId: testService#testEndpointTiny
-            produces:
-                - application/vnd.custom+json
             responses:
                 "200":
                     description: OK response.
                     schema:
-                        $ref: '#/definitions/TestServiceTestEndpointTinyResponseBody'
+                        $ref: '#/definitions/TestServiceTestEndpointTinyResponseBodyTiny'
             schemes:
                 - http
 definitions:
@@ -58,18 +56,13 @@ definitions:
         example:
             int: 1
             string: ""
-    TestServiceTestEndpointTinyResponseBody:
-        title: 'Mediatype identifier: application/json; view=default'
+    TestServiceTestEndpointTinyResponseBodyTiny:
+        title: 'Mediatype identifier: application/json; view=tiny'
         type: object
         properties:
-            int:
-                type: integer
-                example: 1
-                format: int64
             string:
                 type: string
                 example: ""
-        description: TestEndpointTinyResponseBody result type (default view)
+        description: TestEndpointTinyResponseBody result type (tiny view) (default view)
         example:
-            int: 1
             string: ""
diff --git a/http/codegen/openapi/v3/files_test.go b/http/codegen/openapi/v3/files_test.go
index 1f960fbb..fe32f6fd 100644
--- a/http/codegen/openapi/v3/files_test.go
+++ b/http/codegen/openapi/v3/files_test.go
@@ -130,6 +130,6 @@ func validateSwagger(t *testing.T, b []byte) {
 		err = swagger.Validate(context.Background())
 	}
 	if err != nil {
-		t.Errorf("invalid spec: %s", err.Error())
+		t.Errorf("invalid spec: %s\nspec:\n%s", err.Error(), string(b))
 	}
 }
diff --git a/http/codegen/openapi/v3/testdata/golden/multiple-views_file0.golden b/http/codegen/openapi/v3/testdata/golden/multiple-views_file0.golden
index 95c845bd..ae2dd54d 100644
--- a/http/codegen/openapi/v3/testdata/golden/multiple-views_file0.golden
+++ b/http/codegen/openapi/v3/testdata/golden/multiple-views_file0.golden
@@ -1,1 +1,1 @@
-{"openapi":"3.0.3","info":{"title":"Goa API","version":"1.0"},"servers":[{"url":"http://localhost:80","description":"Default server for test api"}],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointDefault testService","operationId":"testService#testEndpointDefault","responses":{"200":{"description":"OK response.","content":{"application/custom+json":{"schema":{"description":"Response body may alternatively be #/components/schemas/TestEndpointDefaultResponseBodyTiny","$ref":"#/components/schemas/Result"},"example":{"int":1,"string":""}}}}}}},"/tiny":{"get":{"tags":["testService"],"summary":"testEndpointTiny testService","operationId":"testService#testEndpointTiny","responses":{"200":{"description":"OK response.","content":{"application/vnd.custom+json":{"schema":{"description":"Response body may alternatively be #/components/schemas/TestEndpointDefaultResponseBodyTiny","$ref":"#/components/schemas/Result"},"example":{"int":1,"string":""}}}}}}}},"components":{"schemas":{"Result":{"type":"object","properties":{"int":{"type":"integer","example":1,"format":"int64"},"string":{"type":"string","example":""}},"example":{"int":1,"string":""}},"TestEndpointDefaultResponseBodyTiny":{"type":"object","properties":{"string":{"type":"string","example":""}},"description":"TestEndpointDefaultResponseBody result type (tiny view)","example":{"string":""}}}},"tags":[{"name":"testService"}]}
\ No newline at end of file
+{"openapi":"3.0.3","info":{"title":"Goa API","version":"1.0"},"servers":[{"url":"http://localhost:80","description":"Default server for test api"}],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpointDefault testService","operationId":"testService#testEndpointDefault","responses":{"200":{"description":"OK response.","content":{"application/custom+json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TestEndpointDefaultResponseBody"},{"$ref":"#/components/schemas/TestEndpointDefaultResponseBodyTiny"}]},"example":{"int":1,"string":""}}}}}}},"/tiny":{"get":{"tags":["testService"],"summary":"testEndpointTiny testService","operationId":"testService#testEndpointTiny","responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEndpointDefaultResponseBodyTiny"},"example":{"string":""}}}}}}}},"components":{"schemas":{"TestEndpointDefaultResponseBody":{"type":"object","properties":{"int":{"type":"integer","example":1,"format":"int64"},"string":{"type":"string","example":""}},"description":"TestEndpointDefaultResponseBody result type (default view)","example":{"int":1,"string":""}},"TestEndpointDefaultResponseBodyTiny":{"type":"object","properties":{"string":{"type":"string","example":""}},"description":"TestEndpointDefaultResponseBody result type (tiny view)","example":{"string":""}}}},"tags":[{"name":"testService"}]}
\ No newline at end of file
diff --git a/http/codegen/openapi/v3/testdata/golden/multiple-views_file1.golden b/http/codegen/openapi/v3/testdata/golden/multiple-views_file1.golden
index b8130f60..2b131884 100644
--- a/http/codegen/openapi/v3/testdata/golden/multiple-views_file1.golden
+++ b/http/codegen/openapi/v3/testdata/golden/multiple-views_file1.golden
@@ -18,8 +18,9 @@ paths:
                     content:
                         application/custom+json:
                             schema:
-                                description: 'Response body may alternatively be #/components/schemas/TestEndpointDefaultResponseBodyTiny'
-                                $ref: '#/components/schemas/Result'
+                                anyOf:
+                                    - $ref: '#/components/schemas/TestEndpointDefaultResponseBody'
+                                    - $ref: '#/components/schemas/TestEndpointDefaultResponseBodyTiny'
                             example:
                                 int: 1
                                 string: ""
@@ -33,16 +34,14 @@ paths:
                 "200":
                     description: OK response.
                     content:
-                        application/vnd.custom+json:
+                        application/json:
                             schema:
-                                description: 'Response body may alternatively be #/components/schemas/TestEndpointDefaultResponseBodyTiny'
-                                $ref: '#/components/schemas/Result'
+                                $ref: '#/components/schemas/TestEndpointDefaultResponseBodyTiny'
                             example:
-                                int: 1
                                 string: ""
 components:
     schemas:
-        Result:
+        TestEndpointDefaultResponseBody:
             type: object
             properties:
                 int:
@@ -52,6 +51,7 @@ components:
                 string:
                     type: string
                     example: ""
+            description: TestEndpointDefaultResponseBody result type (default view)
             example:
                 int: 1
                 string: ""
diff --git a/http/codegen/testdata/openapi_dsls.go b/http/codegen/testdata/openapi_dsls.go
index cc13088a..84de6927 100644
--- a/http/codegen/testdata/openapi_dsls.go
+++ b/http/codegen/testdata/openapi_dsls.go
@@ -101,7 +101,9 @@ var MultipleViewsDSL = func() {
 			})
 		})
 		Method("testEndpointTiny", func() {
-			Result(ResultT)
+			Result(ResultT, func() {
+				View("tiny")
+			})
 			HTTP(func() {
 				GET("/tiny")
 			})