Compare commits

...

8 Commits

Author SHA1 Message Date
Bogdan Ungureanu
fe69d20d50 chore: update README.md 2022-04-22 16:48:51 +03:00
Bogdan Ungureanu
ffac37ae4f chore: add multiple api example 2022-04-22 16:38:39 +03:00
Bogdan U
bd7f2153bf chore: linting (#206) 2022-04-22 14:41:42 +03:00
Bogdan U
f844160fb7 chore: security improvement (#203) 2022-04-16 10:34:09 +03:00
Nishith Savla
2ae0634528 fix: typo in word DocExpansion in Readme (#198) 2022-02-22 14:04:11 +02:00
Jakub Mikłasz
cb6be4cbcf feat: add support for swagger-ui persist-authorization (#195) 2022-02-06 13:05:35 +02:00
Bogdan U
0bb2c39427 chore: preserve compatibility with swagger (#194) 2022-02-06 11:18:16 +02:00
Bogdan U
77495f6d48 chore: drop go1.14 support (#193)
* chore: drop go1.14 support
* chore: update go.mod
2022-02-06 11:07:28 +02:00
24 changed files with 1006 additions and 185 deletions

View File

@@ -10,7 +10,7 @@ jobs:
test:
strategy:
matrix:
go: [ '1.14.x', '1.15.x', '1.16.x', '1.17.x' ]
go: [ '1.15.x', '1.16.x', '1.17.x', '1.18.x' ]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master

View File

@@ -143,6 +143,9 @@ Demo project tree, `swag init` is run at relative `.`
└── main.go
```
## Multiple APIs
This feature where introduced in swag v1.7.9
## Configuration
You can configure Swagger using different configuration options
@@ -162,7 +165,8 @@ func main() {
| Option | Type | Default | Description |
| ------------------------ | ------ | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| URL | string | "doc.json" | URL pointing to API definition |
| DocExpantion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). |
| DocExpansion | string | "list" | Controls the default expansion setting for the operations and tags. It can be 'list' (expands only the tags), 'full' (expands the tags and operations) or 'none' (expands nothing). |
| DeepLinking | bool | true | If set to true, enables deep linking for tags and operations. See the Deep Linking documentation for more information. |
| DefaultModelsExpandDepth | int | 1 | Default expansion depth for models (set to -1 completely hide the models). |
| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_). |
| InstanceName | string | "swagger" | The instance name of the swagger document. If multiple different swagger instances should be deployed on one gin router, ensure that each instance has a unique name (use the _--instanceName_ parameter to generate swagger documents with _swag init_).
| PersistAuthotization | bool | false | If set to true, it persists authorization data and it would not be lost on browser close/refresh. |

View File

@@ -2,16 +2,9 @@
// This file was generated by swaggo/swag
package docs
import (
"bytes"
"encoding/json"
"strings"
"text/template"
import "github.com/swaggo/swag"
"github.com/swaggo/swag"
)
var doc = `{
const docTemplate_swagger = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
@@ -143,56 +136,18 @@ var doc = `{
}
}`
type swaggerInfo struct {
Version string
Host string
BasePath string
Schemes []string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{
Version: "1.0",
Host: "petstore.swagger.io:8080",
BasePath: "/v2",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample server Petstore server.",
}
type s struct{}
func (s *s) ReadDoc() string {
sInfo := SwaggerInfo
sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
t, err := template.New("swagger_info").Funcs(template.FuncMap{
"marshal": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
"escape": func(v interface{}) string {
// escape tabs
str := strings.Replace(v.(string), "\t", "\\t", -1)
// replace " with \", and if that results in \\", replace that with \\\"
str = strings.Replace(str, "\"", "\\\"", -1)
return strings.Replace(str, "\\\\\"", "\\\\\\\"", -1)
},
}).Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, sInfo); err != nil {
return doc
}
return tpl.String()
// SwaggerInfo_swagger holds exported Swagger Info so clients can modify it
var SwaggerInfo_swagger = &swag.Spec{
Version: "1.0",
Host: "petstore.swagger.io:8080",
BasePath: "/v2",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample server Petstore server.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate_swagger,
}
func init() {
swag.Register(swag.Name, &s{})
swag.Register(SwaggerInfo_swagger.InstanceName(), SwaggerInfo_swagger)
}

View File

@@ -0,0 +1,130 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "petstore.swagger.io:8080",
"basePath": "/v2",
"paths": {
"/testapi/get-string-by-int/{some_id}": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Add a new pet to the store",
"parameters": [
{
"type": "integer",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
},
"/testapi/get-struct-array-by-string/{some_id}": {
"get": {
"description": "get struct array by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"parameters": [
{
"type": "string",
"description": "Some ID",
"name": "some_id",
"in": "path",
"required": true
},
{
"type": "integer",
"description": "Offset",
"name": "offset",
"in": "query",
"required": true
},
{
"type": "integer",
"description": "Offset",
"name": "limit",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "string"
}
},
"400": {
"description": "We need ID!!",
"schema": {
"$ref": "#/definitions/web.APIError"
}
},
"404": {
"description": "Can not find ID",
"schema": {
"$ref": "#/definitions/web.APIError"
}
}
}
}
}
},
"definitions": {
"web.APIError": {
"type": "object",
"properties": {
"errorCode": {
"type": "integer"
},
"errorMessage": {
"type": "string"
}
}
}
}
}

View File

@@ -0,0 +1,87 @@
basePath: /v2
definitions:
web.APIError:
properties:
errorCode:
type: integer
errorMessage:
type: string
type: object
host: petstore.swagger.io:8080
info:
contact:
email: support@swagger.io
name: API Support
url: http://www.swagger.io/support
description: This is a sample server Petstore server.
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
termsOfService: http://swagger.io/terms/
title: Swagger Example API
version: "1.0"
paths:
/testapi/get-string-by-int/{some_id}:
get:
consumes:
- application/json
description: get string by ID
parameters:
- description: Some ID
in: path
name: some_id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: ok
schema:
type: string
"400":
description: We need ID!!
schema:
$ref: '#/definitions/web.APIError'
"404":
description: Can not find ID
schema:
$ref: '#/definitions/web.APIError'
summary: Add a new pet to the store
/testapi/get-struct-array-by-string/{some_id}:
get:
consumes:
- application/json
description: get struct array by ID
parameters:
- description: Some ID
in: path
name: some_id
required: true
type: string
- description: Offset
in: query
name: offset
required: true
type: integer
- description: Offset
in: query
name: limit
required: true
type: integer
produces:
- application/json
responses:
"200":
description: ok
schema:
type: string
"400":
description: We need ID!!
schema:
$ref: '#/definitions/web.APIError'
"404":
description: Can not find ID
schema:
$ref: '#/definitions/web.APIError'
swagger: "2.0"

View File

@@ -0,0 +1,44 @@
// Package docs GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import "github.com/swaggo/swag"
const docTemplate_swagger = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {}
}`
// SwaggerInfo_swagger holds exported Swagger Info so clients can modify it
var SwaggerInfo_swagger = &swag.Spec{
Version: "1.0",
Host: "petstore.swagger.io",
BasePath: "/v2",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample server Petstore server.",
InfoInstanceName: "swagger",
SwaggerTemplate: docTemplate_swagger,
}
func init() {
swag.Register(SwaggerInfo_swagger.InstanceName(), SwaggerInfo_swagger)
}

View File

@@ -0,0 +1,21 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"host": "petstore.swagger.io",
"basePath": "/v2",
"paths": {}
}

View File

@@ -0,0 +1,16 @@
basePath: /v2
host: petstore.swagger.io
info:
contact:
email: support@swagger.io
name: API Support
url: http://www.swagger.io/support
description: This is a sample server Petstore server.
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
termsOfService: http://swagger.io/terms/
title: Swagger Example API
version: "1.0"
paths: {}
swagger: "2.0"

View File

@@ -0,0 +1,23 @@
# Multiple API feature
Since swag 1.7.9 we are allowing registration of multiple endpoints into the same server.
Generate documentation for v1 endpoints
```shell
swag i -g main.go -dir api/v1 --instanceName v1
```
Generate documentation for v2 endpoints
```shell
swag i -g main.go -dir api/v2 --instanceName v2
```
Run example
```shell
go run main.go
```
Now you can access the v1 swagger here [http://localhost:8080/swagger/v1/index.html](http://localhost:8080/swagger/v1/index.html) ,
and v2 swagger here [http://localhost:8080/swagger/v2/index.html](http://localhost:8080/swagger/v2/index.html)

View File

@@ -0,0 +1,26 @@
package v1
import (
"github.com/gin-gonic/gin"
)
type Book struct {
ID int `json:"id,omitempty"`
Title string `json:"title"`
Author string `json:"author"`
Year *uint16 `json:"year"`
}
//
// @Summary Get a list of books in the the store
// @Description get string by ID
// @Accept json
// @Produce json
// @Success 200 {array} Book "ok"
// @Router /books [get]
func GetBooks(ctx *gin.Context) {
ctx.JSON(200, []Book{
{ID: 1, Title: "Book 1", Author: "Author 1", Year: nil},
{ID: 2, Title: "Book 2", Author: "Author 2", Year: nil},
})
}

View File

@@ -0,0 +1,25 @@
package v1
import (
"github.com/gin-gonic/gin"
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server.
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @BasePath /v1
func Register(router *gin.Engine) {
v1 := router.Group("v1")
v1.GET("/books", GetBooks)
}

View File

@@ -0,0 +1,26 @@
package v2
import (
"github.com/gin-gonic/gin"
)
type Book struct {
ID int `json:"id,omitempty"`
Title string `json:"title"`
Author string `json:"author"`
Year *uint16 `json:"year"`
}
//
// @Summary Get a list of books in the the store
// @Description get string by ID
// @Accept json
// @Produce json
// @Success 200 {array} Book "ok"
// @Router /books [get]
func GetBooks(ctx *gin.Context) {
ctx.JSON(200, []Book{
{ID: 1, Title: "Book 3", Author: "Author 3", Year: nil},
{ID: 2, Title: "Book 4", Author: "Author 4", Year: nil},
})
}

View File

@@ -0,0 +1,25 @@
package v2
import (
"github.com/gin-gonic/gin"
)
// @title Swagger Example API
// @version 2.0
// @description This is a sample server.
// @termsOfService http://swagger.io/terms/
// @contact.name API Support
// @contact.url http://www.swagger.io/support
// @contact.email support@swagger.io
// @license.name Apache 2.0
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
// @BasePath /v2
func Register(router *gin.Engine) {
v2 := router.Group("v2")
v2.GET("/books", GetBooks)
}

View File

@@ -0,0 +1,87 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import "github.com/swaggo/swag"
const docTemplatev1 = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/books": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get a list of books in the the store",
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/v1.Book"
}
}
}
}
}
}
},
"definitions": {
"v1.Book": {
"type": "object",
"properties": {
"author": {
"type": "string"
},
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"year": {
"type": "integer"
}
}
}
}
}`
// SwaggerInfov1 holds exported Swagger Info so clients can modify it
var SwaggerInfov1 = &swag.Spec{
Version: "1.0",
Host: "",
BasePath: "/v1",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample server.",
InfoInstanceName: "v1",
SwaggerTemplate: docTemplatev1,
}
func init() {
swag.Register(SwaggerInfov1.InstanceName(), SwaggerInfov1)
}

View File

@@ -0,0 +1,63 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0"
},
"basePath": "/v1",
"paths": {
"/books": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get a list of books in the the store",
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/v1.Book"
}
}
}
}
}
}
},
"definitions": {
"v1.Book": {
"type": "object",
"properties": {
"author": {
"type": "string"
},
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"year": {
"type": "integer"
}
}
}
}
}

