Skip to content

Commit a1f2a91

Browse files
authored
Merge branch 'v3' into streaming-interceptors
2 parents 2ba73ae + 25c8744 commit a1f2a91

12 files changed

+308
-3
lines changed

dsl/meta.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,25 @@ const DefaultProtoc = expr.DefaultProtoc
272272
// Meta("openapi:extension:x-api", `{"foo":"bar"}`)
273273
// })
274274
//
275+
// - "openapi:additionalProperties" sets the OpenAPI additionalProperties field.
276+
// The value can be true or false. Defaults to true. Applicable to types (including
277+
// embedded Payload and Result definitions).
278+
//
279+
// var Foo = Type("Foo", func() {
280+
// Attribute("name", String)
281+
// Meta("openapi:additionalProperties", "false")
282+
// })
283+
//
284+
// Payload(Bar, func() {
285+
// Attribute("name", String)
286+
// Meta("openapi:additionalProperties", "false")
287+
// })
288+
//
289+
// Result(func() {
290+
// Attribute("name", String)
291+
// Meta("openapi:additionalProperties", "false")
292+
// })
293+
//
275294
// - "openapi:typename" overrides the name of the type generated in the OpenAPI specification.
276295
// Applicable to types (including embedded Payload and Result definitions).
277296
//

expr/http_body_types.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,16 @@ func httpRequestBody(a *HTTPEndpointExpr) *AttributeExpr {
192192
}
193193
appendSuffix(ut.Attribute().Type, suffix)
194194

195-
// Remember openapi typename for example to generate friendly OpenAPI specs.
196195
if t, ok := payload.Type.(UserType); ok {
196+
// Remember openapi typename for example to generate friendly OpenAPI specs.
197197
if m, ok := t.Attribute().Meta["openapi:typename"]; ok {
198198
ut.AttributeExpr.AddMeta("openapi:typename", m...)
199199
}
200+
201+
// Remember additionalProperties.
202+
if m, ok := t.Attribute().Meta["openapi:additionalProperties"]; ok {
203+
ut.AttributeExpr.AddMeta("openapi:additionalProperties", m...)
204+
}
200205
}
201206

202207
return &AttributeExpr{
@@ -334,13 +339,18 @@ func buildHTTPResponseBody(name string, attr *AttributeExpr, resp *HTTPResponseE
334339
UID: concat(svc.Name(), "#", name),
335340
}
336341

337-
// Remember original type name and openapi typename for example
338-
// to generate friendly OpenAPI specs.
339342
if t, ok := attr.Type.(UserType); ok {
343+
// Remember original type name and openapi typename for example
344+
// to generate friendly OpenAPI specs.
340345
userType.AttributeExpr.AddMeta("name:original", t.Name())
341346
if m, ok := t.Attribute().Meta["openapi:typename"]; ok {
342347
userType.AttributeExpr.AddMeta("openapi:typename", m...)
343348
}
349+
350+
// Remember additionalProperties.
351+
if m, ok := t.Attribute().Meta["openapi:additionalProperties"]; ok {
352+
userType.AttributeExpr.AddMeta("openapi:additionalProperties", m...)
353+
}
344354
}
345355

346356
appendSuffix(userType.Attribute().Type, suffix)

http/codegen/openapi/json_schema.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ func buildAttributeSchema(api *expr.APIExpr, s *Schema, at *expr.AttributeExpr)
486486
s.Description = at.Description
487487
s.Example = at.Example(api.ExampleGenerator)
488488
s.Extensions = ExtensionsFromExpr(at.Meta)
489+
if ap := AdditionalPropertiesFromExpr(at.Meta); ap != nil {
490+
s.AdditionalProperties = ap
491+
}
489492
initAttributeValidation(s, at)
490493

491494
return s
@@ -593,3 +596,12 @@ func MustGenerate(meta expr.MetaExpr) bool {
593596
}
594597
return true
595598
}
599+
600+
// AdditionalPropertiesFromExpr extracts the OpenAPI additionalProperties.
601+
func AdditionalPropertiesFromExpr(meta expr.MetaExpr) any {
602+
m, ok := meta.Last("openapi:additionalProperties")
603+
if ok && m == "false" {
604+
return false
605+
}
606+
return nil
607+
}

