Skip to content

Commit

Permalink
Add openapi:generate Meta support to dsl.Attribute (#3437)
Browse files Browse the repository at this point in the history
* Add test cases about openapi:generate Meta for Attribute

* Add openapi:generate Meta support to dsl.Attribute

* Suppress example if openapi:generate Meta is false

* Fix hashAttribute() to consider openapi:generate Meta

---------

Co-authored-by: Raphael Simon <[email protected]>
  • Loading branch information
tchssk and raphael authored Jan 4, 2024
1 parent 4ce46ca commit 5663d03
Show file tree
Hide file tree
Showing 11 changed files with 195 additions and 3 deletions.
10 changes: 9 additions & 1 deletion expr/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ func (a *AttributeExpr) Example(r *ExampleGenerator) any {
return nil
}

value, ok := a.Meta.Last("openapi:example")
value, ok := a.Meta.Last("openapi:generate")
if !ok {
value, ok = a.Meta.Last("swagger:generate")
}
if ok && value == "false" {
return nil
}

value, ok = a.Meta.Last("openapi:example")
if !ok {
value, ok = a.Meta.Last("swagger:example")
}
Expand Down
16 changes: 16 additions & 0 deletions http/codegen/openapi/json_schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,9 @@ func TypeSchemaWithPrefix(api *expr.APIExpr, t expr.DataType, prefix string) *Sc
case *expr.Object:
s.Type = Object
for _, nat := range *actual {
if !mustGenerate(nat.Attribute.Meta) {
continue
}
prop := NewSchema()
buildAttributeSchema(api, prop, nat.Attribute)
s.Properties[nat.Name] = prop
Expand Down Expand Up @@ -565,3 +568,16 @@ func buildResultTypeSchema(api *expr.APIExpr, mt *expr.ResultTypeExpr, view stri
}
buildAttributeSchema(api, s, projected.AttributeExpr)
}

// mustGenerate returns true if the meta indicates that a OpenAPI specification should be
// generated, false otherwise.
func mustGenerate(meta expr.MetaExpr) bool {
m, ok := meta.Last("openapi:generate")
if !ok {
m, ok = meta.Last("swagger:generate")
}
if ok && m == "false" {
return false
}
return true
}
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 @@ -43,6 +43,7 @@ func TestSections(t *testing.T) {
{"typename", testdata.TypenameDSL},
{"not-generate-server", testdata.NotGenerateServerDSL},
{"not-generate-host", testdata.NotGenerateHostDSL},
{"not-generate-attribute", testdata.NotGenerateAttributeDSL},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +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":{"/":{"get":{"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":["https"]}}},"definitions":{"TestServiceTestEndpointRequestBody":{"title":"TestServiceTestEndpointRequestBody","type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""}},"TestServiceTestEndpointResponseBody":{"title":"TestServiceTestEndpointResponseBody","type":"object","properties":{"int":{"type":"integer","example":0,"format":"int64"}},"example":{"int":0}}}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
swagger: "2.0"
info:
title: ""
version: ""
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/TestServiceTestEndpointRequestBody'
responses:
"200":
description: OK response.
schema:
$ref: '#/definitions/TestServiceTestEndpointResponseBody'
schemes:
- https
definitions:
TestServiceTestEndpointRequestBody:
title: TestServiceTestEndpointRequestBody
type: object
properties:
string:
type: string
example: ""
example:
string: ""
TestServiceTestEndpointResponseBody:
title: TestServiceTestEndpointResponseBody
type: object
properties:
int:
type: integer
example: 0
format: int64
example:
int: 0
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 @@ -47,6 +47,7 @@ func TestFiles(t *testing.T) {
{"typename", testdata.TypenameDSL},
{"not-generate-server", testdata.NotGenerateServerDSL},
{"not-generate-host", testdata.NotGenerateHostDSL},
{"not-generate-attribute", testdata.NotGenerateAttributeDSL},
// TestEndpoints
{"endpoint", testdata.ExtensionDSL},
{"endpoint-swagger", testdata.ExtensionSwaggerDSL},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"openapi":"3.0.3","info":{"title":"Goa API","version":"1.0"},"servers":[{"url":"https://goa.design"}],"paths":{"/":{"get":{"tags":["testService"],"summary":"testEndpoint testService","operationId":"testService#testEndpoint","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestEndpointRequestBody"},"example":{"string":""}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"},"example":{"int":0}}}}}}}},"components":{"schemas":{"Result":{"type":"object","properties":{"int":{"type":"integer","example":0,"format":"int64"}},"example":{"int":0}},"TestEndpointRequestBody":{"type":"object","properties":{"string":{"type":"string","example":""}},"example":{"string":""}}}},"tags":[{"name":"testService"}]}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
openapi: 3.0.3
info:
title: Goa API
version: "1.0"
servers:
- url: https://goa.design
paths:
/:
get:
tags:
- testService
summary: testEndpoint testService
operationId: testService#testEndpoint
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TestEndpointRequestBody'
example:
string: ""
responses:
"200":
description: OK response.
content:
application/json:
schema:
$ref: '#/components/schemas/Result'
example:
int: 0
components:
schemas:
Result:
type: object
properties:
int:
type: integer
example: 0
format: int64
example:
int: 0
TestEndpointRequestBody:
type: object
properties:
string:
type: string
example: ""
example:
string: ""
tags:
- name: testService
6 changes: 6 additions & 0 deletions http/codegen/openapi/v3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ func (sf *schemafier) schemafy(attr *expr.AttributeExpr, noref ...bool) *openapi
s.Type = openapi.Object
var itemNotes []string
for _, nat := range *t {
if !mustGenerate(nat.Attribute.Meta) {
continue
}
s.Properties[nat.Name] = sf.schemafy(nat.Attribute)
}
if len(itemNotes) > 0 {
Expand Down Expand Up @@ -381,6 +384,9 @@ func hashAttribute(att *expr.AttributeExpr, h hash.Hash64, seen map[string]*uint
case expr.ObjectKind:
o := expr.AsObject(t)
for _, m := range *o {
if !mustGenerate(m.Attribute.Meta) {
continue
}
kh := hashString(m.Name, h)
vh := hashAttribute(m.Attribute, h, seen)
*res = *res ^ orderedHash(kh, *vh, h)
Expand Down
23 changes: 21 additions & 2 deletions http/codegen/openapi/v3/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ func TestHashAttribute(t *testing.T) {
h3 = uint64(7729867354446285276)
h4 = uint64(12938215553621425391)
h5 = uint64(590638987843676710)
h6 = uint64(2958992150570065940)
h7 = uint64(17427721879237743911)

metaNotGenerate = expr.MetaExpr{"openapi:generate": []string{"false"}}
metaEmpty = expr.MetaExpr{}
)
cases := []struct {
Name string
Expand All @@ -286,15 +291,17 @@ func TestHashAttribute(t *testing.T) {
{"map-str-str", &expr.AttributeExpr{Type: &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}}, 10408036596908747853},
{"map-int-str", &expr.AttributeExpr{Type: &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.Int}, ElemType: &expr.AttributeExpr{Type: expr.String}}}, 16377853221392883275},
{"map-int-int", &expr.AttributeExpr{Type: &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.Int}, ElemType: &expr.AttributeExpr{Type: expr.Int}}}, 3290208366554661977},
{"obj-str-req", newObj("foo", expr.String, true), 2958992150570065940},
{"obj-str-noreq", newObj("foo", expr.String, false), 17427721879237743911},
{"obj-str-req", newObj("foo", expr.String, true), h6},
{"obj-str-noreq", newObj("foo", expr.String, false), h7},
{"obj-int-req", newObj("foo", expr.Int, true), 8915021286725901502},
{"obj-int-noreq", newObj("foo", expr.Int, false), 11777831908257753485},
{"obj-other", newObj("bar", expr.Int, false), 12868551315046025641},
{"obj-str-str-noreq", newObj2("foo", "bar", expr.String, expr.String), h1},
{"obj-str-str-req1", newObj2("foo", "bar", expr.String, expr.String, "foo"), h2},
{"obj-str-str-req2", newObj2("foo", "bar", expr.String, expr.String, "bar"), h3},
{"obj-str-str-req3", newObj2("foo", "bar", expr.String, expr.String, "foo", "bar"), h4},
{"obj-str-str-notgen-req", newObj2Meta("foo", "bar", expr.String, expr.String, metaEmpty, metaNotGenerate, "foo"), h6},
{"obj-str-str-notgen-noreq", newObj2Meta("foo", "bar", expr.String, expr.String, metaEmpty, metaNotGenerate), h7},
{"obj-int-str-noreq", newObj2("foo", "bar", expr.Int, expr.String), 16228531529443692022},
{"obj1-str-str-noreq", newObj2("bar", "foo", expr.String, expr.String), h1},
{"obj1-str-str-req1", newObj2("bar", "foo", expr.String, expr.String, "foo"), h2},
Expand Down Expand Up @@ -339,6 +346,18 @@ func newObj2(n, o string, t, u expr.DataType, reqs ...string) *expr.AttributeExp
return attr
}

func newObj2Meta(n, o string, t, u expr.DataType, l, m expr.MetaExpr, reqs ...string) *expr.AttributeExpr {
attr := &expr.AttributeExpr{
Type: &expr.Object{
{Name: n, Attribute: &expr.AttributeExpr{Type: t, Meta: l}},
{Name: o, Attribute: &expr.AttributeExpr{Type: u, Meta: m}},
},
Validation: &expr.ValidationExpr{},
}
attr.Validation.Required = append(attr.Validation.Required, reqs...)
return attr
}

func newRT(id string, att *expr.AttributeExpr) *expr.AttributeExpr {
return &expr.AttributeExpr{
Type: &expr.ResultTypeExpr{
Expand Down
35 changes: 35 additions & 0 deletions http/codegen/testdata/openapi_dsls.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,38 @@ var NotGenerateHostDSL = func() {
})
})
}

var NotGenerateAttributeDSL = func() {
var _ = API("test", func() {
Server("test", func() {
Host("localhost", func() {
URI("https://goa.design")
})
})
})
var PayloadT = Type("Payload", func() {
Attribute("int", Int, func() {
Meta("openapi:generate", "false")
})
Attribute("string", String, func() {
Example("")
})
})
var ResultT = Type("Result", func() {
Attribute("int", Int, func() {
Example(0)
})
Attribute("string", String, func() {
Meta("openapi:generate", "false")
})
})
Service("testService", func() {
Method("testEndpoint", func() {
Payload(PayloadT)
Result(ResultT)
HTTP(func() {
GET("/")
})
})
})
}

0 comments on commit 5663d03

Please sign in to comment.