From d07b87b7d2a7c7ba069d633fb456466b44378a21 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 12 Apr 2022 20:04:19 +0200 Subject: [PATCH 01/11] added security definition description --- README.md | 30 +++++++++++++++--------------- parser.go | 35 ++++++++++++++++++++++++++++------- parser_test.go | 4 +++- swagger_test.go | 8 ++++++++ 4 files changed, 54 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 8774db7a4..860cfde9f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/swaggo/swag)](https://goreportcard.com/report/github.com/swaggo/swag) [![codebeat badge](https://codebeat.co/badges/71e2f5e5-9e6b-405d-baf9-7cc8b5037330)](https://codebeat.co/projects/github-com-swaggo-swag-master) [![Go Doc](https://godoc.org/github.com/swaggo/swagg?status.svg)](https://godoc.org/github.com/swaggo/swag) -[![Backers on Open Collective](https://opencollective.com/swag/backers/badge.svg)](#backers) +[![Backers on Open Collective](https://opencollective.com/swag/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/swag/sponsors/badge.svg)](#sponsors) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fswaggo%2Fswag.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fswaggo%2Fswag?ref=badge_shield) [![Release](https://img.shields.io/github/release/swaggo/swag.svg?style=flat-square)](https://github.com/swaggo/swag/releases) @@ -30,7 +30,7 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie - [Descriptions over multiple lines](#descriptions-over-multiple-lines) - [User defined structure with an array type](#user-defined-structure-with-an-array-type) - [Model composition in response](#model-composition-in-response) - - [Add a headers in response](#add-a-headers-in-response) + - [Add a headers in response](#add-a-headers-in-response) - [Use multiple path params](#use-multiple-path-params) - [Example value of struct](#example-value-of-struct) - [SchemaExample of body](#schemaexample-of-body) @@ -193,7 +193,7 @@ import ( "github.com/gin-gonic/gin" "github.com/swaggo/files" "github.com/swaggo/gin-swagger" - + "./docs" // docs is generated by Swag CLI, you have to import it. ) @@ -212,7 +212,7 @@ func main() { docs.SwaggerInfo.Host = "petstore.swagger.io" docs.SwaggerInfo.BasePath = "/v2" docs.SwaggerInfo.Schemes = []string{"http", "https"} - + r := gin.New() // use ginSwagger middleware to serve the API docs @@ -298,10 +298,10 @@ $ swag init ## The swag formatter -The Swag Comments can be automatically formatted, just like 'go fmt'. +The Swag Comments can be automatically formatted, just like 'go fmt'. Find the result of formatting [here](https://github.com/swaggo/swag/tree/master/example/celler). -Usage: +Usage: ```shell swag fmt ``` @@ -444,11 +444,11 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: | annotation | description | parameters | example | |------------|-------------|------------|---------| | securitydefinitions.basic | [Basic](https://swagger.io/docs/specification/2-0/authentication/basic-authentication/) auth. | | // @securityDefinitions.basic BasicAuth | -| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name | // @securityDefinitions.apikey ApiKeyAuth | -| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope | // @securitydefinitions.oauth2.application OAuth2Application | -| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope | // @securitydefinitions.oauth2.implicit OAuth2Implicit | -| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope | // @securitydefinitions.oauth2.password OAuth2Password | -| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | +| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, sec.def.description | // @securityDefinitions.apikey ApiKeyAuth | +| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, sec.def.description | // @securitydefinitions.oauth2.application OAuth2Application | +| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, sec.def.description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | +| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, sec.def.description | // @securitydefinitions.oauth2.password OAuth2Password | +| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, sec.def.description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | | parameters annotation | example | @@ -487,7 +487,7 @@ type Foo struct { Field Name | Type | Description ---|:---:|--- -validate | `string` | Determines the validation for the parameter. Possible values are: `required`. +validate | `string` | Determines the validation for the parameter. Possible values are: `required`. default | * | Declares the value of the parameter that the server will use if none is provided, for example a "count" to control the number of results per page might default to 100 if not supplied by the client in the request. (Note: "default" has no meaning for required parameters.) See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2. Unlike JSON Schema this value MUST conform to the defined [`type`](#parameterType) for this parameter. maximum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.2. minimum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.3. @@ -512,7 +512,7 @@ Field Name | Type | Description ### Descriptions over multiple lines -You can add descriptions spanning multiple lines in either the general api description or routes definitions like so: +You can add descriptions spanning multiple lines in either the general api description or routes definitions like so: ```go // @description This is the first line @@ -561,7 +561,7 @@ type Order struct { //in `proto` package @success 200 {object} jsonresult.JSONResult{data=[]string} "desc" ``` -- overriding multiple fields. field will be added if not exists +- overriding multiple fields. field will be added if not exists ```go @success 200 {object} jsonresult.JSONResult{data1=string,data2=[]string,data3=proto.Order,data4=[]proto.Order} "desc" ``` @@ -751,7 +751,7 @@ Rendered: "id": "integer" } ``` - + ### Use swaggerignore tag to exclude a field diff --git a/parser.go b/parser.go index 387151aba..749f8d32b 100644 --- a/parser.go +++ b/parser.go @@ -472,35 +472,45 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { case "@securitydefinitions.basic": parser.swagger.SecurityDefinitions[value] = spec.BasicAuth() case "@securitydefinitions.apikey": - attrMap, _, _, err := parseSecAttr(attribute, []string{"@in", "@name"}, comments[i+1:]) + attrMap, _, extensions, err := parseSecAttr(attribute, []string{"@in", "@name"}, comments[i+1:]) if err != nil { return err } - parser.swagger.SecurityDefinitions[value] = spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]) + secDef := spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]) + TryAddDescription(secDef, extensions) + parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.application": attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) if err != nil { return err } - parser.swagger.SecurityDefinitions[value] = secOAuth2Application(attrMap["@tokenurl"], scopes, extensions) + secDef := secOAuth2Application(attrMap["@tokenurl"], scopes, extensions) + TryAddDescription(secDef, extensions) + parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.implicit": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments[i+1:]) if err != nil { return err } - parser.swagger.SecurityDefinitions[value] = secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext) + secDef := secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext) + TryAddDescription(secDef, ext) + parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.password": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) if err != nil { return err } - parser.swagger.SecurityDefinitions[value] = secOAuth2Password(attrs["@tokenurl"], scopes, ext) + secDef := secOAuth2Password(attrs["@tokenurl"], scopes, ext) + TryAddDescription(secDef, ext) + parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.accesscode": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:]) if err != nil { return err } - parser.swagger.SecurityDefinitions[value] = secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext) + secDef := secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext) + TryAddDescription(secDef, ext) + parser.swagger.SecurityDefinitions[value] = secDef case "@query.collection.format": parser.collectionFormatInQuery = value default: @@ -549,6 +559,14 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return nil } +func TryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) { + if val, ok := extensions["@sec.def.description"]; ok { + if str, ok := val.(string); ok { + spec.Description = str + } + } +} + // ParseAcceptComment parses comment for given `accept` comment string. func (parser *Parser) ParseAcceptComment(commentLine string) error { return parseMimeTypeList(commentLine, &parser.swagger.Consumes, "%v accept type can't be accepted") @@ -581,7 +599,6 @@ func parseSecAttr(context string, search []string, lines []string) (map[string]s for _, findterm := range search { if securityAttr == findterm { attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):]) - continue } } @@ -596,6 +613,10 @@ func parseSecAttr(context string, search []string, lines []string) (map[string]s // Add the custom attribute without the @ extensions[securityAttr[1:]] = strings.TrimSpace(v[len(securityAttr):]) } + // Not mandatory field + if securityAttr == "@sec.def.description" { + extensions[securityAttr] = strings.TrimSpace(v[len(securityAttr):]) + } // next securityDefinitions if strings.Index(securityAttr, "@securitydefinitions.") == 0 { break diff --git a/parser_test.go b/parser_test.go index 0799ea242..667819052 100644 --- a/parser_test.go +++ b/parser_test.go @@ -538,12 +538,14 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { err := parseGeneralAPIInfo(parser, []string{ "@securitydefinitions.apikey ApiKey", "@in header", - "@name X-API-KEY"}) + "@name X-API-KEY", + "@sec.def.description some"}) assert.NoError(t, err) b, _ := json.MarshalIndent(parser.GetSwagger().SecurityDefinitions, "", " ") expected := `{ "ApiKey": { + "description": "some", "type": "apiKey", "name": "X-API-KEY", "in": "header" diff --git a/swagger_test.go b/swagger_test.go index 6483ef8ad..3d15d0b59 100644 --- a/swagger_test.go +++ b/swagger_test.go @@ -146,6 +146,14 @@ var doc = `{ } } } + }, + "securityDefinitions": { + "ApiKey": { + "description: "some", + "type": "apiKey", + "name": "X-API-KEY", + "in": "header" + } } }` From 607ae632f1088095941f2a649ea9aa8206d6de77 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 12 Apr 2022 20:09:02 +0200 Subject: [PATCH 02/11] unexported functions --- .683642449 | 2 ++ parser.go | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .683642449 diff --git a/.683642449 b/.683642449 new file mode 100644 index 000000000..f8ab67337 --- /dev/null +++ b/.683642449 @@ -0,0 +1,2 @@ +package main +import () \ No newline at end of file diff --git a/parser.go b/parser.go index 749f8d32b..c1498665a 100644 --- a/parser.go +++ b/parser.go @@ -477,7 +477,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return err } secDef := spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]) - TryAddDescription(secDef, extensions) + tryAddDescription(secDef, extensions) parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.application": attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) @@ -485,7 +485,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return err } secDef := secOAuth2Application(attrMap["@tokenurl"], scopes, extensions) - TryAddDescription(secDef, extensions) + tryAddDescription(secDef, extensions) parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.implicit": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments[i+1:]) @@ -493,7 +493,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return err } secDef := secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext) - TryAddDescription(secDef, ext) + tryAddDescription(secDef, ext) parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.password": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) @@ -501,7 +501,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return err } secDef := secOAuth2Password(attrs["@tokenurl"], scopes, ext) - TryAddDescription(secDef, ext) + tryAddDescription(secDef, ext) parser.swagger.SecurityDefinitions[value] = secDef case "@securitydefinitions.oauth2.accesscode": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:]) @@ -509,7 +509,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return err } secDef := secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext) - TryAddDescription(secDef, ext) + tryAddDescription(secDef, ext) parser.swagger.SecurityDefinitions[value] = secDef case "@query.collection.format": parser.collectionFormatInQuery = value @@ -559,7 +559,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return nil } -func TryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) { +func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) { if val, ok := extensions["@sec.def.description"]; ok { if str, ok := val.(string); ok { spec.Description = str From e8fe7c19ffbe9e5910cd741ebd72889fea949ea3 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 12 Apr 2022 20:18:17 +0200 Subject: [PATCH 03/11] removed test generated file --- .683642449 | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .683642449 diff --git a/.683642449 b/.683642449 deleted file mode 100644 index f8ab67337..000000000 --- a/.683642449 +++ /dev/null @@ -1,2 +0,0 @@ -package main -import () \ No newline at end of file From e7c7e2f6d2c64fe6f6d737984808792617ad5e22 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 08:31:37 +0200 Subject: [PATCH 04/11] code format and readme --- .860682986 | 2 ++ README.md | 17 ++++++++--------- parser.go | 23 +++++++---------------- 3 files changed, 17 insertions(+), 25 deletions(-) create mode 100644 .860682986 diff --git a/.860682986 b/.860682986 new file mode 100644 index 000000000..f8ab67337 --- /dev/null +++ b/.860682986 @@ -0,0 +1,2 @@ +package main +import () \ No newline at end of file diff --git a/README.md b/README.md index 860cfde9f..54d91d64a 100644 --- a/README.md +++ b/README.md @@ -450,15 +450,14 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: | securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, sec.def.description | // @securitydefinitions.oauth2.password OAuth2Password | | securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, sec.def.description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | - -| parameters annotation | example | -|-----------------------|----------------------------------------------------------| -| in | // @in header | -| name | // @name Authorization | -| tokenUrl | // @tokenUrl https://example.com/oauth/token | -| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | -| scope.hoge | // @scope.write Grants write access | - +| parameters annotation | example | +|-----------------------|-------------------------------------------------------------| +| in | // @in header | +| name | // @name Authorization | +| tokenUrl | // @tokenUrl https://example.com/oauth/token | +| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | +| scope.hoge | // @scope.write Grants write access | +| sec.def.description | // @sec.def.description OAuth protects our entity endpoints | ## Attribute diff --git a/parser.go b/parser.go index c1498665a..e0381d11a 100644 --- a/parser.go +++ b/parser.go @@ -476,41 +476,31 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { if err != nil { return err } - secDef := spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]) - tryAddDescription(secDef, extensions) - parser.swagger.SecurityDefinitions[value] = secDef + parser.swagger.SecurityDefinitions[value] = tryAddDescription(spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]), extensions) case "@securitydefinitions.oauth2.application": attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) if err != nil { return err } - secDef := secOAuth2Application(attrMap["@tokenurl"], scopes, extensions) - tryAddDescription(secDef, extensions) - parser.swagger.SecurityDefinitions[value] = secDef + parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Application(attrMap["@tokenurl"], scopes, extensions), extensions) case "@securitydefinitions.oauth2.implicit": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments[i+1:]) if err != nil { return err } - secDef := secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext) - tryAddDescription(secDef, ext) - parser.swagger.SecurityDefinitions[value] = secDef + parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext), ext) case "@securitydefinitions.oauth2.password": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) if err != nil { return err } - secDef := secOAuth2Password(attrs["@tokenurl"], scopes, ext) - tryAddDescription(secDef, ext) - parser.swagger.SecurityDefinitions[value] = secDef + parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Password(attrs["@tokenurl"], scopes, ext), ext) case "@securitydefinitions.oauth2.accesscode": attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:]) if err != nil { return err } - secDef := secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext) - tryAddDescription(secDef, ext) - parser.swagger.SecurityDefinitions[value] = secDef + parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext), ext) case "@query.collection.format": parser.collectionFormatInQuery = value default: @@ -559,12 +549,13 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { return nil } -func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) { +func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) *spec.SecurityScheme { if val, ok := extensions["@sec.def.description"]; ok { if str, ok := val.(string); ok { spec.Description = str } } + return spec } // ParseAcceptComment parses comment for given `accept` comment string. From f73b6092b24aeb04467c5e850f4d71287fa50f48 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 08:34:08 +0200 Subject: [PATCH 05/11] removed test generated file --- .860682986 | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .860682986 diff --git a/.860682986 b/.860682986 deleted file mode 100644 index f8ab67337..000000000 --- a/.860682986 +++ /dev/null @@ -1,2 +0,0 @@ -package main -import () \ No newline at end of file From 2a6b9a3e271cbcf85a23ec6cf1537ef459d9a0df Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 12:48:28 +0200 Subject: [PATCH 06/11] renamed sec.def to security.definition --- README.md | 29 +++++++++++++++-------------- parser.go | 4 ++-- parser_test.go | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 54d91d64a..bf8054146 100644 --- a/README.md +++ b/README.md @@ -444,20 +444,21 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: | annotation | description | parameters | example | |------------|-------------|------------|---------| | securitydefinitions.basic | [Basic](https://swagger.io/docs/specification/2-0/authentication/basic-authentication/) auth. | | // @securityDefinitions.basic BasicAuth | -| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, sec.def.description | // @securityDefinitions.apikey ApiKeyAuth | -| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, sec.def.description | // @securitydefinitions.oauth2.application OAuth2Application | -| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, sec.def.description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | -| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, sec.def.description | // @securitydefinitions.oauth2.password OAuth2Password | -| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, sec.def.description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | - -| parameters annotation | example | -|-----------------------|-------------------------------------------------------------| -| in | // @in header | -| name | // @name Authorization | -| tokenUrl | // @tokenUrl https://example.com/oauth/token | -| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | -| scope.hoge | // @scope.write Grants write access | -| sec.def.description | // @sec.def.description OAuth protects our entity endpoints | +| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, security.definition.description | // @securityDefinitions.apikey ApiKeyAuth | +| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, security.definition.description | // @securitydefinitions.oauth2.application OAuth2Application | +| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, security.definition.description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | +| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, security.definition.description | // @securitydefinitions.oauth2.password OAuth2Password | +| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, security.definition.description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | + + +| parameters annotation | example | +|---------------------------------|-------------------------------------------------------------------------| +| in | // @in header | +| name | // @name Authorization | +| tokenUrl | // @tokenUrl https://example.com/oauth/token | +| authorizationurl | // @authorizationurl https://example.com/oauth/authorize | +| scope.hoge | // @scope.write Grants write access | +| security.definition.description | // @security.definition.description OAuth protects our entity endpoints | ## Attribute diff --git a/parser.go b/parser.go index e0381d11a..bb3c451c2 100644 --- a/parser.go +++ b/parser.go @@ -550,7 +550,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { } func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) *spec.SecurityScheme { - if val, ok := extensions["@sec.def.description"]; ok { + if val, ok := extensions["@security.definition.description"]; ok { if str, ok := val.(string); ok { spec.Description = str } @@ -605,7 +605,7 @@ func parseSecAttr(context string, search []string, lines []string) (map[string]s extensions[securityAttr[1:]] = strings.TrimSpace(v[len(securityAttr):]) } // Not mandatory field - if securityAttr == "@sec.def.description" { + if securityAttr == "@security.definition.description" { extensions[securityAttr] = strings.TrimSpace(v[len(securityAttr):]) } // next securityDefinitions diff --git a/parser_test.go b/parser_test.go index 667819052..e2f99b374 100644 --- a/parser_test.go +++ b/parser_test.go @@ -539,7 +539,7 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { "@securitydefinitions.apikey ApiKey", "@in header", "@name X-API-KEY", - "@sec.def.description some"}) + "@security.definition.description some"}) assert.NoError(t, err) b, _ := json.MarshalIndent(parser.GetSwagger().SecurityDefinitions, "", " ") From cad312737883099d9540ca542be5d384284881cb Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 13:36:37 +0200 Subject: [PATCH 07/11] support for description on security definition --- README.md | 12 ++++++------ example/celler/main.go | 1 + parser.go | 26 ++++++++++++++++---------- parser_test.go | 3 ++- testdata/main.go | 1 + 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index bf8054146..12138b91e 100644 --- a/README.md +++ b/README.md @@ -444,11 +444,11 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: | annotation | description | parameters | example | |------------|-------------|------------|---------| | securitydefinitions.basic | [Basic](https://swagger.io/docs/specification/2-0/authentication/basic-authentication/) auth. | | // @securityDefinitions.basic BasicAuth | -| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, security.definition.description | // @securityDefinitions.apikey ApiKeyAuth | -| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, security.definition.description | // @securitydefinitions.oauth2.application OAuth2Application | -| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, security.definition.description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | -| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, security.definition.description | // @securitydefinitions.oauth2.password OAuth2Password | -| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, security.definition.description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | +| securitydefinitions.apikey | [API key](https://swagger.io/docs/specification/2-0/authentication/api-keys/) auth. | in, name, description | // @securityDefinitions.apikey ApiKeyAuth | +| securitydefinitions.oauth2.application | [OAuth2 application](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.application OAuth2Application | +| securitydefinitions.oauth2.implicit | [OAuth2 implicit](https://swagger.io/docs/specification/authentication/oauth2/) auth. | authorizationUrl, scope, description | // @securitydefinitions.oauth2.implicit OAuth2Implicit | +| securitydefinitions.oauth2.password | [OAuth2 password](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, scope, description | // @securitydefinitions.oauth2.password OAuth2Password | +| securitydefinitions.oauth2.accessCode | [OAuth2 access code](https://swagger.io/docs/specification/authentication/oauth2/) auth. | tokenUrl, authorizationUrl, scope, description | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode | | parameters annotation | example | @@ -458,7 +458,7 @@ Besides that, `swag` also accepts aliases for some MIME Types as follows: | tokenUrl | // @tokenUrl https://example.com/oauth/token | | authorizationurl | // @authorizationurl https://example.com/oauth/authorize | | scope.hoge | // @scope.write Grants write access | -| security.definition.description | // @security.definition.description OAuth protects our entity endpoints | +| description | // @description OAuth protects our entity endpoints | ## Attribute diff --git a/example/celler/main.go b/example/celler/main.go index c94ebc475..87d4742a4 100644 --- a/example/celler/main.go +++ b/example/celler/main.go @@ -33,6 +33,7 @@ import ( // @securityDefinitions.apikey ApiKeyAuth // @in header // @name Authorization +// @description Description for what is this security definition being used // @securitydefinitions.oauth2.application OAuth2Application // @tokenUrl https://example.com/oauth/token diff --git a/parser.go b/parser.go index bb3c451c2..cf59ee065 100644 --- a/parser.go +++ b/parser.go @@ -383,7 +383,8 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { previousAttribute := "" // parsing classic meta data model - for i, commentLine := range comments { + for i := 0; i < len(comments); i++ { + commentLine := comments[i] attribute := strings.Split(commentLine, " ")[0] value := strings.TrimSpace(commentLine[len(attribute):]) multilineBlock := false @@ -472,31 +473,31 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { case "@securitydefinitions.basic": parser.swagger.SecurityDefinitions[value] = spec.BasicAuth() case "@securitydefinitions.apikey": - attrMap, _, extensions, err := parseSecAttr(attribute, []string{"@in", "@name"}, comments[i+1:]) + attrMap, _, extensions, err := parseSecAttr(attribute, []string{"@in", "@name"}, comments, &i) if err != nil { return err } parser.swagger.SecurityDefinitions[value] = tryAddDescription(spec.APIKeyAuth(attrMap["@name"], attrMap["@in"]), extensions) case "@securitydefinitions.oauth2.application": - attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) + attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments, &i) if err != nil { return err } parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Application(attrMap["@tokenurl"], scopes, extensions), extensions) case "@securitydefinitions.oauth2.implicit": - attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments[i+1:]) + attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments, &i) if err != nil { return err } parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext), ext) case "@securitydefinitions.oauth2.password": - attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:]) + attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments, &i) if err != nil { return err } parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2Password(attrs["@tokenurl"], scopes, ext), ext) case "@securitydefinitions.oauth2.accesscode": - attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:]) + attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments, &i) if err != nil { return err } @@ -550,7 +551,7 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error { } func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) *spec.SecurityScheme { - if val, ok := extensions["@security.definition.description"]; ok { + if val, ok := extensions["@description"]; ok { if str, ok := val.(string); ok { spec.Description = str } @@ -581,11 +582,16 @@ func isGeneralAPIComment(comments []string) bool { return true } -func parseSecAttr(context string, search []string, lines []string) (map[string]string, map[string]string, map[string]interface{}, error) { +func parseSecAttr(context string, search []string, lines []string, index *int) (map[string]string, map[string]string, map[string]interface{}, error) { attrMap := map[string]string{} scopes := map[string]string{} extensions := map[string]interface{}{} - for _, v := range lines { + + // For the first line we get the attributes in the context parameter, so we skip to the next one + *index++ + + for ; *index < len(lines); *index++ { + v := lines[*index] securityAttr := strings.ToLower(strings.Split(v, " ")[0]) for _, findterm := range search { if securityAttr == findterm { @@ -605,7 +611,7 @@ func parseSecAttr(context string, search []string, lines []string) (map[string]s extensions[securityAttr[1:]] = strings.TrimSpace(v[len(securityAttr):]) } // Not mandatory field - if securityAttr == "@security.definition.description" { + if securityAttr == "@description" { extensions[securityAttr] = strings.TrimSpace(v[len(securityAttr):]) } // next securityDefinitions diff --git a/parser_test.go b/parser_test.go index e2f99b374..fa06c1585 100644 --- a/parser_test.go +++ b/parser_test.go @@ -152,6 +152,7 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) { "paths": {}, "securityDefinitions": { "ApiKeyAuth": { + "description": "some description", "type": "apiKey", "name": "Authorization", "in": "header" @@ -539,7 +540,7 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { "@securitydefinitions.apikey ApiKey", "@in header", "@name X-API-KEY", - "@security.definition.description some"}) + "@description some"}) assert.NoError(t, err) b, _ := json.MarshalIndent(parser.GetSwagger().SecurityDefinitions, "", " ") diff --git a/testdata/main.go b/testdata/main.go index 2cadfe4fb..d70a642e8 100644 --- a/testdata/main.go +++ b/testdata/main.go @@ -21,6 +21,7 @@ package main // @securityDefinitions.apikey ApiKeyAuth // @in header // @name Authorization +// @description some description // @securitydefinitions.oauth2.application OAuth2Application // @tokenUrl https://example.com/oauth/token From ee9d582d4096e61e9b947d9f0e09edfd8c5b0ad2 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 13:47:32 +0200 Subject: [PATCH 08/11] added tryAddDescription tests --- parser_test.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/parser_test.go b/parser_test.go index fa06c1585..5717be0d7 100644 --- a/parser_test.go +++ b/parser_test.go @@ -10,8 +10,10 @@ import ( "log" "os" "path/filepath" + "reflect" "testing" + "github.com/go-openapi/spec" "github.com/stretchr/testify/assert" ) @@ -3425,3 +3427,51 @@ func TestGetFieldType(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "models.User", field) } + +func TestTryAddDescription(t *testing.T) { + type args struct { + spec *spec.SecurityScheme + extensions map[string]interface{} + } + tests := []struct { + name string + args args + want *spec.SecurityScheme + }{ + { + name: "added dscription", + args: args{ + spec: &spec.SecurityScheme{}, + extensions: map[string]interface{}{ + "@description": "some description", + }, + }, + want: &spec.SecurityScheme{ + SecuritySchemeProps: spec.SecuritySchemeProps{ + Description: "some description", + }, + }, + }, + { + name: "no description", + args: args{ + spec: &spec.SecurityScheme{}, + extensions: map[string]interface{}{ + "@not-description": "some description", + }, + }, + want: &spec.SecurityScheme{ + SecuritySchemeProps: spec.SecuritySchemeProps{ + Description: "", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tryAddDescription(tt.args.spec, tt.args.extensions); !reflect.DeepEqual(got, tt.want) { + t.Errorf("tryAddDescription() = %v, want %v", got, tt.want) + } + }) + } +} From c1a4f7d19e9e923b02677e079917d711cf54e652 Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 13:48:16 +0200 Subject: [PATCH 09/11] fixed typo --- parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser_test.go b/parser_test.go index 5717be0d7..296423999 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3439,7 +3439,7 @@ func TestTryAddDescription(t *testing.T) { want *spec.SecurityScheme }{ { - name: "added dscription", + name: "added description", args: args{ spec: &spec.SecurityScheme{}, extensions: map[string]interface{}{ From 36d1424dc0be9cfccaf060eb2cadde5953acba7a Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 14:11:16 +0200 Subject: [PATCH 10/11] added another test case scenario --- parser_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/parser_test.go b/parser_test.go index 296423999..8d5a8b762 100644 --- a/parser_test.go +++ b/parser_test.go @@ -3466,6 +3466,20 @@ func TestTryAddDescription(t *testing.T) { }, }, }, + { + name: "description has invalid format", + args: args{ + spec: &spec.SecurityScheme{}, + extensions: map[string]interface{}{ + "@description": 12345, + }, + }, + want: &spec.SecurityScheme{ + SecuritySchemeProps: spec.SecuritySchemeProps{ + Description: "", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From a1174951dc0bf5e1a16d5ab649da2b1e23822c1a Mon Sep 17 00:00:00 2001 From: Pablo Morelli Date: Tue, 19 Apr 2022 14:50:37 +0200 Subject: [PATCH 11/11] added test for multiple security definitions --- parser.go | 2 ++ parser_test.go | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/parser.go b/parser.go index cf59ee065..980c1d7b2 100644 --- a/parser.go +++ b/parser.go @@ -616,6 +616,8 @@ func parseSecAttr(context string, search []string, lines []string, index *int) ( } // next securityDefinitions if strings.Index(securityAttr, "@securitydefinitions.") == 0 { + // Go back to the previous line and break + *index-- break } } diff --git a/parser_test.go b/parser_test.go index 8d5a8b762..908d54a7f 100644 --- a/parser_test.go +++ b/parser_test.go @@ -542,7 +542,13 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { "@securitydefinitions.apikey ApiKey", "@in header", "@name X-API-KEY", - "@description some"}) + "@description some", + "", + "@securitydefinitions.oauth2.accessCode OAuth2AccessCode", + "@tokenUrl https://example.com/oauth/token", + "@authorizationUrl https://example.com/oauth/authorize", + "@scope.admin foo", + }) assert.NoError(t, err) b, _ := json.MarshalIndent(parser.GetSwagger().SecurityDefinitions, "", " ") @@ -552,6 +558,15 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) { "type": "apiKey", "name": "X-API-KEY", "in": "header" + }, + "OAuth2AccessCode": { + "type": "oauth2", + "flow": "accessCode", + "authorizationUrl": "https://example.com/oauth/authorize", + "tokenUrl": "https://example.com/oauth/token", + "scopes": { + "admin": " foo" + } } }` assert.Equal(t, expected, string(b))