http/codegen/openapi/v2/files_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ func TestSections(t *testing.T) {
5353
{"json-prefix", testdata.JSONPrefixDSL},
5454
{"json-indent", testdata.JSONIndentDSL},
5555
{"json-prefix-indent", testdata.JSONPrefixIndentDSL},
56+
{"additional-properties-type", testdata.AdditionalPropertiesTypeDSL},
57+
{"additional-properties-payload-result", testdata.AdditionalPropertiesPayloadResultDSL},
58+
{"additional-properties-embedded-payload-result", testdata.AdditionalPropertiesPayloadResultDSL},
5659
}
5760
for _, c := range cases {
5861
t.Run(c.Name, func(t *testing.T) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"swagger":"2.0","info":{"title":"","version":"0.0.1"},"host":"goa.design","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","parameters":[{"name":"TestEndpointRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/Payload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/Result"}}},"schemes":["https"]}}},"definitions":{"Payload":{"title":"Payload","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false},"Result":{"title":"Result","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false}}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
swagger: "2.0"
2+
info:
3+
title: ""
4+
version: 0.0.1
5+
host: goa.design
6+
consumes:
7+
- application/json
8+
- application/xml
9+
- application/gob
10+
produces:
11+
- application/json
12+
- application/xml
13+
- application/gob
14+
paths:
15+
/:
16+
get:
17+
tags:
18+
- testService
19+
summary: testEndpoint testService
20+
operationId: testService#testEndpoint
21+
parameters:
22+
- name: TestEndpointRequestBody
23+
in: body
24+
required: true
25+
schema:
26+
$ref: '#/definitions/Payload'
27+
responses:
28+
"200":
29+
description: OK response.
30+
schema:
31+
$ref: '#/definitions/Result'
32+
schemes:
33+
- https
34+
definitions:
35+
Payload:
36+
title: Payload
37+
type: object
38+
properties:
39+
string:
40+
type: string
41+
example: ""
42+
example:
43+
string: ""
44+
additionalProperties: false
45+
Result:
46+
title: Result
47+
type: object
48+
properties:
49+
string:
50+
type: string
51+
example: ""
52+
example:
53+
string: ""
54+
additionalProperties: false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"swagger":"2.0","info":{"title":"","version":"0.0.1"},"host":"goa.design","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","parameters":[{"name":"TestEndpointRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/Payload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/Result"}}},"schemes":["https"]}}},"definitions":{"Payload":{"title":"Payload","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false},"Result":{"title":"Result","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false}}}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
swagger: "2.0"
2+
info:
3+
title: ""
4+
version: 0.0.1
5+
host: goa.design
6+
consumes:
7+
- application/json
8+
- application/xml
9+
- application/gob
10+
produces:
11+
- application/json
12+
- application/xml
13+
- application/gob
14+
paths:
15+
/:
16+
get:
17+
tags:
18+
- testService
19+
summary: testEndpoint testService
20+
operationId: testService#testEndpoint
21+
parameters:
22+
- name: TestEndpointRequestBody
23+
in: body
24+
required: true
25+
schema:
26+
$ref: '#/definitions/Payload'
27+
responses:
28+
"200":
29+
description: OK response.
30+
schema:
31+
$ref: '#/definitions/Result'
32+
schemes:
33+
- https
34+
definitions:
35+
Payload:
36+
title: Payload
37+
type: object
38+
properties:
39+
string:
40+
type: string
41+
example: ""
42+
example:
43+
string: ""
44+
additionalProperties: false
45+
Result:
46+
title: Result
47+
type: object
48+
properties:
49+
string:
50+
type: string
51+
example: ""
52+
example:
53+
string: ""
54+
additionalProperties: false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"swagger":"2.0","info":{"title":"","version":"0.0.1"},"host":"goa.design","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","parameters":[{"name":"TestEndpointRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/Payload"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/Result"}}},"schemes":["https"]}}},"definitions":{"Payload":{"title":"Payload","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false},"Result":{"title":"Result","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""},"additionalProperties":false}}}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
swagger: "2.0"
2+
info:
3+
title: ""
4+
version: 0.0.1
5+
host: goa.design
6+
consumes:
7+
- application/json
8+
- application/xml
9+
- application/gob
10+
produces:
11+
- application/json
12+
- application/xml
13+
- application/gob
14+
paths:
15+
/:
16+
get:
17+
tags:
18+
- testService
19+
summary: testEndpoint testService
20+
operationId: testService#testEndpoint
21+
parameters:
22+
- name: TestEndpointRequestBody
23+
in: body
24+
required: true
25+
schema:
26+
$ref: '#/definitions/Payload'
27+
responses:
28+
"200":
29+
description: OK response.
30+
schema:
31+
$ref: '#/definitions/Result'
32+
schemes:
33+
- https
34+
definitions:
35+
Payload:
36+
title: Payload
37+
type: object
38+
properties:
39+
string:
40+
type: string
41+
example: ""
42+
example:
43+
string: ""
44+
additionalProperties: false
45+
Result:
46+
title: Result
47+
type: object
48+
properties:
49+
string:
50+
type: string
51+
example: ""
52+
example:
53+
string: ""
54+
additionalProperties: false

http/codegen/openapi/v3/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ func (sf *schemafier) schemafy(attr *expr.AttributeExpr, noref ...bool) *openapi
266266
s.Extensions = openapi.ExtensionsFromExpr(attr.Meta)
267267

268268
// Validations
269+
if ap := openapi.AdditionalPropertiesFromExpr(attr.Meta); ap != nil {
270+
s.AdditionalProperties = ap
271+
}
269272
val := attr.Validation
270273
if val == nil {
271274
return s

http/codegen/testdata/openapi_dsls.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,3 +945,96 @@ var JSONPrefixIndentDSL = func() {
945945
})
946946
})
947947
}
948+
949+
var AdditionalPropertiesTypeDSL = func() {
950+
var PayloadT = Type("Payload", func() {
951+
Attribute("string", String, func() {
952+
Example("")
953+
})
954+
Meta("openapi:additionalProperties", "false")
955+
})
956+
var ResultT = Type("Result", func() {
957+
Attribute("string", String, func() {
958+
Example("")
959+
})
960+
Meta("openapi:additionalProperties", "false")
961+
})
962+
var _ = API("test", func() {
963+
Server("test", func() {
964+
Host("localhost", func() {
965+
URI("https://goa.design")
966+
})
967+
})
968+
})
969+
Service("testService", func() {
970+
Method("testEndpoint", func() {
971+
Payload(PayloadT)
972+
Result(ResultT)
973+
HTTP(func() {
974+
GET("/")
975+
})
976+
})
977+
})
978+
}
979+
980+
var AdditionalPropertiesPayloadResultDSL = func() {
981+
var PayloadT = Type("Payload", func() {
982+
Attribute("string", String, func() {
983+
Example("")
984+
})
985+
})
986+
var ResultT = Type("Result", func() {
987+
Attribute("string", String, func() {
988+
Example("")
989+
})
990+
})
991+
var _ = API("test", func() {
992+
Server("test", func() {
993+
Host("localhost", func() {
994+
URI("https://goa.design")
995+
})
996+
})
997+
})
998+
Service("testService", func() {
999+
Method("testEndpoint", func() {
1000+
Payload(PayloadT, func() {
1001+
Meta("openapi:additionalProperties", "false")
1002+
})
1003+
Result(ResultT, func() {
1004+
Meta("openapi:additionalProperties", "false")
1005+
})
1006+
HTTP(func() {
1007+
GET("/")
1008+
})
1009+
})
1010+
})
1011+
}
1012+
1013+
var AdditionalPropertiesEmbeddedPayloadResultDSL = func() {
1014+
var _ = API("test", func() {
1015+
Server("test", func() {
1016+
Host("localhost", func() {
1017+
URI("https://goa.design")
1018+
})
1019+
})
1020+
})
1021+
Service("testService", func() {
1022+
Method("testEndpoint", func() {
1023+
Payload(func() {
1024+
Attribute("string", String, func() {
1025+
Example("")
1026+
})
1027+
Meta("openapi:additionalProperties", "false")
1028+
})
1029+
Result(func() {
1030+
Attribute("string", String, func() {
1031+
Example("")
1032+
})
1033+
Meta("openapi:additionalProperties", "false")
1034+
})
1035+
HTTP(func() {
1036+
GET("/")
1037+
})
1038+
})
1039+
})
1040+
}

0 commit comments

Comments
 (0)