View File

@@ -0,0 +1,42 @@
basePath: /v1
definitions:
v1.Book:
properties:
author:
type: string
id:
type: integer
title:
type: string
year:
type: integer
type: object
info:
contact:
email: support@swagger.io
name: API Support
url: http://www.swagger.io/support
description: This is a sample server.
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
termsOfService: http://swagger.io/terms/
title: Swagger Example API
version: "1.0"
paths:
/books:
get:
consumes:
- application/json
description: get string by ID
produces:
- application/json
responses:
"200":
description: ok
schema:
items:
$ref: '#/definitions/v1.Book'
type: array
summary: Get a list of books in the the store
swagger: "2.0"

View File

@@ -0,0 +1,87 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT
// This file was generated by swaggo/swag
package docs
import "github.com/swaggo/swag"
const docTemplatev2 = `{
"schemes": {{ marshal .Schemes }},
"swagger": "2.0",
"info": {
"description": "{{escape .Description}}",
"title": "{{.Title}}",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "{{.Version}}"
},
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/books": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get a list of books in the the store",
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/v2.Book"
}
}
}
}
}
}
},
"definitions": {
"v2.Book": {
"type": "object",
"properties": {
"author": {
"type": "string"
},
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"year": {
"type": "integer"
}
}
}
}
}`
// SwaggerInfov2 holds exported Swagger Info so clients can modify it
var SwaggerInfov2 = &swag.Spec{
Version: "2.0",
Host: "",
BasePath: "/v2",
Schemes: []string{},
Title: "Swagger Example API",
Description: "This is a sample server.",
InfoInstanceName: "v2",
SwaggerTemplate: docTemplatev2,
}
func init() {
swag.Register(SwaggerInfov2.InstanceName(), SwaggerInfov2)
}

