diff --git a/README.md b/README.md
index 8774db7a4..12138b91e 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
[](https://goreportcard.com/report/github.com/swaggo/swag)
[](https://codebeat.co/projects/github.aaakk.us.kg-swaggo-swag-master)
[](https://godoc.org/github.com/swaggo/swag)
-[](#backers)
+[](#backers)
[](#sponsors) [](https://app.fossa.io/projects/git%2Bgithub.com%2Fswaggo%2Fswag?ref=badge_shield)
[](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,21 +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 | // @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 |
-
-
-| 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 |
-
+| 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 |
+|---------------------------------|-------------------------------------------------------------------------|
+| 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 |
+| description | // @description OAuth protects our entity endpoints |
## Attribute
@@ -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/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 387151aba..980c1d7b2 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,35 +473,35 @@ 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)
if err != nil {
return err
}
- parser.swagger.SecurityDefinitions[value] = spec.APIKeyAuth(attrMap["@name"], attrMap["@in"])
+ 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] = secOAuth2Application(attrMap["@tokenurl"], scopes, extensions)
+ 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] = secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext)
+ 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] = secOAuth2Password(attrs["@tokenurl"], scopes, ext)
+ 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
}
- parser.swagger.SecurityDefinitions[value] = secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext)
+ parser.swagger.SecurityDefinitions[value] = tryAddDescription(secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext), ext)
case "@query.collection.format":
parser.collectionFormatInQuery = value
default:
@@ -549,6 +550,15 @@ func parseGeneralAPIInfo(parser *Parser, comments []string) error {
return nil
}
+func tryAddDescription(spec *spec.SecurityScheme, extensions map[string]interface{}) *spec.SecurityScheme {
+ if val, ok := extensions["@description"]; ok {
+ if str, ok := val.(string); ok {
+ spec.Description = str
+ }
+ }
+ return spec
+}
+
// 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")
@@ -572,16 +582,20 @@ 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 {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
-
continue
}
}
@@ -596,8 +610,14 @@ 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 == "@description" {
+ extensions[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
+ }
// 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 0799ea242..908d54a7f 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"
)
@@ -152,6 +154,7 @@ func TestParser_ParseGeneralApiInfo(t *testing.T) {
"paths": {},
"securityDefinitions": {
"ApiKeyAuth": {
+ "description": "some description",
"type": "apiKey",
"name": "Authorization",
"in": "header"
@@ -538,15 +541,32 @@ func TestParser_ParseGeneralAPISecurity(t *testing.T) {
err := parseGeneralAPIInfo(parser, []string{
"@securitydefinitions.apikey ApiKey",
"@in header",
- "@name X-API-KEY"})
+ "@name X-API-KEY",
+ "@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, "", " ")
expected := `{
"ApiKey": {
+ "description": "some",
"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))
@@ -3422,3 +3442,65 @@ 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 description",
+ 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: "",
+ },
+ },
+ },
+ {
+ 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) {
+ if got := tryAddDescription(tt.args.spec, tt.args.extensions); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("tryAddDescription() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
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"
+ }
}
}`
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