Skip to content

Commit

Permalink
Handle Any type as a typeless schema in OpenAPI (#3561)
Browse files Browse the repository at this point in the history
* Add test cases about Any type

* Add Any type attributes to test of openapi v3

* Handle Any type as a typeless schema in OpenAPI

* Combine types of function parameters

---------

Co-authored-by: Raphael Simon <[email protected]>
  • Loading branch information
tchssk and raphael authored Jul 25, 2024
1 parent abb0a49 commit c1a4639
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 6 deletions.
7 changes: 4 additions & 3 deletions http/codegen/openapi/json_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func ResultTypeRef(api *expr.APIExpr, mt *expr.ResultTypeExpr, view string) stri

// ResultTypeRefWithPrefix produces the JSON reference to the media type definition with
// the given view and adds the provided prefix to the type name
func ResultTypeRefWithPrefix(api *expr.APIExpr, mt *expr.ResultTypeExpr, view string, prefix string) string {
func ResultTypeRefWithPrefix(api *expr.APIExpr, mt *expr.ResultTypeExpr, view, prefix string) string {
projected, err := expr.Project(mt, view)
if err != nil {
panic(fmt.Sprintf("failed to project media type %#v: %s", mt.Identifier, err)) // bug
Expand Down Expand Up @@ -318,8 +318,9 @@ func TypeSchemaWithPrefix(api *expr.APIExpr, t expr.DataType, prefix string) *Sc
s.Type = Type(actual.Name())
switch actual.Kind() {
case expr.AnyKind:
s.Type = Type("string")
s.Format = "binary"
// A schema without a type matches any data type.
// See https://swagger.io/docs/specification/data-models/data-types/#any.
s.Type = Type("")
case expr.IntKind, expr.Int64Kind,
expr.UIntKind, expr.UInt64Kind:
// Use int64 format for IntKind and UIntKind because the OpenAPI
Expand Down
1 change: 1 addition & 0 deletions http/codegen/openapi/v2/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func TestSections(t *testing.T) {
{"server-host-with-variables", testdata.ServerHostWithVariablesDSL},
{"with-spaces", testdata.WithSpacesDSL},
{"with-map", testdata.WithMapDSL},
{"with-any", testdata.WithAnyDSL},
{"path-with-wildcards", testdata.PathWithWildcardDSL},
{"path-with-multiple-wildcards", testdata.PathWithMultipleWildcardDSL},
{"path-with-multiple-explicit-wildcards", testdata.PathWithMultipleExplicitWildcardDSL},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"swagger":"2.0","info":{"title":"","version":"0.0.1"},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"post":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","parameters":[{"name":"TestEndpointRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/TestServiceTestEndpointRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/TestServiceTestEndpointResponseBody"}}},"schemes":["http"]}}},"definitions":{"TestServiceTestEndpointRequestBody":{"title":"TestServiceTestEndpointRequestBody","type":"object","properties":{"any":{"example":""},"any_array":{"type":"array","items":{"example":""},"example":["","","",""]},"any_map":{"type":"object","example":{"":""},"additionalProperties":true}},"example":{"any":"","any_array":["","",""],"any_map":{"":""}}},"TestServiceTestEndpointResponseBody":{"title":"TestServiceTestEndpointResponseBody","type":"object","properties":{"any":{"example":""},"any_array":{"type":"array","items":{"example":""},"example":["","","",""]},"any_map":{"type":"object","example":{"":""},"additionalProperties":true}},"example":{"any":"","any_array":["",""],"any_map":{"":""}}}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
swagger: "2.0"
info:
title: ""
version: 0.0.1
host: localhost:80
consumes:
- application/json
- application/xml
- application/gob
produces:
- application/json
- application/xml
- application/gob
paths:
/:
post:
tags:
- testService
summary: testEndpoint testService
operationId: testService#testEndpoint
parameters:
- name: TestEndpointRequestBody
in: body
required: true
schema:
$ref: '#/definitions/TestServiceTestEndpointRequestBody'
responses:
"200":
description: OK response.
schema:
$ref: '#/definitions/TestServiceTestEndpointResponseBody'
schemes:
- http
definitions:
TestServiceTestEndpointRequestBody:
title: TestServiceTestEndpointRequestBody
type: object
properties:
any:
example: ""
any_array:
type: array
items:
example: ""
example:
- ""
- ""
- ""
- ""
any_map:
type: object
example:
"": ""
additionalProperties: true
example:
any: ""
any_array:
- ""
- ""
- ""
any_map:
"": ""
TestServiceTestEndpointResponseBody:
title: TestServiceTestEndpointResponseBody
type: object
properties:
any:
example: ""
any_array:
type: array
items:
example: ""
example:
- ""
- ""
- ""
- ""
any_map:
type: object
example:
"": ""
additionalProperties: true
example:
any: ""
any_array:
- ""
- ""
any_map:
"": ""
1 change: 1 addition & 0 deletions http/codegen/openapi/v3/files_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func TestFiles(t *testing.T) {
{"server-host-with-variables", testdata.ServerHostWithVariablesDSL},
{"with-spaces", testdata.WithSpacesDSL},
{"with-map", testdata.WithMapDSL},
{"with-any", testdata.WithAnyDSL},
{"path-with-wildcards", testdata.PathWithWildcardDSL},
{"path-with-multiple-wildcards", testdata.PathWithMultipleWildcardDSL},
{"path-with-multiple-explicit-wildcards", testdata.PathWithMultipleExplicitWildcardDSL},
Expand Down
1 change: 1 addition & 0 deletions http/codegen/openapi/v3/testdata/dsls/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func ObjectResponseBodyDSL(svcName, metName string) func() {
Result(func() {
Attribute("name")
Attribute("age", Int)
Attribute("misc", Any)
})
HTTP(func() {
POST("/")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"openapi":"3.0.3","info":{"title":"Goa API","version":"0.0.1"},"servers":[{"url":"http://localhost:80","description":"Default server for test api"}],"paths":{"/":{"post":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEndpointRequestBody"},"example":{"any":"","any_array":["","","",""],"any_map":{"":""}}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEndpointRequestBody"},"example":{"any":"","any_array":["","",""],"any_map":{"":""}}}}}}}}},"components":{"schemas":{"TestEndpointRequestBody":{"type":"object","properties":{"any":{"example":""},"any_array":{"type":"array","items":{"example":""},"example":["","","",""]},"any_map":{"type":"object","example":{"":""},"additionalProperties":true}},"example":{"any":"","any_array":["",""],"any_map":{"":""}}}}},"tags":[{"name":"testService"}]}
74 changes: 74 additions & 0 deletions http/codegen/openapi/v3/testdata/golden/with-any_file1.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
openapi: 3.0.3
info:
title: Goa API
version: 0.0.1
servers:
- url: http://localhost:80
description: Default server for test api
paths:
/:
post:
tags:
- testService
summary: testEndpoint testService
operationId: testService#testEndpoint
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TestEndpointRequestBody'
example:
any: ""
any_array:
- ""
- ""
- ""
- ""
any_map:
"": ""
responses:
"200":
description: OK response.
content:
application/json:
schema:
$ref: '#/components/schemas/TestEndpointRequestBody'
example:
any: ""
any_array:
- ""
- ""
- ""
any_map:
"": ""
components:
schemas:
TestEndpointRequestBody:
type: object
properties:
any:
example: ""
any_array:
type: array
items:
example: ""
example:
- ""
- ""
- ""
- ""
any_map:
type: object
example:
"": ""
additionalProperties: true
example:
any: ""
any_array:
- ""
- ""
any_map:
"": ""
tags:
- name: testService
6 changes: 5 additions & 1 deletion http/codegen/openapi/v3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ func (sf *schemafier) schemafy(attr *expr.AttributeExpr, noref ...bool) *openapi
case expr.Float64Kind:
s.Type = openapi.Type("number")
s.Format = "double"
case expr.BytesKind, expr.AnyKind:
case expr.BytesKind:
if bases := attr.Bases; len(bases) > 0 {
for _, b := range bases {
// Union type
Expand All @@ -177,6 +177,10 @@ func (sf *schemafier) schemafy(attr *expr.AttributeExpr, noref ...bool) *openapi
s.Type = openapi.Type("string")
s.Format = "binary"
}
case expr.AnyKind:
// A schema without a type matches any data type.
// See https://swagger.io/docs/specification/data-models/data-types/#any.
s.Type = openapi.Type("")
default:
s.Type = openapi.Type(t.Name())
}
Expand Down
4 changes: 2 additions & 2 deletions http/codegen/openapi/v3/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func TestBuildBodyTypes(t *testing.T) {
DSL: dsls.ObjectResponseBodyDSL(svcName, "object_response_body"),

ExpectedType: tempty,
ExpectedResponseTypes: rt{200: tobj("name", tstring, "age", tint)},
ExpectedResponseTypes: rt{200: tobj("name", tstring, "age", tint, "misc", tempty)},
}, {
Name: "multi_cookie_response_body",
DSL: dsls.MultiCookieResponseBodyDSL(svcName, "multi_cookie_response_body"),
Expand All @@ -137,7 +137,7 @@ func TestBuildBodyTypes(t *testing.T) {
DSL: dsls.ObjectResponseBodyDSL(svcName, "object_streaming_response_body"),

ExpectedType: tempty,
ExpectedResponseTypes: rt{200: tobj("name", tstring, "age", tint)},
ExpectedResponseTypes: rt{200: tobj("name", tstring, "age", tint, "misc", tempty)},
}, {
Name: "string_error_response",
DSL: dsls.StringErrorResponseBodyDSL(svcName, "string_error_response"),
Expand Down
34 changes: 34 additions & 0 deletions http/codegen/testdata/openapi_dsls.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,40 @@ var WithMapDSL = func() {
})
}

var WithAnyDSL = func() {
Service("testService", func() {
Method("testEndpoint", func() {
Payload(func() {
Attribute("any", Any, func() {
Example("")
})
Attribute("any_array", ArrayOf(Any, func() {
Example("")
}))
Attribute("any_map", MapOf(String, Any), func() {
Key(func() { Example("") })
Elem(func() { Example("") })
})
})
Result(func() {
Attribute("any", Any, func() {
Example("")
})
Attribute("any_array", ArrayOf(Any, func() {
Example("")
}))
Attribute("any_map", MapOf(String, Any), func() {
Key(func() { Example("") })
Elem(func() { Example("") })
})
})
HTTP(func() {
POST("/")
})
})
})
}

var PathWithWildcardDSL = func() {
Service("test service", func() {
Method("test endpoint", func() {
Expand Down

0 comments on commit c1a4639

Please sign in to comment.