View File

@@ -0,0 +1,63 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server.",
"title": "Swagger Example API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "http://www.swagger.io/support",
"email": "support@swagger.io"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "2.0"
},
"basePath": "/v2",
"paths": {
"/books": {
"get": {
"description": "get string by ID",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"summary": "Get a list of books in the the store",
"responses": {
"200": {
"description": "ok",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/v2.Book"
}
}
}
}
}
}
},
"definitions": {
"v2.Book": {
"type": "object",
"properties": {
"author": {
"type": "string"
},
"id": {
"type": "integer"
},
"title": {
"type": "string"
},
"year": {
"type": "integer"
}
}
}
}
}

View File

@@ -0,0 +1,42 @@
basePath: /v2
definitions:
v2.Book:
properties:
author:
type: string
id:
type: integer
title:
type: string
year:
type: integer
type: object
info:
contact:
email: support@swagger.io
name: API Support
url: http://www.swagger.io/support
description: This is a sample server.
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
termsOfService: http://swagger.io/terms/
title: Swagger Example API
version: "2.0"
paths:
/books:
get:
consumes:
- application/json
description: get string by ID
produces:
- application/json
responses:
"200":
description: ok
schema:
items:
$ref: '#/definitions/v2.Book'
type: array
summary: Get a list of books in the the store
swagger: "2.0"

26
example/multiple/main.go Normal file
View File

@@ -0,0 +1,26 @@
package main
import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
v1 "github.com/swaggo/gin-swagger/example/multiple/api/v1"
v2 "github.com/swaggo/gin-swagger/example/multiple/api/v2"
_ "github.com/swaggo/gin-swagger/example/multiple/docs"
)
func main() {
// New gin router
router := gin.New()
// Register api/v1 endpoints
v1.Register(router)
router.GET("/swagger/v1/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("v1")))
// Register api/v2 endpoints
v2.Register(router)
router.GET("/swagger/v2/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, ginSwagger.InstanceName("v2")))
// Listen and Server in
_ = router.Run()
}

6
go.mod
View File

@@ -1,12 +1,12 @@
module github.com/swaggo/gin-swagger
go 1.14
go 1.15
require (
github.com/gin-contrib/gzip v0.0.3
github.com/gin-gonic/gin v1.7.7
github.com/stretchr/testify v1.7.0
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2
github.com/swaggo/swag v1.7.8
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d
github.com/swaggo/swag v1.7.9
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
)

11
go.sum
View File

@@ -81,6 +81,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -89,8 +90,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2 h1:+iNTcqQJy0OZ5jk6a5NLib47eqXK8uYcPX+O4+cBpEM=
github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w=
github.com/swaggo/swag v1.7.8 h1:w249t0l/kc/DKMGlS0fppNJQxKyJ8heNaUWB6nsH3zc=
github.com/swaggo/swag v1.7.8/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
github.com/swaggo/swag v1.7.9 h1:6vCG5mm43ebDzGlZPMGYrYI4zKFfOr5kicQX8qjeDwc=
github.com/swaggo/swag v1.7.9/go.mod h1:gZ+TJ2w/Ve1RwQsA2IRoSOTidHz6DX+PIG8GWvbnoLU=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
@@ -109,6 +110,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -117,9 +120,13 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@@ -16,56 +16,56 @@ import (
type swaggerConfig struct {
URL string
DeepLinking bool
DocExpansion string
DefaultModelsExpandDepth int
Oauth2RedirectURL template.JS
Title string
Oauth2RedirectURL template.JS
DefaultModelsExpandDepth int
DeepLinking bool
PersistAuthorization bool
}
// Config stores ginSwagger configuration variables.
type Config struct {
//The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`.
// The url pointing to API definition (normally swagger.json or swagger.yaml). Default is `doc.json`.
URL string
DeepLinking bool
DocExpansion string
DefaultModelsExpandDepth int
InstanceName string
Title string
DefaultModelsExpandDepth int
DeepLinking bool
PersistAuthorization bool
}
// Convert the config to a swagger one in order to fill unexposed template values.
func (c Config) ToSwaggerConfig() swaggerConfig {
func (config Config) toSwaggerConfig() swaggerConfig {
return swaggerConfig{
URL: c.URL,
DeepLinking: c.DeepLinking,
DocExpansion: c.DocExpansion,
DefaultModelsExpandDepth: c.DefaultModelsExpandDepth,
Oauth2RedirectURL: template.JS(
"`${window.location.protocol}//${window.location.host}$" +
"{window.location.pathname.split('/').slice(0, window.location.pathname.split('/').length - 1).join('/')}" +
"/oauth2-redirect.html`",
),
Title: c.Title,
URL: config.URL,
DeepLinking: config.DeepLinking,
DocExpansion: config.DocExpansion,
DefaultModelsExpandDepth: config.DefaultModelsExpandDepth,
Oauth2RedirectURL: "`${window.location.protocol}//${window.location.host}$" +
"{window.location.pathname.split('/').slice(0, window.location.pathname.split('/').length - 1).join('/')}" +
"/oauth2-redirect.html`",
Title: config.Title,
PersistAuthorization: config.PersistAuthorization,
}
}
// URL presents the url pointing to API definition (normally swagger.json or swagger.yaml).
func URL(url string) func(c *Config) {
func URL(url string) func(*Config) {
return func(c *Config) {
c.URL = url
}
}
// DocExpansion list, full, none.
func DocExpansion(docExpansion string) func(c *Config) {
func DocExpansion(docExpansion string) func(*Config) {
return func(c *Config) {
c.DocExpansion = docExpansion
}
}
// DeepLinking set the swagger deeplinking configuration
func DeepLinking(deepLinking bool) func(c *Config) {
// DeepLinking set the swagger deep linking configuration.
func DeepLinking(deepLinking bool) func(*Config) {
return func(c *Config) {
c.DeepLinking = deepLinking
}
@@ -73,61 +73,76 @@ func DeepLinking(deepLinking bool) func(c *Config) {
// DefaultModelsExpandDepth set the default expansion depth for models
// (set to -1 completely hide the models).
func DefaultModelsExpandDepth(depth int) func(c *Config) {
func DefaultModelsExpandDepth(depth int) func(*Config) {
return func(c *Config) {
c.DefaultModelsExpandDepth = depth
}
}
// InstanceName set the instance name that was used to generate the swagger documents.
// InstanceName set the instance name that was used to generate the swagger documents
// Defaults to swag.Name ("swagger").
func InstanceName(name string) func(c *Config) {
func InstanceName(name string) func(*Config) {
return func(c *Config) {
c.InstanceName = name
}
}
// WrapHandler wraps `http.Handler` into `gin.HandlerFunc`.
func WrapHandler(h *webdav.Handler, confs ...func(c *Config)) gin.HandlerFunc {
defaultConfig := &Config{
URL: "doc.json",
DeepLinking: true,
DocExpansion: "list",
DefaultModelsExpandDepth: 1,
InstanceName: swag.Name,
Title: "Swagger UI",
// PersistAuthorization Persist authorization information over browser close/refresh.
// Defaults to false.
func PersistAuthorization(persistAuthorization bool) func(*Config) {
return func(c *Config) {
c.PersistAuthorization = persistAuthorization
}
for _, c := range confs {
c(defaultConfig)
}
return CustomWrapHandler(defaultConfig, h)
}
// CustomWrapHandler wraps `http.Handler` into `gin.HandlerFunc`
// WrapHandler wraps `http.Handler` into `gin.HandlerFunc`.
func WrapHandler(handler *webdav.Handler, options ...func(*Config)) gin.HandlerFunc {
var config = Config{
URL: "doc.json",
DocExpansion: "list",
InstanceName: swag.Name,
Title: "Swagger UI",
DefaultModelsExpandDepth: 1,
DeepLinking: true,
PersistAuthorization: false,
}
for _, c := range options {
c(&config)
}
return CustomWrapHandler(&config, handler)
}
// CustomWrapHandler wraps `http.Handler` into `gin.HandlerFunc`.
func CustomWrapHandler(config *Config, handler *webdav.Handler) gin.HandlerFunc {
var once sync.Once
if config.InstanceName == "" {
config.InstanceName = swag.Name
}
if config.Title == "" {
config.Title = "Swagger UI"
}
// create a template with name
t := template.New("swagger_index.html")
index, _ := t.Parse(swagger_index_templ)
index, _ := template.New("swagger_index.html").Parse(swaggerIndexTpl)
var rexp = regexp.MustCompile(`(.*)(index\.html|doc\.json|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[\?|.]*`)
var matcher = regexp.MustCompile(`(.*)(index\.html|doc\.json|favicon-16x16\.png|favicon-32x32\.png|/oauth2-redirect\.html|swagger-ui\.css|swagger-ui\.css\.map|swagger-ui\.js|swagger-ui\.js\.map|swagger-ui-bundle\.js|swagger-ui-bundle\.js\.map|swagger-ui-standalone-preset\.js|swagger-ui-standalone-preset\.js\.map)[?|.]*`)
return func(c *gin.Context) {
matches := rexp.FindStringSubmatch(c.Request.RequestURI)
return func(ctx *gin.Context) {
if ctx.Request.Method != http.MethodGet {
ctx.AbortWithStatus(http.StatusMethodNotAllowed)
return
}
matches := matcher.FindStringSubmatch(ctx.Request.RequestURI)
if len(matches) != 3 {
c.Status(http.StatusNotFound)
_, _ = c.Writer.Write([]byte("404 page not found"))
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return
}
@@ -138,39 +153,39 @@ func CustomWrapHandler(config *Config, handler *webdav.Handler) gin.HandlerFunc
switch filepath.Ext(path) {
case ".html":
c.Header("Content-Type", "text/html; charset=utf-8")
ctx.Header("Content-Type", "text/html; charset=utf-8")
case ".css":
c.Header("Content-Type", "text/css; charset=utf-8")
ctx.Header("Content-Type", "text/css; charset=utf-8")
case ".js":
c.Header("Content-Type", "application/javascript")
ctx.Header("Content-Type", "application/javascript")
case ".png":
c.Header("Content-Type", "image/png")
ctx.Header("Content-Type", "image/png")
case ".json":
c.Header("Content-Type", "application/json; charset=utf-8")
ctx.Header("Content-Type", "application/json; charset=utf-8")
}
switch path {
case "index.html":
_ = index.Execute(c.Writer, config.ToSwaggerConfig())
_ = index.Execute(ctx.Writer, config.toSwaggerConfig())
case "doc.json":
doc, err := swag.ReadDoc(config.InstanceName)
if err != nil {
c.AbortWithStatus(http.StatusInternalServerError)
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
_, _ = c.Writer.Write([]byte(doc))
ctx.String(http.StatusOK, doc)
default:
handler.ServeHTTP(c.Writer, c.Request)
handler.ServeHTTP(ctx.Writer, ctx.Request)
}
}
}
// DisablingWrapHandler turn handler off
// if specified environment variable passed
func DisablingWrapHandler(h *webdav.Handler, envName string) gin.HandlerFunc {
eFlag := os.Getenv(envName)
if eFlag != "" {
// if specified environment variable passed.
func DisablingWrapHandler(handler *webdav.Handler, envName string) gin.HandlerFunc {
if os.Getenv(envName) != "" {
return func(c *gin.Context) {
// Simulate behavior when route unspecified and
// return 404 HTTP code
@@ -178,14 +193,13 @@ func DisablingWrapHandler(h *webdav.Handler, envName string) gin.HandlerFunc {
}
}
return WrapHandler(h)
return WrapHandler(handler)
}
// DisablingCustomWrapHandler turn handler off
// if specified environment variable passed
func DisablingCustomWrapHandler(config *Config, h *webdav.Handler, envName string) gin.HandlerFunc {
eFlag := os.Getenv(envName)
if eFlag != "" {
// if specified environment variable passed.
func DisablingCustomWrapHandler(config *Config, handler *webdav.Handler, envName string) gin.HandlerFunc {
if os.Getenv(envName) != "" {
return func(c *gin.Context) {
// Simulate behavior when route unspecified and
// return 404 HTTP code
@@ -193,10 +207,10 @@ func DisablingCustomWrapHandler(config *Config, h *webdav.Handler, envName strin
}
}
return CustomWrapHandler(config, h)
return CustomWrapHandler(config, handler)
}
const swagger_index_templ = `<!-- HTML for static distribution bundle build -->
const swaggerIndexTpl = `<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
@@ -275,6 +289,7 @@ window.onload = function() {
dom_id: '#swagger-ui',
validatorUrl: null,
oauth2RedirectUrl: {{.Oauth2RedirectURL}},
persistAuthorization: {{.PersistAuthorization}},
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset

View File

@@ -1,6 +1,8 @@
package ginSwagger
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"testing"
@@ -26,43 +28,50 @@ func TestWrapHandler(t *testing.T) {
router.GET("/*any", WrapHandler(swaggerFiles.Handler, URL("https://github.com/swaggo/gin-swagger")))
w1 := performRequest("GET", "/index.html", router)
assert.Equal(t, 200, w1.Code)
assert.Equal(t, http.StatusOK, performRequest("GET", "/index.html", router).Code)
}
func TestWrapCustomHandler(t *testing.T) {
gin.SetMode(gin.TestMode)
router := gin.New()
router.GET("/*any", CustomWrapHandler(&Config{}, swaggerFiles.Handler))
router.Any("/*any", CustomWrapHandler(&Config{}, swaggerFiles.Handler))
w1 := performRequest("GET", "/index.html", router)
assert.Equal(t, 200, w1.Code)
w1 := performRequest(http.MethodGet, "/index.html", router)
assert.Equal(t, http.StatusOK, w1.Code)
assert.Equal(t, w1.Header()["Content-Type"][0], "text/html; charset=utf-8")
w2 := performRequest("GET", "/doc.json", router)
assert.Equal(t, 500, w2.Code)
assert.Equal(t, http.StatusInternalServerError, performRequest(http.MethodGet, "/doc.json", router).Code)
swag.Register(swag.Name, &mockedSwag{})
doc := &mockedSwag{}
swag.Register(swag.Name, doc)
w2 = performRequest("GET", "/doc.json", router)
assert.Equal(t, 200, w2.Code)
w2 := performRequest(http.MethodGet, "/doc.json", router)
assert.Equal(t, http.StatusOK, w2.Code)
assert.Equal(t, w2.Header()["Content-Type"][0], "application/json; charset=utf-8")
w3 := performRequest("GET", "/favicon-16x16.png", router)
assert.Equal(t, 200, w3.Code)
// Perform body rendering validation
w2Body, err := ioutil.ReadAll(w2.Body)
assert.NoError(t, err)
assert.Equal(t, doc.ReadDoc(), string(w2Body))
w3 := performRequest(http.MethodGet, "/favicon-16x16.png", router)
assert.Equal(t, http.StatusOK, w3.Code)
assert.Equal(t, w3.Header()["Content-Type"][0], "image/png")
w4 := performRequest("GET", "/swagger-ui.css", router)
assert.Equal(t, 200, w4.Code)
w4 := performRequest(http.MethodGet, "/swagger-ui.css", router)
assert.Equal(t, http.StatusOK, w4.Code)
assert.Equal(t, w4.Header()["Content-Type"][0], "text/css; charset=utf-8")
w5 := performRequest("GET", "/swagger-ui-bundle.js", router)
assert.Equal(t, 200, w5.Code)
w5 := performRequest(http.MethodGet, "/swagger-ui-bundle.js", router)
assert.Equal(t, http.StatusOK, w5.Code)
assert.Equal(t, w5.Header()["Content-Type"][0], "application/javascript")
w6 := performRequest("GET", "/notfound", router)
assert.Equal(t, 404, w6.Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/notfound", router).Code)
assert.Equal(t, http.StatusMethodNotAllowed, performRequest(http.MethodPost, "/index.html", router).Code)
assert.Equal(t, http.StatusMethodNotAllowed, performRequest(http.MethodPut, "/index.html", router).Code)
}
func TestDisablingWrapHandler(t *testing.T) {
@@ -73,33 +82,20 @@ func TestDisablingWrapHandler(t *testing.T) {
router.GET("/simple/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey))
w1 := performRequest("GET", "/simple/index.html", router)
assert.Equal(t, 200, w1.Code)
assert.Equal(t, http.StatusOK, performRequest(http.MethodGet, "/simple/index.html", router).Code)
assert.Equal(t, http.StatusOK, performRequest(http.MethodGet, "/simple/doc.json", router).Code)
w2 := performRequest("GET", "/simple/doc.json", router)
assert.Equal(t, 200, w2.Code)
w3 := performRequest("GET", "/simple/favicon-16x16.png", router)
assert.Equal(t, 200, w3.Code)
w4 := performRequest("GET", "/simple/notfound", router)
assert.Equal(t, 404, w4.Code)
assert.Equal(t, http.StatusOK, performRequest(http.MethodGet, "/simple/favicon-16x16.png", router).Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/simple/notfound", router).Code)
_ = os.Setenv(disablingKey, "true")
router.GET("/disabling/*any", DisablingWrapHandler(swaggerFiles.Handler, disablingKey))
w11 := performRequest("GET", "/disabling/index.html", router)
assert.Equal(t, 404, w11.Code)
w22 := performRequest("GET", "/disabling/doc.json", router)
assert.Equal(t, 404, w22.Code)
w44 := performRequest("GET", "/disabling/oauth2-redirect.html", router)
assert.Equal(t, 404, w44.Code)
w55 := performRequest("GET", "/disabling/notfound", router)
assert.Equal(t, 404, w55.Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/disabling/index.html", router).Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/disabling/doc.json", router).Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/disabling/oauth2-redirect.html", router).Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/disabling/notfound", router).Code)
}
func TestDisablingCustomWrapHandler(t *testing.T) {
@@ -110,15 +106,13 @@ func TestDisablingCustomWrapHandler(t *testing.T) {
router.GET("/simple/*any", DisablingCustomWrapHandler(&Config{}, swaggerFiles.Handler, disablingKey))
w1 := performRequest("GET", "/simple/index.html", router)
assert.Equal(t, 200, w1.Code)
assert.Equal(t, http.StatusOK, performRequest(http.MethodGet, "/simple/index.html", router).Code)
_ = os.Setenv(disablingKey, "true")
router.GET("/disabling/*any", DisablingCustomWrapHandler(&Config{}, swaggerFiles.Handler, disablingKey))
w11 := performRequest("GET", "/disabling/index.html", router)
assert.Equal(t, 404, w11.Code)
assert.Equal(t, http.StatusNotFound, performRequest(http.MethodGet, "/disabling/index.html", router).Code)
}
func TestWithGzipMiddleware(t *testing.T) {
@@ -129,20 +123,20 @@ func TestWithGzipMiddleware(t *testing.T) {
router.GET("/*any", WrapHandler(swaggerFiles.Handler))
w1 := performRequest("GET", "/index.html", router)
assert.Equal(t, 200, w1.Code)
w1 := performRequest(http.MethodGet, "/index.html", router)
assert.Equal(t, http.StatusOK, w1.Code)
assert.Equal(t, w1.Header()["Content-Type"][0], "text/html; charset=utf-8")
w2 := performRequest("GET", "/swagger-ui.css", router)
assert.Equal(t, 200, w2.Code)
w2 := performRequest(http.MethodGet, "/swagger-ui.css", router)
assert.Equal(t, http.StatusOK, w2.Code)
assert.Equal(t, w2.Header()["Content-Type"][0], "text/css; charset=utf-8")
w3 := performRequest("GET", "/swagger-ui-bundle.js", router)
assert.Equal(t, 200, w3.Code)
w3 := performRequest(http.MethodGet, "/swagger-ui-bundle.js", router)
assert.Equal(t, http.StatusOK, w3.Code)
assert.Equal(t, w3.Header()["Content-Type"][0], "application/javascript")
w4 := performRequest("GET", "/doc.json", router)
assert.Equal(t, 200, w4.Code)
w4 := performRequest(http.MethodGet, "/doc.json", router)
assert.Equal(t, http.StatusOK, w4.Code)
assert.Equal(t, w4.Header()["Content-Type"][0], "application/json; charset=utf-8")
}
@@ -226,3 +220,16 @@ func TestInstanceName(t *testing.T) {
configFunc(&cfg)
assert.Equal(t, expected, cfg.InstanceName)
}
func TestPersistAuthorization(t *testing.T) {
var cfg Config
assert.Equal(t, false, cfg.PersistAuthorization)
configFunc := PersistAuthorization(true)
configFunc(&cfg)
assert.Equal(t, true, cfg.PersistAuthorization)
configFunc = PersistAuthorization(false)
configFunc(&cfg)
assert.Equal(t, false, cfg.PersistAuthorization)
}