1
0

Migrate to new swagger docs generation (#18)

This commit is contained in:
konrad
2018-11-12 15:46:35 +00:00
committed by Gitea
parent d3de658882
commit 373bbd2202
153 changed files with 32114 additions and 1416 deletions

15
vendor/github.com/swaggo/echo-swagger/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,15 @@
language: go
go:
- 1.8.x
- 1.9.x
- 1.10.x
before_install:
- go get -t -v ./...
script:
- go test -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

21
vendor/github.com/swaggo/echo-swagger/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Swaggo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

72
vendor/github.com/swaggo/echo-swagger/README.md generated vendored Normal file
View File

@ -0,0 +1,72 @@
# echo-swagger
echo middleware to automatically generate RESTful API documentation with Swagger 2.0.
[![Travis branch](https://img.shields.io/travis/swaggo/echo-swagger/master.svg)](https://travis-ci.org/swaggo/echo-swagger)
[![Codecov branch](https://img.shields.io/codecov/c/github/swaggo/echo-swagger/master.svg)](https://codecov.io/gh/swaggo/echo-swagger)
[![Go Report Card](https://goreportcard.com/badge/github.com/swaggo/echo-swagger)](https://goreportcard.com/report/github.com/swaggo/echo-swagger)
## Usage
### Start using it
1. Add comments to your API source code, [See Declarative Comments Format](https://github.com/swaggo/swag#declarative-comments-format).
2. Download [Swag](https://github.com/swaggo/swag) for Go by using:
```sh
$ go get github.com/swaggo/swag/cmd/swag
```
3. Run the [Swag](https://github.com/swaggo/swag) in your Go project root folder which contains `main.go` file, [Swag](https://github.com/swaggo/swag) will parse comments and generate required files(`docs` folder and `docs/doc.go`).
```sh
$ swag init
```
4.Download [echo-swagger](https://github.com/swaggo/echo-swagger) by using:
```sh
$ go get -u github.com/swaggo/echo-swagger
```
And import following in your code:
```go
import "github.com/swaggo/echo-swagger" // echo-swagger middleware
```
### Canonical example:
```go
package main
import (
"github.com/labstack/echo"
"github.com/swaggo/echo-swagger"
_ "github.com/swaggo/echo-swagger/example/docs" // docs is generated by Swag CLI, you have to import it.
)
// @title Swagger Example API
// @version 1.0
// @description This is a sample server Petstore 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
// @host petstore.swagger.io
// @BasePath /v2
func main() {
e := echo.New()
e.GET("/swagger/*", echoSwagger.WrapHandler)
e.Logger.Fatal(e.Start(":1323"))
}
```
5. Run it, and browser to http://localhost:1323/swagger/index.html, you can see Swagger 2.0 Api documents.
![swagger_index.html](https://user-images.githubusercontent.com/8943871/36250587-40834072-1279-11e8-8bb7-02a2e2fdd7a7.png)

151
vendor/github.com/swaggo/echo-swagger/swagger.go generated vendored Normal file
View File

@ -0,0 +1,151 @@
package echoSwagger
import (
"golang.org/x/net/webdav"
"html/template"
"net/http"
"regexp"
"github.com/labstack/echo"
"github.com/swaggo/files"
"github.com/swaggo/swag"
)
// WrapHandler wraps swaggerFiles.Handler and returns echo.HandlerFunc
var WrapHandler = wrapHandler(swaggerFiles.Handler)
// wapHandler wraps `http.Handler` into `gin.HandlerFunc`.
func wrapHandler(h *webdav.Handler) echo.HandlerFunc {
//create a template with name
t := template.New("swagger_index.html")
index, _ := t.Parse(indexTempl)
type pro struct {
Host string
}
var re = 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 echo.Context) error {
var matches []string
if matches = re.FindStringSubmatch(c.Request().RequestURI); len(matches) != 3 {
return c.String(http.StatusNotFound, "404 page not found")
}
path := matches[2]
prefix := matches[1]
h.Prefix = prefix
switch path {
case "index.html":
s := &pro{
Host: "doc.json", //TODO: provide to customs?
}
index.Execute(c.Response().Writer, s)
case "doc.json":
doc, _ := swag.ReadDoc()
c.Response().Write([]byte(doc))
default:
h.ServeHTTP(c.Response().Writer, c.Request())
}
return nil
}
}
const indexTempl = `<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700|Source+Code+Pro:300,600|Titillium+Web:400,600,700" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body {
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "{{.Host}}",
dom_id: '#swagger-ui',
validatorUrl: null,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
window.ui = ui
}
</script>
</body>
</html>
`

1
vendor/github.com/swaggo/files/README.md generated vendored Normal file
View File

@ -0,0 +1 @@
# swaggerFiles

131
vendor/github.com/swaggo/files/ab0x.go generated vendored Normal file
View File

@ -0,0 +1,131 @@
// Code generated by fileb0x at "2017-11-26 17:57:18.000591466 +0600 +06 m=+3.756909921" from config file "b0x.yml" DO NOT EDIT.
package swaggerFiles
import (
"bytes"
"io"
"log"
"net/http"
"os"
"path"
"golang.org/x/net/context"
"golang.org/x/net/webdav"
)
var (
// CTX is a context for webdav vfs
CTX = context.Background()
// FS is a virtual memory file system
FS = webdav.NewMemFS()
// Handler is used to server files through a http handler
Handler *webdav.Handler
// HTTP is the http file system
HTTP http.FileSystem = new(HTTPFS)
)
// HTTPFS implements http.FileSystem
type HTTPFS struct{}
func init() {
if CTX.Err() != nil {
log.Fatal(CTX.Err())
}
//var err error
Handler = &webdav.Handler{
FileSystem: FS,
LockSystem: webdav.NewMemLS(),
}
}
// Open a file
func (hfs *HTTPFS) Open(path string) (http.File, error) {
f, err := FS.OpenFile(CTX, path, os.O_RDONLY, 0644)
if err != nil {
return nil, err
}
return f, nil
}
// ReadFile is adapTed from ioutil
func ReadFile(path string) ([]byte, error) {
f, err := FS.OpenFile(CTX, path, os.O_RDONLY, 0644)
if err != nil {
return nil, err
}
buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
// If the buffer overflows, we will get bytes.ErrTooLarge.
// Return that as an error. Any other panic remains.
defer func() {
e := recover()
if e == nil {
return
}
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
err = panicErr
} else {
panic(e)
}
}()
_, err = buf.ReadFrom(f)
return buf.Bytes(), err
}
// WriteFile is adapTed from ioutil
func WriteFile(filename string, data []byte, perm os.FileMode) error {
f, err := FS.OpenFile(CTX, filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
n, err := f.Write(data)
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
if err1 := f.Close(); err == nil {
err = err1
}
return err
}
// WalkDirs looks for files in the given dir and returns a list of files in it
// usage for all files in the b0x: WalkDirs("", false)
func WalkDirs(name string, includeDirsInList bool, files ...string) ([]string, error) {
f, err := FS.OpenFile(CTX, name, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
fileInfos, err := f.Readdir(0)
f.Close()
if err != nil {
return nil, err
}
for _, info := range fileInfos {
filename := path.Join(name, info.Name())
if includeDirsInList || !info.IsDir() {
files = append(files, filename)
}
if info.IsDir() {
files, err = WalkDirs(filename, includeDirsInList, files...)
if err != nil {
return nil, err
}
}
}
return files, nil
}

View File

@ -0,0 +1,29 @@
// Code generaTed by fileb0x at "2017-11-26 17:57:23.142282087 +0600 +06 m=+8.898600553" from config file "b0x.yml" DO NOT EDIT.
package swaggerFiles
import (
"log"
"os"
)
// FileFavicon16x16Png is "/favicon-16x16.png"
var FileFavicon16x16Png = []byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\x00\x00\x01\x84\x49\x44\x41\x54\x78\x01\x95\x53\x03\x4c\x75\x71\x1c\xfd\x8c\xf1\xc3\xec\x30\xa7\x29\xcd\x61\xb6\x6b\x36\xb2\x9b\xf9\xb2\x6b\xc8\x35\x2f\xdb\x8d\x71\x78\xc6\x94\x6d\xcc\x7b\xef\x7f\x4f\xff\xf3\x6c\xdc\xed\xf2\xe0\xfe\xf8\xc9\xff\x50\x14\x11\x2f\x14\x5b\xa3\x50\xc4\xa1\xbc\x3f\xf1\x74\x3e\x37\x12\x73\x13\x03\x85\xca\x37\x49\x52\x09\x61\xb5\x6a\x8f\xa7\x31\xbe\x5d\x88\xf6\xb9\x4c\xf0\x1c\x93\xcf\xda\xe3\x29\x10\x93\x66\x8d\xe4\x06\x13\xcf\xde\x3c\x9b\xd1\x34\x95\x8a\x92\x81\x4f\x41\xcf\x46\x89\xdd\x3c\x9b\x20\x4d\xe6\x7d\x4c\xe4\x07\x15\xc5\xf5\xe3\xff\x49\x0c\x7b\xd6\x8d\xff\x73\x99\x34\xba\x73\x66\x68\xae\x3f\xaf\x6b\x1a\x70\x72\x77\x10\x20\x3c\xb9\xdb\xc7\x86\xa6\xd1\x19\x49\x0a\xa8\xb1\xd7\x84\x79\x33\x67\x17\x31\x54\x24\xb5\x63\x7f\x71\xfb\x62\x71\xbf\x6b\x8e\x27\x1d\x51\xb0\xc2\x2c\x92\x0b\x78\x7c\x3b\x46\xe5\xf0\xef\x00\x83\xf2\xa1\x1f\x78\x7c\x3f\x71\xbd\xcb\xc2\x16\x80\x5a\x46\xf0\xc4\x4a\xf3\xe3\xe4\x6e\x31\xcc\x17\x6b\x60\x3a\x7d\xcb\x79\xe8\x98\xcb\x42\xc7\x7c\x36\x7a\x97\x72\xd1\x34\x9d\x06\xd3\xf9\x8a\xe4\x94\x90\x8b\xb6\xd9\x0c\x50\xeb\x63\x40\xd0\x7c\xbe\x2a\xc9\x34\xc8\xa7\x98\x27\xcd\x68\x00\xe3\xd9\x32\xa6\x76\x4b\x7d\x0c\x42\xa4\xf0\x2b\x44\x0a\xc7\x81\x29\xb0\x10\x9a\xe3\xa9\xd8\x8b\x78\xe4\x28\xa2\xbb\x8d\x6c\x0d\x01\xb6\x8a\x2d\xf3\x37\x38\xbe\xdd\xc7\xa6\xb6\xc9\xd9\xc6\x64\xd8\x5c\x6d\xf4\x0c\x92\x09\x75\x51\x0e\xd2\xf5\xb3\xd1\xf1\x77\xdf\x51\x16\xb3\x34\x61\x24\xa1\xc4\xc4\x28\x56\xbc\x46\xd9\xdf\xa4\x91\xe9\xb0\x26\x2c\x12\x2b\xcd\x93\xcf\x1c\x1c\x62\xdc\xca\x00\x71\x74\xeb\xcc\x2d\x14\x89\xfe\xfc\x0f\x6d\x32\x6a\x88\xec\xcc\x73\x18\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82")
func init() {
f, err := FS.OpenFile(CTX, "/favicon-16x16.png", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
log.Fatal(err)
}
_, err = f.Write(FileFavicon16x16Png)
if err != nil {
log.Fatal(err)
}
err = f.Close()
if err != nil {
log.Fatal(err)
}
}

View File

@ -0,0 +1,29 @@
// Code generaTed by fileb0x at "2017-11-26 17:57:18.759175324 +0600 +06 m=+4.515493805" from config file "b0x.yml" DO NOT EDIT.
package swaggerFiles
import (
"log"
"os"
)
// FileFavicon32x32Png is "/favicon-32x32.png"
var FileFavicon32x32Png = []byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\x00\x00\x04\x3c\x49\x44\x41\x54\x78\x01\xbd\x57\x03\xcc\x65\x59\x0c\x7e\x6b\xdb\xb6\x6d\xc4\x5e\xc7\x5e\xdb\xb6\x3d\x46\xf0\xdb\xb6\x6d\xdb\xb6\x6d\xf3\xa2\xd3\x6f\xf2\xce\x33\x7f\x35\x69\xee\x61\xdb\x5b\x1f\x8d\xa3\xa0\xaa\xea\xa9\xb2\xb2\xf9\xa4\xac\x48\x3f\xf2\x37\x42\x92\xd7\xab\x78\x3c\x02\x94\xe4\x8d\x1a\xfe\x46\x61\x0f\x67\x70\x56\xb3\x53\xa0\xa8\xf2\x85\x4c\xf8\x7b\x45\x91\xfa\x88\x54\x72\x04\x70\x96\x05\xf9\x91\xef\x5e\x6c\x8f\xbe\x9d\x3f\x96\xde\x66\x22\x53\x82\x30\xaf\xd1\xf0\x74\x03\x95\xb4\x7a\x52\x62\xe5\xcf\x14\x5e\xf4\x21\x90\xc7\x3f\x51\x71\xab\x07\xef\xd5\x13\xce\x08\xc0\x5d\xa6\xf1\x2e\x68\x39\xc9\x5c\xb9\x98\xff\x20\x4e\x10\x5b\xdf\x5c\xa6\xbc\xa6\xe3\xf4\x6f\xc4\xdd\xf4\x99\xa7\xc6\x26\xfe\x13\x71\x17\xe5\x36\x1e\xe3\x3b\x4b\x3a\xa1\x59\x88\x04\xd0\x74\x94\xf9\xd5\x7c\xa1\x41\x5c\xae\xea\x0a\xa1\x5f\x82\xaf\x01\x71\xa7\xf0\x97\xa0\xab\xa9\xb2\x33\x08\x34\x84\x59\x9a\x98\xf6\xb5\x76\xff\x5c\x30\x67\xc7\xa2\x90\xfc\xb7\x41\x6c\x5b\x18\x9c\xff\x26\xb1\xc3\x1a\x08\xa1\x5e\x6c\xcb\xe6\x71\x82\xb9\x47\xc6\x2b\x20\xb0\x23\xe8\x9e\xf6\xa2\xa1\x10\x09\x16\x7d\x02\x0e\x07\x75\x01\x43\xf2\xdf\xc2\xc5\x1d\xc5\xa0\xbc\x37\x48\xd0\x87\x63\x9a\xaa\xfe\x42\xf6\xd8\x09\x62\xa8\xea\x0a\x36\xba\xf8\xb5\xcf\x39\x54\xd1\x11\x40\xab\xeb\x73\x34\x34\x55\x6b\x97\xd1\xe0\x54\x0d\xad\x6e\xcc\xc3\xfe\xf4\xb5\xef\xb9\x46\x7b\x15\x9d\x01\xba\xe8\x30\x0a\x51\xc4\xb9\xf0\x76\x53\x87\x8b\x2e\xfd\x82\x00\xe3\x73\x6d\x94\xdd\x70\xc8\xae\x00\xd9\xf5\x07\x69\x6c\xb6\x85\x00\xb1\x65\x5f\x1b\xed\xfd\x1c\x74\x95\x2e\x3a\x90\xb4\x74\xb6\x67\xbb\xf4\x60\x31\x8f\xc3\xc7\x94\x20\xa2\x00\xb0\x3f\xfa\x21\x87\xd5\xfd\x5f\xd4\x7d\x04\xa8\xed\x89\x30\xdb\xcb\x69\x38\xa2\xf5\x05\xb9\xef\xa4\x2f\x20\x75\x8a\x90\x43\x0c\x9b\x5e\x68\x19\x4c\x21\xc0\xef\xa1\x37\x39\x2c\x00\xb4\x08\x68\x1d\x4c\x33\xdb\xfb\x3b\xfc\x0e\x5d\x68\x32\xef\xa7\x35\x50\x05\x26\xc8\x62\x38\x60\x2e\x40\x1a\x01\x7e\x0b\xb9\xde\x61\x01\x7e\x0c\xbc\x1c\x4c\xa8\x75\x28\xdd\xd2\x3e\x7c\x49\x44\xc4\xcf\xd0\x40\x04\x26\x25\xad\x1e\x16\x0f\xf7\x8d\x97\x41\x52\xfa\xca\xe7\x6c\x87\x05\xf8\xd2\xfb\x0c\x84\x1d\x0d\x4c\x56\x59\xdc\x2f\x6a\x75\x13\x1a\x88\xd2\xa0\xaa\x61\x82\x7c\x6e\x7a\x70\x5f\xf4\x03\xc8\x09\xd4\x3b\x5e\x8a\x39\x7d\xee\x75\x9a\x91\x20\x60\x04\x14\x73\xec\xe1\x0c\xc6\x5d\xa3\x05\x60\x60\xd1\x77\x12\x2a\x7e\x20\x00\xf3\xae\xd3\xa0\x9c\x62\x82\xa2\x62\x78\x28\xb3\x6e\x1f\x71\x78\xd2\xf2\xda\x34\x1d\x8a\x7d\x1c\x6b\xd4\x3e\x9c\x49\x2b\xeb\xb3\xf4\x6b\xc8\x75\x60\x4c\x93\xf3\x5d\x34\xb5\xd0\xc3\xe3\x33\xd9\xee\xd7\xf2\xd9\x19\xea\x18\xc9\xc1\x59\x3a\x18\xfb\x28\x2d\xad\x4e\x82\x06\x65\xd5\x1f\x30\xa2\x1d\x56\xf8\xbe\x30\xc1\x98\x35\x01\xf8\xd2\x7e\x5c\xa6\xa5\xb5\x29\x26\xf6\x98\x56\x80\x6c\xe4\x03\xf8\x03\x04\x00\x73\x9a\x5e\xec\x85\x00\xf4\x2b\x0b\x00\xe1\x3a\x47\xf2\x70\x96\x0e\xc4\x3c\x42\x8b\xab\x13\xa0\x81\xd0\xb4\x2e\x00\xab\xd8\xaa\x09\xf6\xc7\x3c\xac\x35\x41\x09\xe6\xf4\x05\xab\xf7\x6b\x23\x13\x9c\x09\x34\x32\xc1\x17\x3a\x13\xe4\xc3\x04\x10\xde\xae\x09\x22\x30\x29\xb6\xe6\x84\x13\xc2\x09\xcf\x72\xda\x09\xfb\x27\x2b\x2d\x3b\x61\x8b\x70\x42\x29\x66\x77\xc2\x30\xc0\x66\x18\x22\x5d\x0b\x01\x10\x86\x92\x41\x22\xba\x73\x0f\x12\xd1\xed\x06\x89\x48\x7a\x5a\x9b\x8a\xe5\x3e\x2c\xe4\x36\x1e\x35\xbb\x50\xdd\x15\x4a\x80\x7d\xce\xa4\xe2\xc8\x7b\x6d\xa4\xe2\xc3\xc2\x01\x07\xc0\xdb\xa4\x18\x2d\xa1\x93\x31\xba\x10\x53\xfa\x25\xb6\x50\x60\x10\x19\x76\x99\x23\x7c\x47\x67\x9b\x09\x10\x57\xf6\x8d\x49\x31\xba\x92\xd6\x36\x17\x45\x12\xfa\xd9\xa8\xf3\x55\x54\x65\x0a\x1b\x95\x9d\x81\x66\xe5\x18\xa5\x75\x6d\x63\x81\x86\xa6\xeb\xec\x09\x80\x34\xcb\x67\x17\xa1\x39\xfa\xc6\xf7\x3c\xa3\xbd\xf2\x0e\x7f\x02\x80\x97\x59\xc7\xac\x18\x34\x24\x68\xa3\x76\xba\x21\x09\xcc\x7b\xcd\xb4\x21\xb1\xd8\x92\x25\x68\xe3\x93\xdc\xd3\x5f\xda\x31\xe6\xae\x69\xcf\x83\xa6\x70\xbc\x24\xf0\xb2\xda\x94\xa2\x71\x14\x42\x40\x13\xdb\xff\xf3\xd7\x0d\xfa\x41\xb9\xc5\x6e\x7b\x8e\xd6\x59\x08\x01\x75\xc1\x27\x7e\x16\x8e\xe9\x04\xa2\xfb\x41\x2b\xc7\x34\x0c\x98\xab\xd7\x3a\xfc\x30\xd1\x76\xaf\x24\xa2\x23\xb7\xf1\x08\xfd\x6d\x21\x4f\x58\x68\x38\x10\x6a\x7c\x67\xd1\xe0\x61\xb2\x99\x04\x9a\x5b\x79\x9a\xbd\x6b\xf2\x34\x43\x24\xa0\x9e\x23\x9f\xa3\xa8\x00\x31\xc6\x1a\x22\xc0\xe4\x69\xa6\xcc\x30\xf3\xf7\xb7\xf5\x58\x45\xb8\xe0\xa1\xc9\xc2\x0c\x90\x83\x80\x24\x83\x38\xdf\xd6\xe3\xd4\x82\x46\x4e\x47\x0f\x87\x36\x8a\xbf\x31\xa8\x64\x28\xa7\x40\x8c\x51\x58\x90\xdb\x19\x9f\xc5\x59\x47\xe9\x9e\x00\xa5\x79\x33\x5d\x9a\x4a\xe1\x22\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82")
func init() {
f, err := FS.OpenFile(CTX, "/favicon-32x32.png", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
log.Fatal(err)
}
_, err = f.Write(FileFavicon32x32Png)
if err != nil {
log.Fatal(err)
}
err = f.Close()
if err != nil {
log.Fatal(err)
}
}

29
vendor/github.com/swaggo/files/b0xfile__index.html.go generated vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
// Code generaTed by fileb0x at "2017-11-26 17:57:18.489607614 +0600 +06 m=+4.245926076" from config file "b0x.yml" DO NOT EDIT.
package swaggerFiles
import (
"log"
"os"
)
// FileSwaggerUICSSMap is "/swagger-ui.css.map"
var FileSwaggerUICSSMap = []byte("\x7b\x22\x76\x65\x72\x73\x69\x6f\x6e\x22\x3a\x33\x2c\x22\x73\x6f\x75\x72\x63\x65\x73\x22\x3a\x5b\x5d\x2c\x22\x6e\x61\x6d\x65\x73\x22\x3a\x5b\x5d\x2c\x22\x6d\x61\x70\x70\x69\x6e\x67\x73\x22\x3a\x22\x22\x2c\x22\x66\x69\x6c\x65\x22\x3a\x22\x73\x77\x61\x67\x67\x65\x72\x2d\x75\x69\x2e\x63\x73\x73\x22\x2c\x22\x73\x6f\x75\x72\x63\x65\x52\x6f\x6f\x74\x22\x3a\x22\x22\x7d")
func init() {
f, err := FS.OpenFile(CTX, "/swagger-ui.css.map", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
if err != nil {
log.Fatal(err)
}
_, err = f.Write(FileSwaggerUICSSMap)
if err != nil {
log.Fatal(err)
}
err = f.Close()
if err != nil {
log.Fatal(err)
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
vendor/github.com/swaggo/swag/.gitignore generated vendored Normal file
View File

@ -0,0 +1,14 @@
swag
testdata/simple/docs
cover.out
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.idea
.vscode
# Etc
.DS_Store

1
vendor/github.com/swaggo/swag/.golint_exclude generated vendored Normal file
View File

@ -0,0 +1 @@
^example

17
vendor/github.com/swaggo/swag/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,17 @@
language: go
go:
- 1.9.x
- 1.10.x
- 1.11.x
before_install:
- make deps
script:
- make lint
- make build
- make test
after_success:
- bash <(curl -s https://codecov.io/bash)

46
vendor/github.com/swaggo/swag/CODE_OF_CONDUCT.md generated vendored Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [gitter.im/swaggo/swag](https://gitter.im/swaggo/swag).The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

16
vendor/github.com/swaggo/swag/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,16 @@
# Contributing
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.
Please note we have a code of conduct, please follow it in all your interactions with the project.
## Pull Request Process
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
Please make an issue first if the change is likely to increase.

43
vendor/github.com/swaggo/swag/Makefile generated vendored Normal file
View File

@ -0,0 +1,43 @@
#! /usr/bin/make
GOCMD=$(shell which go)
GOLINT=$(shell which golint)
GOIMPORT=$(shell which goimports)
GOBUILD=$(GOCMD) build
GOCLEAN=$(GOCMD) clean
GOTEST=$(GOCMD) test
GOGET=$(GOCMD) get
GOLIST=$(GOCMD) list
BINARY_NAME=swag
all: test build
build:
$(GOBUILD) -o $(BINARY_NAME) -v ./cmd/...
test:
$(GOTEST) -v ./...
clean:
$(GOCLEAN)
rm -f $(BINARY_NAME)
DIRS=$(shell $(GOLIST) -f {{.Dir}} ./...)
lint:
@for d in $(DIRS) ; do \
if [ "`$(GOIMPORT) -l $$d/*.go | tee /dev/stderr`" ]; then \
echo "^ - Repo contains improperly formatted go files" && echo && exit 1; \
fi \
done
@if [ "`$(GOLINT) ./... | grep -vf .golint_exclude | tee /dev/stderr`" ]; then \
echo "^ - Lint errors!" && echo && exit 1; \
fi
deps:
$(GOGET) -v ./...
$(GOGET) github.com/stretchr/testify/assert
$(GOGET) golang.org/x/lint/golint
$(GOGET) golang.org/x/tools/cmd/goimports
view-covered:
$(GOTEST) -coverprofile=cover.out $(TARGET)
$(GOCMD) tool cover -html=cover.out

View File

@ -0,0 +1,8 @@
**Describe the PR**
e.g. add cool parser.
**Relation issue**
e.g. https://github.com/swaggo/swag/pull/118/files
**Additional context**
Add any other context about the problem here.

527
vendor/github.com/swaggo/swag/README.md generated vendored Normal file
View File

@ -0,0 +1,527 @@
# swag
<p align="center">
<img alt="swaggo" src="https://raw.githubusercontent.com/swaggo/swag/master/assets/swaggo.png" width="200">
</p>
<p align="center">
Automatically generate RESTful API documentation with Swagger 2.0 for Go.
</p>
<p align="center">
<a href="https://travis-ci.org/swaggo/swag"><img alt="Travis Status" src="https://img.shields.io/travis/swaggo/swag/master.svg"></a>
<a href="https://codecov.io/gh/swaggo/swag"><img alt="Coverage Status" src="https://img.shields.io/codecov/c/github/swaggo/swag/master.svg"></a>
<a href="https://goreportcard.com/badge/github.com/swaggo/swag"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/swaggo/swag"></a>
<a href="https://codebeat.co/projects/github-com-swaggo-swag-master"><img alt="codebeat badge" src="https://codebeat.co/badges/71e2f5e5-9e6b-405d-baf9-7cc8b5037330" /></a>
<a href="https://godoc.org/github.com/swaggo/swag"><img alt="Go Doc" src="https://godoc.org/github.com/swaggo/swagg?status.svg"></a>
</p>
<p align="center">gopher image source is <a href="https://github.com/tenntenn/gopher-stickers">tenntenn/gopher-stickers.</a> It has licenses <a href="http://creativecommons.org/licenses/by/3.0/deed.en">creative commons licensing.</a></p>
## Content
- [Getting started](#getting-started)
- [Go web frameworks](#supported-web-frameworks)
- [Supported Web Frameworks](#supported-web-frameworks)
- [How to use it with `gin`?](#how-to-use-it-with-`gin`?)
- [Implementation Status](#implementation-status)
- [swag cli](#swag-cli)
- [General API Info](#general-api-info)
- [Security](#security)
- [API Operation](#api-operation)
- [TIPS](#tips)
- [User defined structure with an array type](#user-defined-structure-with-an-array-type)
- [Use multiple path params](#use-multiple-path-params)
- [Example value of struct](#example-value-of-struct)
- [Description of struct](#description-of-struct)
- [About the Project](#about-the-project)
## Summary
Swag converts Go annotations to Swagger Documentation 2.0. We've created a variety of plugins for popular [Go web frameworks](#supported-web-frameworks). This allows you to quickly integrate with an existing Go project (using Swagger UI).
## Examples
[swaggo + gin](https://github.com/swaggo/swag/tree/master/example)
## Getting started
1. Add comments to your API source code, [See Declarative Comments Format](#general-api-info).
2. Download swag by using:
```sh
$ go get -u github.com/swaggo/swag/cmd/swag
```
3. Run `swag init` in the project's root folder which contains the `main.go` file. This will parse your comments and generate the required files (`docs` folder and `docs/docs.go`).
```sh
$ swag init
```
4. In order to serve these files, you can utilize one of our supported plugins. For go's core library, check out [net/http](https://github.com/swaggo/http-swagger).
* Make sure to import the generated `docs/docs.go` so that your specific configuration gets `init`'ed.
* If your General API annotation do not live in `main.go`, you can let swag know with `-g`.
```sh
swag init -g http/api.go
```
## Supported Web Frameworks
- [gin](http://github.com/swaggo/gin-swagger)
- [echo](http://github.com/swaggo/echo-swagger)
- [net/http](https://github.com/swaggo/http-swagger)
## How to use it with `gin`?
Find the example source code [here](https://github.com/swaggo/swag/tree/master/example/celler).
1. After using `swag init` to generate Swagger 2.0 docs, import the following packages:
```go
import "github.com/swaggo/gin-swagger" // gin-swagger middleware
import "github.com/swaggo/gin-swagger/swaggerFiles" // swagger embed files
```
2. Add [General API](#general-api-info) annotations in `main.go` code:
```go
// @title Swagger Example API
// @version 1.0
// @description This is a sample server celler 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
// @host localhost:8080
// @BasePath /api/v1
// @securityDefinitions.basic BasicAuth
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.implicit OAuth2Implicit
// @authorizationurl https://example.com/oauth/authorize
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.password OAuth2Password
// @tokenUrl https://example.com/oauth/token
// @scope.read Grants read access
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
// @securitydefinitions.oauth2.accessCode OAuth2AccessCode
// @tokenUrl https://example.com/oauth/token
// @authorizationurl https://example.com/oauth/authorize
// @scope.admin Grants read and write access to administrative information
func main() {
r := gin.Default()
c := controller.NewController()
v1 := r.Group("/api/v1")
{
accounts := v1.Group("/accounts")
{
accounts.GET(":id", c.ShowAccount)
accounts.GET("", c.ListAccounts)
accounts.POST("", c.AddAccount)
accounts.DELETE(":id", c.DeleteAccount)
accounts.PATCH(":id", c.UpdateAccount)
accounts.POST(":id/images", c.UploadAccountImage)
}
//...
}
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run(":8080")
}
//...
```
Additionally some general API info can be set dynamically. The generated code package `docs` exports `SwaggerInfo` variable which we can use to set the title, description, version, host and base path programatically. Example using Gin:
```go
package main
import (
"github.com/gin-gonic/gin"
"github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"./docs" // docs is generated by Swag CLI, you have to import it.
)
// @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
// @termsOfService http://swagger.io/terms/
func main() {
// programatically set swagger info
docs.Title = "Swagger Example API"
docs.Description = "This is a sample server Petstore server."
docs.SwaggerInfo.Version = "1.0"
docs.SwaggerInfo.Host = "petstore.swagger.io"
docs.SwaggerInfo.BasePath = "/v2"
r := gin.New()
// use ginSwagger middleware to serve the API docs
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
r.Run()
}
```
3. Add [API Operation](#api-operation) annotations in `controller` code
``` go
package controller
import (
"fmt"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/swaggo/swag/example/celler/httputil"
"github.com/swaggo/swag/example/celler/model"
)
// ShowAccount godoc
// @Summary Show a account
// @Description get string by ID
// @ID get-string-by-int
// @Accept json
// @Produce json
// @Param id path int true "Account ID"
// @Success 200 {object} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts/{id} [get]
func (c *Controller) ShowAccount(ctx *gin.Context) {
id := ctx.Param("id")
aid, err := strconv.Atoi(id)
if err != nil {
httputil.NewError(ctx, http.StatusBadRequest, err)
return
}
account, err := model.AccountOne(aid)
if err != nil {
httputil.NewError(ctx, http.StatusNotFound, err)
return
}
ctx.JSON(http.StatusOK, account)
}
// ListAccounts godoc
// @Summary List accounts
// @Description get accounts
// @Accept json
// @Produce json
// @Param q query string false "name search by q"
// @Success 200 {array} model.Account
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /accounts [get]
func (c *Controller) ListAccounts(ctx *gin.Context) {
q := ctx.Request.URL.Query().Get("q")
accounts, err := model.AccountsAll(q)
if err != nil {
httputil.NewError(ctx, http.StatusNotFound, err)
return
}
ctx.JSON(http.StatusOK, accounts)
}
//...
```
```console
$ swag init
```
4.Run your app, and browse to http://localhost:8080/swagger/index.html. You will see Swagger 2.0 Api documents as shown below:
![swagger_index.html](https://raw.githubusercontent.com/swaggo/swag/master/assets/swagger-image.png)
## Implementation Status
[Swagger 2.0 document](https://swagger.io/docs/specification/2-0/basic-structure/)
- [x] Basic Structure
- [x] API Host and Base Path
- [x] Paths and Operations
- [x] Describing Parameters
- [x] Describing Request Body
- [x] Describing Responses
- [x] MIME Types
- [x] Authentication
- [x] Basic Authentication
- [x] API Keys
- [x] Adding Examples
- [x] File Upload
- [x] Enums
- [x] Grouping Operations With Tags
- [ ] Swagger Extensions
# swag cli
```console
$ swag init -h
NAME:
swag init - Create docs.go
USAGE:
swag init [command options] [arguments...]
OPTIONS:
--generalInfo value, -g value Go file path in which 'swagger general API Info' is written (default: "main.go")
--dir value, -d value Directory you want to parse (default: "./")
--swagger value, -s value Output the swagger conf for json and yaml (default: "./docs/swagger")
--propertyStrategy value, -p value Property Naming Strategy like snakecase,camelcase,pascalcase (default: "camelcase")
```
# General API Info
**Example**
[celler/main.go](https://github.com/swaggo/swag/blob/master/example/celler/main.go)
| annotation | description | example |
|--------------------|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------|
| title | **Required.** The title of the application. | // @title Swagger Example API |
| version | **Required.** Provides the version of the application API. | // @version 1.0 |
| description | A short description of the application. | // @description This is a sample server celler server. |
| termsOfService | The Terms of Service for the API. | // @termsOfService http://swagger.io/terms/ |
| contact.name | The contact information for the exposed API. | // @contact.name API Support |
| contact.url | The URL pointing to the contact information. MUST be in the format of a URL. | // @contact.url http://www.swagger.io/support |
| contact.email | The email address of the contact person/organization. MUST be in the format of an email address.| // @contact.email support@swagger.io |
| license.name | **Required.** The license name used for the API. | // @license.name Apache 2.0 |
| license.url | A URL to the license used for the API. MUST be in the format of a URL. | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html |
| host | The host (name or ip) serving the API. | // @host localhost:8080 |
| BasePath | The base path on which the API is served. | // @BasePath /api/v1 |
## Security
| 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 |
# API Operation
**Example**
[celler/controller](https://github.com/swaggo/swag/tree/master/example/celler/controller)
| annotation | description |
|--------------------|----------------------------------------------------------------------------------------------------------------------------|
| description | A verbose explanation of the operation behavior. |
| id | A unique string used to identify the operation. Must be unique among all API operations. |
| tags | A list of tags to each API operation that separated by commas. |
| summary | A short summary of what the operation does. |
| accept | A list of MIME types the APIs can consume. Value MUST be as described under [Mime Types](#mime-types). |
| produce | A list of MIME types the APIs can produce. Value MUST be as described under [Mime Types](#mime-types). |
| param | Parameters that separated by spaces. `param name`,`param type`,`data type`,`is mandatory?`,`comment` `attribute(optional)` |
| security | [Security](#security) to each API operation. |
| success | Success response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` |
| failure | Failure response that separated by spaces. `return code`,`{param type}`,`data type`,`comment` |
| router | Path definition that separated by spaces. `path`,`[httpMethod]` |
## Mime Types
| Mime Type | annotation |
|-----------------------------------|-----------------------------------------------------------|
| application/json | application/json, json |
| text/xml | text/xml, xml |
| text/plain | text/plain, plain |
| html | text/html, html |
| multipart/form-data | multipart/form-data, mpfd |
| application/x-www-form-urlencoded | application/x-www-form-urlencoded, x-www-form-urlencoded |
| application/vnd.api+json | application/vnd.api+json, json-api |
| application/x-json-stream | application/x-json-stream, json-stream |
| application/octet-stream | application/octet-stream, octet-stream |
| image/png | image/png, png |
| image/jpeg | image/jpeg, jpeg |
| image/gif | image/gif, gif |
## Security
General API info.
```go
// @securityDefinitions.basic BasicAuth
// @securitydefinitions.oauth2.application OAuth2Application
// @tokenUrl https://example.com/oauth/token
// @scope.write Grants write access
// @scope.admin Grants read and write access to administrative information
```
Each API operation.
```go
// @Security ApiKeyAuth
```
Make it AND condition
```go
// @Security ApiKeyAuth
// @Security OAuth2Application[write, admin]
```
## Param Type
- object (struct)
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- array
## Data Type
- string (string)
- integer (int, uint, uint32, uint64)
- number (float32)
- boolean (bool)
- user defined struct
## Attribute
```go
// @Param enumstring query string false "string enums" Enums(A, B, C)
// @Param enumint query int false "int enums" Enums(1, 2, 3)
// @Param enumnumber query number false "int enums" Enums(1.1, 1.2, 1.3)
// @Param string query string false "string valid" minlength(5) maxlength(10)
// @Param int query int false "int valid" mininum(1) maxinum(10)
// @Param default query string false "string default" default(A)
```
It also works for the struct fields:
```go
type Foo struct {
Bar string `minLength:"4" maxLength:"16"`
Baz int `minimum:"10" maximum:"20" default:"15"`
Qux []string `enums:"foo,bar,baz"`
}
```
### Available
Field Name | Type | Description
---|:---:|---
<a name="parameterDefault"></a>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.
<a name="parameterMaximum"></a>maximum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.2.
<a name="parameterMinimum"></a>minimum | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.3.
<a name="parameterMaxLength"></a>maxLength | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.1.
<a name="parameterMinLength"></a>minLength | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.2.
<a name="parameterEnums"></a>enums | [\*] | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1.
### Future
Field Name | Type | Description
---|:---:|---
<a name="parameterFormat"></a>format | `string` | The extending format for the previously mentioned [`type`](#parameterType). See [Data Type Formats](#dataTypeFormat) for further details.
<a name="parameterMultipleOf"></a>multipleOf | `number` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.1.1.
<a name="parameterPattern"></a>pattern | `string` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
<a name="parameterMaxItems"></a>maxItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.2.
<a name="parameterMinItems"></a>minItems | `integer` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.3.
<a name="parameterUniqueItems"></a>uniqueItems | `boolean` | See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.3.4.
<a name="parameterCollectionFormat"></a>collectionFormat | `string` | Determines the format of the array if type array is used. Possible values are: <ul><li>`csv` - comma separated values `foo,bar`. <li>`ssv` - space separated values `foo bar`. <li>`tsv` - tab separated values `foo\tbar`. <li>`pipes` - pipe separated values <code>foo&#124;bar</code>. <li>`multi` - corresponds to multiple parameter instances instead of multiple values for a single instance `foo=bar&foo=baz`. This is valid only for parameters [`in`](#parameterIn) "query" or "formData". </ul> Default value is `csv`.
## TIPS
### User defined structure with an array type
```go
// @Success 200 {array} model.Account <-- This is a user defined struct.
```
```go
package model
type Account struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"account name"`
}
```
### Use multiple path params
```go
/// ...
// @Param group_id path int true "Group ID"
// @Param account_id path int true "Account ID"
// ...
// @Router /examples/groups/{group_id}/accounts/{account_id} [get]
```
### Example value of struct
```go
type Account struct {
ID int `json:"id" example:"1"`
Name string `json:"name" example:"account name"`
PhotoUrls []string `json:"photo_urls" example:"http://test/image/1.jpg,http://test/image/2.jpg"`
}
```
### Description of struct
```go
type Account struct {
// ID this is userid
ID int `json:"id"
}
```
### Override swagger type of a struct field
```go
type Account struct {
// Override primitive type by simply specifying it via `swaggertype` tag
ID sql.NullInt64 `json:"id" swaggertype:"integer"`
// Array types can be overridden using "array,<prim_type>" format
Coeffs []big.Float `json:"coeffs" swaggertype:"array,number"`
}
```
## About the Project
This project was inspired by [yvasiyarov/swagger](https://github.com/yvasiyarov/swagger) but we simplified the usage and added support a variety of [web frameworks](#supported-web-frameworks).

5
vendor/github.com/swaggo/swag/doc.go generated vendored Normal file
View File

@ -0,0 +1,5 @@
/*
Package swag converts Go annotations to Swagger Documentation 2.0.
See https://github.com/swaggo/swag for more information about swag.
*/
package swag // import "github.com/swaggo/swag"

126
vendor/github.com/swaggo/swag/gen/gen.go generated vendored Normal file
View File

@ -0,0 +1,126 @@
package gen
import (
"encoding/json"
"log"
"os"
"path"
"text/template"
"time"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
"github.com/swaggo/swag"
)
// Gen presents a generate tool for swag.
type Gen struct {
}
// New creates a new Gen.
func New() *Gen {
return &Gen{}
}
// Build builds swagger json file for gived searchDir and mainAPIFile. Returns json
func (g *Gen) Build(searchDir, mainAPIFile, swaggerConfDir, propNamingStrategy string) error {
log.Println("Generate swagger docs....")
p := swag.New()
p.PropNamingStrategy = propNamingStrategy
p.ParseAPI(searchDir, mainAPIFile)
swagger := p.GetSwagger()
b, err := json.MarshalIndent(swagger, "", " ")
if err != nil {
return err
}
os.MkdirAll(path.Join(searchDir, "docs"), os.ModePerm)
docs, err := os.Create(path.Join(searchDir, "docs", "docs.go"))
if err != nil {
return err
}
defer docs.Close()
os.Mkdir(swaggerConfDir, os.ModePerm)
swaggerJSON, err := os.Create(path.Join(swaggerConfDir, "swagger.json"))
if err != nil {
return err
}
defer swaggerJSON.Close()
swaggerJSON.Write(b)
swaggerYAML, err := os.Create(path.Join(swaggerConfDir, "swagger.yaml"))
if err != nil {
return err
}
defer swaggerYAML.Close()
y, err := yaml.JSONToYAML(b)
if err != nil {
return errors.Wrap(err, "cannot covert json to yaml")
}
swaggerYAML.Write(y)
if err := packageTemplate.Execute(docs, struct {
Timestamp time.Time
Doc string
}{
Timestamp: time.Now(),
Doc: "`" + string(b) + "`",
}); err != nil {
return err
}
log.Printf("create docs.go at %+v", docs.Name())
return nil
}
var packageTemplate = template.Must(template.New("").Parse(`// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
// This file was generated by swaggo/swag at
// {{ .Timestamp }}
package docs
import (
"bytes"
"github.com/alecthomas/template"
"github.com/swaggo/swag"
)
var doc = {{.Doc}}
type swaggerInfo struct {
Version string
Host string
BasePath string
Title string
Description string
}
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo swaggerInfo
type s struct{}
func (s *s) ReadDoc() string {
t, err := template.New("swagger_info").Parse(doc)
if err != nil {
return doc
}
var tpl bytes.Buffer
if err := t.Execute(&tpl, SwaggerInfo); err != nil {
return doc
}
return tpl.String()
}
func init() {
swag.Register(swag.Name, &s{})
}
`))

21
vendor/github.com/swaggo/swag/license generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Eason Lin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

674
vendor/github.com/swaggo/swag/operation.go generated vendored Normal file
View File

@ -0,0 +1,674 @@
package swag
import (
"fmt"
"go/ast"
goparser "go/parser"
"go/token"
"log"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"github.com/go-openapi/jsonreference"
"github.com/go-openapi/spec"
"github.com/pkg/errors"
"golang.org/x/tools/go/loader"
)
// Operation describes a single API operation on a path.
// For more information: https://github.com/swaggo/swag#api-operation
type Operation struct {
HTTPMethod string
Path string
spec.Operation
parser *Parser
}
// NewOperation creates a new Operation with default properties.
// map[int]Response
func NewOperation() *Operation {
return &Operation{
HTTPMethod: "get",
Operation: spec.Operation{
OperationProps: spec.OperationProps{},
},
}
}
// ParseComment parses comment for given comment string and returns error if error occurs.
func (operation *Operation) ParseComment(comment string, astFile *ast.File) error {
commentLine := strings.TrimSpace(strings.TrimLeft(comment, "//"))
if len(commentLine) == 0 {
return nil
}
attribute := strings.Fields(commentLine)[0]
lineRemainder := strings.TrimSpace(commentLine[len(attribute):])
switch strings.ToLower(attribute) {
case "@description":
if operation.Description == "" {
operation.Description = lineRemainder
} else {
operation.Description += "<br>" + lineRemainder
}
case "@summary":
operation.Summary = lineRemainder
case "@id":
operation.ID = lineRemainder
case "@tags":
operation.ParseTagsComment(lineRemainder)
case "@accept":
if err := operation.ParseAcceptComment(lineRemainder); err != nil {
return err
}
case "@produce":
if err := operation.ParseProduceComment(lineRemainder); err != nil {
return err
}
case "@param":
if err := operation.ParseParamComment(lineRemainder, astFile); err != nil {
return err
}
case "@success", "@failure":
if err := operation.ParseResponseComment(lineRemainder, astFile); err != nil {
if err := operation.ParseEmptyResponseComment(lineRemainder); err != nil {
if err := operation.ParseEmptyResponseOnly(lineRemainder); err != nil {
return err
}
}
}
case "@router":
if err := operation.ParseRouterComment(strings.TrimSpace(commentLine[len(attribute):])); err != nil {
return err
}
case "@security":
if err := operation.ParseSecurityComment(strings.TrimSpace(commentLine[len(attribute):])); err != nil {
return err
}
}
return nil
}
// ParseParamComment parses params return []string of param properties
// @Param queryText form string true "The email for login"
// [param name] [paramType] [data type] [is mandatory?] [Comment]
// @Param some_id path int true "Some ID"
func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.File) error {
re := regexp.MustCompile(`([-\w]+)[\s]+([\w]+)[\s]+([\S.]+)[\s]+([\w]+)[\s]+"([^"]+)"`)
matches := re.FindStringSubmatch(commentLine)
if len(matches) != 6 {
return fmt.Errorf("can not parse param comment \"%s\"", commentLine)
}
name := matches[1]
paramType := matches[2]
schemaType := matches[3]
requiredText := strings.ToLower(matches[4])
required := requiredText == "true" || requiredText == "required"
description := matches[5]
var param spec.Parameter
//five possible parameter types.
switch paramType {
case "query", "path", "header":
param = createParameter(paramType, description, name, TransToValidSchemeType(schemaType), required)
case "body":
param = createParameter(paramType, description, name, "object", required) // TODO: if Parameter types can be objects, but also primitives and arrays
// TODO: this snippets have to extract out
refSplit := strings.Split(schemaType, ".")
if len(refSplit) == 2 {
pkgName := refSplit[0]
typeName := refSplit[1]
if typeSpec, ok := operation.parser.TypeDefinitions[pkgName][typeName]; ok {
operation.parser.registerTypes[schemaType] = typeSpec
} else {
var typeSpec *ast.TypeSpec
if astFile != nil {
for _, imp := range astFile.Imports {
if imp.Name != nil && imp.Name.Name == pkgName { // the import had an alias that matched
break
}
impPath := strings.Replace(imp.Path.Value, `"`, ``, -1)
if strings.HasSuffix(impPath, "/"+pkgName) {
var err error
typeSpec, err = findTypeDef(impPath, typeName)
if err != nil {
return errors.Wrapf(err, "can not find ref type: %q", schemaType)
}
break
}
}
}
if typeSpec == nil {
return fmt.Errorf("can not find ref type:\"%s\"", schemaType)
}
operation.parser.TypeDefinitions[pkgName][typeName] = typeSpec
operation.parser.registerTypes[schemaType] = typeSpec
}
param.Schema.Ref = spec.Ref{
Ref: jsonreference.MustCreateRef("#/definitions/" + schemaType),
}
}
case "formData":
param = createParameter(paramType, description, name, TransToValidSchemeType(schemaType), required)
}
param = operation.parseAndExtractionParamAttribute(commentLine, schemaType, param)
operation.Operation.Parameters = append(operation.Operation.Parameters, param)
return nil
}
var regexAttributes = map[string]*regexp.Regexp{
// for Enums(A, B)
"enums": regexp.MustCompile(`(?i)enums\(.*\)`),
// for Minimum(0)
"maxinum": regexp.MustCompile(`(?i)maxinum\(.*\)`),
// for Maximum(0)
"mininum": regexp.MustCompile(`(?i)mininum\(.*\)`),
// for Maximum(0)
"default": regexp.MustCompile(`(?i)default\(.*\)`),
// for minlength(0)
"minlength": regexp.MustCompile(`(?i)minlength\(.*\)`),
// for maxlength(0)
"maxlength": regexp.MustCompile(`(?i)maxlength\(.*\)`),
// for format(email)
"format": regexp.MustCompile(`(?i)format\(.*\)`),
}
func (operation *Operation) parseAndExtractionParamAttribute(commentLine, schemaType string, param spec.Parameter) spec.Parameter {
schemaType = TransToValidSchemeType(schemaType)
for attrKey, re := range regexAttributes {
switch attrKey {
case "enums":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
enums := strings.Split(attr[l+1:r], ",")
for _, e := range enums {
e = strings.TrimSpace(e)
param.Enum = append(param.Enum, defineType(schemaType, e))
}
}
case "maxinum":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
if schemaType != "integer" && schemaType != "number" {
log.Panicf("maxinum is attribute to set to a number. comment=%s got=%s", commentLine, schemaType)
}
attr = strings.TrimSpace(attr[l+1 : r])
n, err := strconv.ParseFloat(attr, 64)
if err != nil {
log.Panicf("maximum is allow only a number. comment=%s got=%s", commentLine, attr)
}
param.Maximum = &n
}
case "mininum":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
if schemaType != "integer" && schemaType != "number" {
log.Panicf("mininum is attribute to set to a number. comment=%s got=%s", commentLine, schemaType)
}
attr = strings.TrimSpace(attr[l+1 : r])
n, err := strconv.ParseFloat(attr, 64)
if err != nil {
log.Panicf("mininum is allow only a number got=%s", attr)
}
param.Minimum = &n
}
case "default":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
attr = strings.TrimSpace(attr[l+1 : r])
param.Default = defineType(schemaType, attr)
}
case "maxlength":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
if schemaType != "string" {
log.Panicf("maxlength is attribute to set to a number. comment=%s got=%s", commentLine, schemaType)
}
attr = strings.TrimSpace(attr[l+1 : r])
n, err := strconv.ParseInt(attr, 10, 64)
if err != nil {
log.Panicf("maxlength is allow only a number got=%s", attr)
}
param.MaxLength = &n
}
case "minlength":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
if schemaType != "string" {
log.Panicf("maxlength is attribute to set to a number. comment=%s got=%s", commentLine, schemaType)
}
attr = strings.TrimSpace(attr[l+1 : r])
n, err := strconv.ParseInt(attr, 10, 64)
if err != nil {
log.Panicf("minlength is allow only a number got=%s", attr)
}
param.MinLength = &n
}
case "format":
attr := re.FindString(commentLine)
l := strings.Index(attr, "(")
r := strings.Index(attr, ")")
if !(l == -1 && r == -1) {
param.Format = strings.TrimSpace(attr[l+1 : r])
}
}
}
return param
}
// defineType enum value define the type (object and array unsupported)
func defineType(schemaType string, value string) interface{} {
schemaType = TransToValidSchemeType(schemaType)
switch schemaType {
case "string":
return value
case "number":
v, err := strconv.ParseFloat(value, 64)
if err != nil {
panic(fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err))
}
return v
case "integer":
v, err := strconv.Atoi(value)
if err != nil {
panic(fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err))
}
return v
case "boolean":
v, err := strconv.ParseBool(value)
if err != nil {
panic(fmt.Errorf("enum value %s can't convert to %s err: %s", value, schemaType, err))
}
return v
default:
panic(fmt.Errorf("%s is unsupported type in enum value", schemaType))
}
}
// ParseTagsComment parses comment for given `tag` comment string.
func (operation *Operation) ParseTagsComment(commentLine string) {
tags := strings.Split(commentLine, ",")
for _, tag := range tags {
operation.Tags = append(operation.Tags, strings.TrimSpace(tag))
}
}
// ParseAcceptComment parses comment for given `accept` comment string.
func (operation *Operation) ParseAcceptComment(commentLine string) error {
accepts := strings.Split(commentLine, ",")
for _, a := range accepts {
switch a {
case "json", "application/json":
operation.Consumes = append(operation.Consumes, "application/json")
case "xml", "text/xml":
operation.Consumes = append(operation.Consumes, "text/xml")
case "plain", "text/plain":
operation.Consumes = append(operation.Consumes, "text/plain")
case "html", "text/html":
operation.Consumes = append(operation.Consumes, "text/html")
case "mpfd", "multipart/form-data":
operation.Consumes = append(operation.Consumes, "multipart/form-data")
case "x-www-form-urlencoded", "application/x-www-form-urlencoded":
operation.Consumes = append(operation.Consumes, "application/x-www-form-urlencoded")
case "json-api", "application/vnd.api+json":
operation.Consumes = append(operation.Consumes, "application/vnd.api+json")
case "json-stream", "application/x-json-stream":
operation.Consumes = append(operation.Consumes, "application/x-json-stream")
case "octet-stream", "application/octet-stream":
operation.Consumes = append(operation.Consumes, "application/octet-stream")
case "png", "image/png":
operation.Consumes = append(operation.Consumes, "image/png")
case "jpeg", "image/jpeg":
operation.Consumes = append(operation.Consumes, "image/jpeg")
case "gif", "image/gif":
operation.Consumes = append(operation.Consumes, "image/gif")
default:
return fmt.Errorf("%v accept type can't accepted", a)
}
}
return nil
}
// ParseProduceComment parses comment for gived `produce` comment string.
func (operation *Operation) ParseProduceComment(commentLine string) error {
produces := strings.Split(commentLine, ",")
for _, a := range produces {
switch a {
case "json", "application/json":
operation.Produces = append(operation.Produces, "application/json")
case "xml", "text/xml":
operation.Produces = append(operation.Produces, "text/xml")
case "plain", "text/plain":
operation.Produces = append(operation.Produces, "text/plain")
case "html", "text/html":
operation.Produces = append(operation.Produces, "text/html")
case "mpfd", "multipart/form-data":
operation.Produces = append(operation.Produces, "multipart/form-data")
case "x-www-form-urlencoded", "application/x-www-form-urlencoded":
operation.Produces = append(operation.Produces, "application/x-www-form-urlencoded")
case "json-api", "application/vnd.api+json":
operation.Produces = append(operation.Produces, "application/vnd.api+json")
case "json-stream", "application/x-json-stream":
operation.Produces = append(operation.Produces, "application/x-json-stream")
case "octet-stream", "application/octet-stream":
operation.Produces = append(operation.Produces, "application/octet-stream")
case "png", "image/png":
operation.Produces = append(operation.Produces, "image/png")
case "jpeg", "image/jpeg":
operation.Produces = append(operation.Produces, "image/jpeg")
case "gif", "image/gif":
operation.Produces = append(operation.Produces, "image/gif")
default:
return fmt.Errorf("%v produce type can't accepted", a)
}
}
return nil
}
// ParseRouterComment parses comment for gived `router` comment string.
func (operation *Operation) ParseRouterComment(commentLine string) error {
re := regexp.MustCompile(`([\w\.\/\-{}\+]+)[^\[]+\[([^\]]+)`)
var matches []string
if matches = re.FindStringSubmatch(commentLine); len(matches) != 3 {
return fmt.Errorf("can not parse router comment \"%s\"", commentLine)
}
path := matches[1]
httpMethod := matches[2]
operation.Path = path
operation.HTTPMethod = strings.ToUpper(httpMethod)
return nil
}
// ParseSecurityComment parses comment for gived `security` comment string.
func (operation *Operation) ParseSecurityComment(commentLine string) error {
securitySource := commentLine[strings.Index(commentLine, "@Security")+1:]
l := strings.Index(securitySource, "[")
r := strings.Index(securitySource, "]")
// exists scope
if !(l == -1 && r == -1) {
scopes := securitySource[l+1 : r]
s := []string{}
for _, scope := range strings.Split(scopes, ",") {
scope = strings.TrimSpace(scope)
s = append(s, scope)
}
securityKey := securitySource[0:l]
securityMap := map[string][]string{}
securityMap[securityKey] = append(securityMap[securityKey], s...)
operation.Security = append(operation.Security, securityMap)
} else {
securityKey := strings.TrimSpace(securitySource)
securityMap := map[string][]string{}
securityMap[securityKey] = []string{}
operation.Security = append(operation.Security, securityMap)
}
return nil
}
// findTypeDef attempts to find the *ast.TypeSpec for a specific type given the
// type's name and the package's import path
func findTypeDef(importPath, typeName string) (*ast.TypeSpec, error) {
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
conf := loader.Config{
ParserMode: goparser.SpuriousErrors,
Cwd: cwd,
}
conf.Import(importPath)
lprog, err := conf.Load()
if err != nil {
return nil, err
}
// If the pkg is vendored, the actual pkg path is going to resemble
// something like "{importPath}/vendor/{importPath}"
for k := range lprog.AllPackages {
realPkgPath := k.Path()
if strings.Contains(realPkgPath, "vendor/"+importPath) {
importPath = realPkgPath
}
}
pkgInfo := lprog.Package(importPath)
if pkgInfo == nil {
return nil, errors.New("package was nil")
}
// TODO: possibly cache pkgInfo since it's an expensive operation
for i := range pkgInfo.Files {
for _, astDeclaration := range pkgInfo.Files[i].Decls {
if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE {
for _, astSpec := range generalDeclaration.Specs {
if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
if typeSpec.Name.String() == typeName {
return typeSpec, nil
}
}
}
}
}
}
return nil, errors.New("type spec not found")
}
// ParseResponseComment parses comment for gived `response` comment string.
func (operation *Operation) ParseResponseComment(commentLine string, astFile *ast.File) error {
re := regexp.MustCompile(`([\d]+)[\s]+([\w\{\}]+)[\s]+([\w\-\.\/]+)[^"]*(.*)?`)
var matches []string
if matches = re.FindStringSubmatch(commentLine); len(matches) != 5 {
return fmt.Errorf("can not parse response comment \"%s\"", commentLine)
}
response := spec.Response{}
code, _ := strconv.Atoi(matches[1])
responseDescription := strings.Trim(matches[4], "\"")
if responseDescription == "" {
responseDescription = http.StatusText(code)
}
response.Description = responseDescription
schemaType := strings.Trim(matches[2], "{}")
refType := matches[3]
if operation.parser != nil { // checking refType has existing in 'TypeDefinitions'
refSplit := strings.Split(refType, ".")
if len(refSplit) == 2 {
pkgName := refSplit[0]
typeName := refSplit[1]
if typeSpec, ok := operation.parser.TypeDefinitions[pkgName][typeName]; ok {
operation.parser.registerTypes[refType] = typeSpec
} else {
var typeSpec *ast.TypeSpec
if astFile != nil {
for _, imp := range astFile.Imports {
if imp.Name != nil && imp.Name.Name == pkgName { // the import had an alias that matched
break
}
impPath := strings.Replace(imp.Path.Value, `"`, ``, -1)
if strings.HasSuffix(impPath, "/"+pkgName) {
var err error
typeSpec, err = findTypeDef(impPath, typeName)
if err != nil {
return errors.Wrapf(err, "can not find ref type: %q", refType)
}
break
}
}
}
if typeSpec == nil {
return fmt.Errorf("can not find ref type: %q", refType)
}
if _, ok := operation.parser.TypeDefinitions[pkgName]; !ok {
operation.parser.TypeDefinitions[pkgName] = make(map[string]*ast.TypeSpec)
}
operation.parser.TypeDefinitions[pkgName][typeName] = typeSpec
operation.parser.registerTypes[refType] = typeSpec
}
}
}
// so we have to know all type in app
//TODO: we might omitted schema.type if schemaType equals 'object'
response.Schema = &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{schemaType}}}
if schemaType == "object" {
response.Schema.Ref = spec.Ref{
Ref: jsonreference.MustCreateRef("#/definitions/" + refType),
}
}
if schemaType == "array" {
refType = TransToValidSchemeType(refType)
if IsPrimitiveType(refType) {
response.Schema.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: spec.StringOrArray{refType},
},
},
}
} else {
response.Schema.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.Ref{Ref: jsonreference.MustCreateRef("#/definitions/" + refType)},
},
},
}
}
}
if operation.Responses == nil {
operation.Responses = &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: make(map[int]spec.Response),
},
}
}
operation.Responses.StatusCodeResponses[code] = response
return nil
}
// ParseEmptyResponseComment parse only comment out status code and description,eg: @Success 200 "it's ok"
func (operation *Operation) ParseEmptyResponseComment(commentLine string) error {
re := regexp.MustCompile(`([\d]+)[\s]+"(.*)"`)
var matches []string
if matches = re.FindStringSubmatch(commentLine); len(matches) != 3 {
return fmt.Errorf("can not parse response comment \"%s\"", commentLine)
}
response := spec.Response{}
code, _ := strconv.Atoi(matches[1])
response.Description = strings.Trim(matches[2], "")
if operation.Responses == nil {
operation.Responses = &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: make(map[int]spec.Response),
},
}
}
operation.Responses.StatusCodeResponses[code] = response
return nil
}
//ParseEmptyResponseOnly parse only comment out status code ,eg: @Success 200
func (operation *Operation) ParseEmptyResponseOnly(commentLine string) error {
response := spec.Response{}
code, err := strconv.Atoi(commentLine)
if err != nil {
return fmt.Errorf("can not parse response comment \"%s\"", commentLine)
}
if operation.Responses == nil {
operation.Responses = &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: make(map[int]spec.Response),
},
}
}
operation.Responses.StatusCodeResponses[code] = response
return nil
}
// createParameter returns swagger spec.Parameter for gived paramType, description, paramName, schemaType, required
func createParameter(paramType, description, paramName, schemaType string, required bool) spec.Parameter {
// //five possible parameter types. query, path, body, header, form
paramProps := spec.ParamProps{
Name: paramName,
Description: description,
Required: required,
In: paramType,
}
if paramType == "body" {
paramProps.Schema = &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{schemaType},
},
}
parameter := spec.Parameter{
ParamProps: paramProps,
}
return parameter
}
parameter := spec.Parameter{
ParamProps: paramProps,
SimpleSchema: spec.SimpleSchema{
Type: schemaType,
},
}
return parameter
}

892
vendor/github.com/swaggo/swag/parser.go generated vendored Normal file
View File

@ -0,0 +1,892 @@
package swag
import (
"fmt"
"go/ast"
goparser "go/parser"
"go/token"
"log"
"net/http"
"os"
"path"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
"unicode"
"github.com/go-openapi/jsonreference"
"github.com/go-openapi/spec"
"github.com/pkg/errors"
)
const (
// CamelCase indicates using CamelCase strategy for struct field.
CamelCase = "camelcase"
// PascalCase indicates using PascalCase strategy for struct field.
PascalCase = "pascalcase"
// SnakeCase indicates using SnakeCase strategy for struct field.
SnakeCase = "snakecase"
)
// Parser implements a parser for Go source files.
type Parser struct {
// swagger represents the root document object for the API specification
swagger *spec.Swagger
//files is a map that stores map[real_go_file_path][astFile]
files map[string]*ast.File
// TypeDefinitions is a map that stores [package name][type name][*ast.TypeSpec]
TypeDefinitions map[string]map[string]*ast.TypeSpec
// CustomPrimitiveTypes is a map that stores custom primitive types to actual golang types [type name][string]
CustomPrimitiveTypes map[string]string
//registerTypes is a map that stores [refTypeName][*ast.TypeSpec]
registerTypes map[string]*ast.TypeSpec
PropNamingStrategy string
}
// New creates a new Parser with default properties.
func New() *Parser {
parser := &Parser{
swagger: &spec.Swagger{
SwaggerProps: spec.SwaggerProps{
Info: &spec.Info{
InfoProps: spec.InfoProps{
Contact: &spec.ContactInfo{},
License: &spec.License{},
},
},
Paths: &spec.Paths{
Paths: make(map[string]spec.PathItem),
},
Definitions: make(map[string]spec.Schema),
},
},
files: make(map[string]*ast.File),
TypeDefinitions: make(map[string]map[string]*ast.TypeSpec),
CustomPrimitiveTypes: make(map[string]string),
registerTypes: make(map[string]*ast.TypeSpec),
}
return parser
}
// ParseAPI parses general api info for gived searchDir and mainAPIFile
func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
log.Println("Generate general API Info")
if err := parser.getAllGoFileInfo(searchDir); err != nil {
return err
}
parser.ParseGeneralAPIInfo(path.Join(searchDir, mainAPIFile))
for _, astFile := range parser.files {
parser.ParseType(astFile)
}
for _, astFile := range parser.files {
parser.ParseRouterAPIInfo(astFile)
}
parser.ParseDefinitions()
return nil
}
// ParseGeneralAPIInfo parses general api info for gived mainAPIFile path
func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error {
fileSet := token.NewFileSet()
fileTree, err := goparser.ParseFile(fileSet, mainAPIFile, nil, goparser.ParseComments)
if err != nil {
return errors.Wrap(err, "cannot parse soure files")
}
parser.swagger.Swagger = "2.0"
securityMap := map[string]*spec.SecurityScheme{}
// templated defaults
parser.swagger.Info.Version = "{{.Version}}"
parser.swagger.Info.Title = "{{.Title}}"
parser.swagger.Info.Description = "{{.Description}}"
parser.swagger.Host = "{{.Host}}"
parser.swagger.BasePath = "{{.BasePath}}"
if fileTree.Comments != nil {
for _, comment := range fileTree.Comments {
comments := strings.Split(comment.Text(), "\n")
for _, commentLine := range comments {
attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
switch attribute {
case "@version":
parser.swagger.Info.Version = strings.TrimSpace(commentLine[len(attribute):])
case "@title":
parser.swagger.Info.Title = strings.TrimSpace(commentLine[len(attribute):])
case "@description":
parser.swagger.Info.Description = strings.TrimSpace(commentLine[len(attribute):])
case "@termsofservice":
parser.swagger.Info.TermsOfService = strings.TrimSpace(commentLine[len(attribute):])
case "@contact.name":
parser.swagger.Info.Contact.Name = strings.TrimSpace(commentLine[len(attribute):])
case "@contact.email":
parser.swagger.Info.Contact.Email = strings.TrimSpace(commentLine[len(attribute):])
case "@contact.url":
parser.swagger.Info.Contact.URL = strings.TrimSpace(commentLine[len(attribute):])
case "@license.name":
parser.swagger.Info.License.Name = strings.TrimSpace(commentLine[len(attribute):])
case "@license.url":
parser.swagger.Info.License.URL = strings.TrimSpace(commentLine[len(attribute):])
case "@host":
parser.swagger.Host = strings.TrimSpace(commentLine[len(attribute):])
case "@basepath":
parser.swagger.BasePath = strings.TrimSpace(commentLine[len(attribute):])
case "@schemes":
parser.swagger.Schemes = GetSchemes(commentLine)
}
}
for i := 0; i < len(comments); i++ {
attribute := strings.ToLower(strings.Split(comments[i], " ")[0])
switch attribute {
case "@securitydefinitions.basic":
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = spec.BasicAuth()
case "@securitydefinitions.apikey":
attrMap := map[string]string{}
for _, v := range comments[i+1:] {
securityAttr := strings.ToLower(strings.Split(v, " ")[0])
if securityAttr == "@in" || securityAttr == "@name" {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
}
// next securityDefinitions
if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
break
}
}
if len(attrMap) != 2 {
log.Panic("@securitydefinitions.apikey is @name and @in required")
}
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = spec.APIKeyAuth(attrMap["@name"], attrMap["@in"])
case "@securitydefinitions.oauth2.application":
attrMap := map[string]string{}
scopes := map[string]string{}
for _, v := range comments[i+1:] {
securityAttr := strings.ToLower(strings.Split(v, " ")[0])
if securityAttr == "@tokenurl" {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
} else if isExistsScope(securityAttr) {
scopes[getScopeScheme(securityAttr)] = v[len(securityAttr):]
}
// next securityDefinitions
if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
break
}
}
if len(attrMap) != 1 {
log.Panic("@securitydefinitions.oauth2.application is @tokenUrl required")
}
securityScheme := spec.OAuth2Application(attrMap["@tokenurl"])
for scope, description := range scopes {
securityScheme.AddScope(scope, description)
}
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = securityScheme
case "@securitydefinitions.oauth2.implicit":
attrMap := map[string]string{}
scopes := map[string]string{}
for _, v := range comments[i+1:] {
securityAttr := strings.ToLower(strings.Split(v, " ")[0])
if securityAttr == "@authorizationurl" {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
} else if isExistsScope(securityAttr) {
scopes[getScopeScheme(securityAttr)] = v[len(securityAttr):]
}
// next securityDefinitions
if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
break
}
}
if len(attrMap) != 1 {
log.Panic("@securitydefinitions.oauth2.implicit is @authorizationUrl required")
}
securityScheme := spec.OAuth2Implicit(attrMap["@authorizationurl"])
for scope, description := range scopes {
securityScheme.AddScope(scope, description)
}
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = securityScheme
case "@securitydefinitions.oauth2.password":
attrMap := map[string]string{}
scopes := map[string]string{}
for _, v := range comments[i+1:] {
securityAttr := strings.ToLower(strings.Split(v, " ")[0])
if securityAttr == "@tokenurl" {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
} else if isExistsScope(securityAttr) {
scopes[getScopeScheme(securityAttr)] = v[len(securityAttr):]
}
// next securityDefinitions
if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
break
}
}
if len(attrMap) != 1 {
log.Panic("@securitydefinitions.oauth2.password is @tokenUrl required")
}
securityScheme := spec.OAuth2Password(attrMap["@tokenurl"])
for scope, description := range scopes {
securityScheme.AddScope(scope, description)
}
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = securityScheme
case "@securitydefinitions.oauth2.accesscode":
attrMap := map[string]string{}
scopes := map[string]string{}
for _, v := range comments[i+1:] {
securityAttr := strings.ToLower(strings.Split(v, " ")[0])
if securityAttr == "@tokenurl" || securityAttr == "@authorizationurl" {
attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
} else if isExistsScope(securityAttr) {
scopes[getScopeScheme(securityAttr)] = v[len(securityAttr):]
}
// next securityDefinitions
if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
break
}
}
if len(attrMap) != 2 {
log.Panic("@securitydefinitions.oauth2.accessCode is @tokenUrl and @authorizationUrl required")
}
securityScheme := spec.OAuth2AccessToken(attrMap["@authorizationurl"], attrMap["@tokenurl"])
for scope, description := range scopes {
securityScheme.AddScope(scope, description)
}
securityMap[strings.TrimSpace(comments[i][len(attribute):])] = securityScheme
}
}
}
}
if len(securityMap) > 0 {
parser.swagger.SecurityDefinitions = securityMap
}
return nil
}
func getScopeScheme(scope string) string {
scopeValue := scope[strings.Index(scope, "@scope."):]
if scopeValue == "" {
panic("@scope is empty")
}
return scope[len("@scope."):]
}
func isExistsScope(scope string) bool {
s := strings.Fields(scope)
for _, v := range s {
if strings.Index(v, "@scope.") != -1 {
if strings.Index(v, ",") != -1 {
panic("@scope can't use comma(,) get=" + v)
}
}
}
return strings.Index(scope, "@scope.") != -1
}
// GetSchemes parses swagger schemes for given commentLine
func GetSchemes(commentLine string) []string {
attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
return strings.Split(strings.TrimSpace(commentLine[len(attribute):]), " ")
}
// ParseRouterAPIInfo parses router api info for given astFile
func (parser *Parser) ParseRouterAPIInfo(astFile *ast.File) {
for _, astDescription := range astFile.Decls {
switch astDeclaration := astDescription.(type) {
case *ast.FuncDecl:
if astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
operation := NewOperation() //for per 'function' comment, create a new 'Operation' object
operation.parser = parser
for _, comment := range astDeclaration.Doc.List {
if err := operation.ParseComment(comment.Text, astFile); err != nil {
log.Panicf("ParseComment panic:%+v", err)
}
}
var pathItem spec.PathItem
var ok bool
if pathItem, ok = parser.swagger.Paths.Paths[operation.Path]; !ok {
pathItem = spec.PathItem{}
}
switch strings.ToUpper(operation.HTTPMethod) {
case http.MethodGet:
pathItem.Get = &operation.Operation
case http.MethodPost:
pathItem.Post = &operation.Operation
case http.MethodDelete:
pathItem.Delete = &operation.Operation
case http.MethodPut:
pathItem.Put = &operation.Operation
case http.MethodPatch:
pathItem.Patch = &operation.Operation
case http.MethodHead:
pathItem.Head = &operation.Operation
case http.MethodOptions:
pathItem.Options = &operation.Operation
}
parser.swagger.Paths.Paths[operation.Path] = pathItem
}
}
}
}
// ParseType parses type info for given astFile.
func (parser *Parser) ParseType(astFile *ast.File) {
if _, ok := parser.TypeDefinitions[astFile.Name.String()]; !ok {
parser.TypeDefinitions[astFile.Name.String()] = make(map[string]*ast.TypeSpec)
}
for _, astDeclaration := range astFile.Decls {
if generalDeclaration, ok := astDeclaration.(*ast.GenDecl); ok && generalDeclaration.Tok == token.TYPE {
for _, astSpec := range generalDeclaration.Specs {
if typeSpec, ok := astSpec.(*ast.TypeSpec); ok {
typeName := fmt.Sprintf("%v", typeSpec.Type)
// check if its a custom primitive type
if IsGolangPrimitiveType(typeName) {
parser.CustomPrimitiveTypes[typeSpec.Name.String()] = TransToValidSchemeType(typeName)
} else {
parser.TypeDefinitions[astFile.Name.String()][typeSpec.Name.String()] = typeSpec
}
}
}
}
}
}
// ParseDefinitions parses Swagger Api definitions.
func (parser *Parser) ParseDefinitions() {
for refTypeName, typeSpec := range parser.registerTypes {
ss := strings.Split(refTypeName, ".")
pkgName := ss[0]
parser.ParseDefinition(pkgName, typeSpec, typeSpec.Name.Name)
}
}
var structStacks []string
// isNotRecurringNestStruct check if a structure that is not a not repeating
func isNotRecurringNestStruct(refTypeName string, structStacks []string) bool {
for _, v := range structStacks {
if refTypeName == v {
return false
}
}
return true
}
// ParseDefinition TODO: NEEDS COMMENT INFO
func (parser *Parser) ParseDefinition(pkgName string, typeSpec *ast.TypeSpec, typeName string) {
var refTypeName string
if len(pkgName) > 0 {
refTypeName = pkgName + "." + typeName
} else {
refTypeName = typeName
}
if _, already := parser.swagger.Definitions[refTypeName]; already {
log.Println("Skipping '" + refTypeName + "', already present.")
return
}
properties := make(map[string]spec.Schema)
// stop repetitive structural parsing
if isNotRecurringNestStruct(refTypeName, structStacks) {
structStacks = append(structStacks, refTypeName)
parser.parseTypeSpec(pkgName, typeSpec, properties)
}
structStacks = []string{}
// created sorted list of properties keys so when we iterate over them it's deterministic
ks := make([]string, 0, len(properties))
for k := range properties {
ks = append(ks, k)
}
sort.Strings(ks)
requiredFields := make([]string, 0)
// iterate over keys list instead of map to avoid the random shuffle of the order that go does for maps
for _, k := range ks {
prop := properties[k]
// todo find the pkgName of the property type
tname := prop.SchemaProps.Type[0]
if _, ok := parser.TypeDefinitions[pkgName][tname]; ok {
tspec := parser.TypeDefinitions[pkgName][tname]
parser.ParseDefinition(pkgName, tspec, tname)
}
if tname != "object" {
requiredFields = append(requiredFields, prop.SchemaProps.Required...)
prop.SchemaProps.Required = make([]string, 0)
}
properties[k] = prop
}
log.Println("Generating " + refTypeName)
parser.swagger.Definitions[refTypeName] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: properties,
Required: requiredFields,
},
}
}
func (parser *Parser) parseTypeSpec(pkgName string, typeSpec *ast.TypeSpec, properties map[string]spec.Schema) {
switch typeSpec.Type.(type) {
case *ast.StructType:
structDecl := typeSpec.Type.(*ast.StructType)
fields := structDecl.Fields.List
for _, field := range fields {
if field.Names == nil { //anonymous field
parser.parseAnonymousField(pkgName, field, properties)
} else {
props := parser.parseStruct(pkgName, field)
for k, v := range props {
properties[k] = v
}
}
}
case *ast.ArrayType:
log.Println("ParseDefinitions not supported 'Array' yet.")
case *ast.InterfaceType:
log.Println("ParseDefinitions not supported 'Interface' yet.")
case *ast.MapType:
log.Println("ParseDefinitions not supported 'Map' yet.")
}
}
type structField struct {
name string
schemaType string
arrayType string
formatType string
isRequired bool
crossPkg string
exampleValue interface{}
maximum *float64
minimum *float64
maxLength *int64
minLength *int64
enums []interface{}
defaultValue interface{}
}
func (parser *Parser) parseStruct(pkgName string, field *ast.Field) (properties map[string]spec.Schema) {
properties = map[string]spec.Schema{}
structField := parser.parseField(field)
if structField.name == "" {
return
}
var desc string
if field.Doc != nil {
desc = strings.TrimSpace(field.Doc.Text())
}
// TODO: find package of schemaType and/or arrayType
if structField.crossPkg != "" {
pkgName = structField.crossPkg
}
if _, ok := parser.TypeDefinitions[pkgName][structField.schemaType]; ok { // user type field
// write definition if not yet present
parser.ParseDefinition(pkgName, parser.TypeDefinitions[pkgName][structField.schemaType], structField.schemaType)
properties[structField.name] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"}, // to avoid swagger validation error
Description: desc,
Ref: spec.Ref{
Ref: jsonreference.MustCreateRef("#/definitions/" + pkgName + "." + structField.schemaType),
},
},
}
} else if structField.schemaType == "array" { // array field type
// if defined -- ref it
if _, ok := parser.TypeDefinitions[pkgName][structField.arrayType]; ok { // user type in array
parser.ParseDefinition(pkgName, parser.TypeDefinitions[pkgName][structField.arrayType], structField.arrayType)
properties[structField.name] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{structField.schemaType},
Description: desc,
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.Ref{
Ref: jsonreference.MustCreateRef("#/definitions/" + pkgName + "." + structField.arrayType),
},
},
},
},
},
}
} else { // standard type in array
required := make([]string, 0)
if structField.isRequired {
required = append(required, structField.name)
}
properties[structField.name] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{structField.schemaType},
Description: desc,
Format: structField.formatType,
Required: required,
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{structField.arrayType},
Maximum: structField.maximum,
Minimum: structField.minimum,
MaxLength: structField.maxLength,
MinLength: structField.minLength,
Enum: structField.enums,
Default: structField.defaultValue,
},
},
},
},
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: structField.exampleValue,
},
}
}
} else {
required := make([]string, 0)
if structField.isRequired {
required = append(required, structField.name)
}
properties[structField.name] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{structField.schemaType},
Description: desc,
Format: structField.formatType,
Required: required,
Maximum: structField.maximum,
Minimum: structField.minimum,
MaxLength: structField.maxLength,
MinLength: structField.minLength,
Enum: structField.enums,
Default: structField.defaultValue,
},
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: structField.exampleValue,
},
}
nestStruct, ok := field.Type.(*ast.StructType)
if ok {
props := map[string]spec.Schema{}
nestRequired := make([]string, 0)
for _, v := range nestStruct.Fields.List {
p := parser.parseStruct(pkgName, v)
for k, v := range p {
if v.SchemaProps.Type[0] != "object" {
nestRequired = append(nestRequired, v.SchemaProps.Required...)
v.SchemaProps.Required = make([]string, 0)
}
props[k] = v
}
}
properties[structField.name] = spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{structField.schemaType},
Description: desc,
Format: structField.formatType,
Properties: props,
Required: nestRequired,
Maximum: structField.maximum,
Minimum: structField.minimum,
MaxLength: structField.maxLength,
MinLength: structField.minLength,
Enum: structField.enums,
Default: structField.defaultValue,
},
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: structField.exampleValue,
},
}
}
}
return
}
func (parser *Parser) parseAnonymousField(pkgName string, field *ast.Field, properties map[string]spec.Schema) {
// check if ast Field is Ident type
astTypeIdent, okTypeIdent := field.Type.(*ast.Ident)
// if ast Field is not Ident type we check if it's StarExpr
// because it might be a pointer to an Ident
if !okTypeIdent {
if astTypeStar, okTypeStar := field.Type.(*ast.StarExpr); okTypeStar {
astTypeIdent, okTypeIdent = astTypeStar.X.(*ast.Ident)
}
}
if okTypeIdent {
findPgkName := pkgName
findBaseTypeName := astTypeIdent.Name
ss := strings.Split(astTypeIdent.Name, ".")
if len(ss) > 1 {
findPgkName = ss[0]
findBaseTypeName = ss[1]
}
baseTypeSpec := parser.TypeDefinitions[findPgkName][findBaseTypeName]
parser.parseTypeSpec(findPgkName, baseTypeSpec, properties)
}
}
func (parser *Parser) parseField(field *ast.Field) *structField {
prop := getPropertyName(field, parser)
if len(prop.ArrayType) == 0 {
CheckSchemaType(prop.SchemaType)
} else {
CheckSchemaType("array")
}
structField := &structField{
name: field.Names[0].Name,
schemaType: prop.SchemaType,
arrayType: prop.ArrayType,
crossPkg: prop.CrossPkg,
}
switch parser.PropNamingStrategy {
case SnakeCase:
structField.name = toSnakeCase(structField.name)
case PascalCase:
//use struct field name
case CamelCase:
structField.name = toLowerCamelCase(structField.name)
default:
structField.name = toLowerCamelCase(structField.name)
}
if field.Tag == nil {
return structField
}
// `json:"tag"` -> json:"tag"
structTag := reflect.StructTag(strings.Replace(field.Tag.Value, "`", "", -1))
jsonTag := structTag.Get("json")
// json:"tag,hoge"
if strings.Contains(jsonTag, ",") {
// json:",hoge"
if strings.HasPrefix(jsonTag, ",") {
jsonTag = ""
} else {
jsonTag = strings.SplitN(jsonTag, ",", 2)[0]
}
}
if jsonTag == "-" {
structField.name = ""
} else if jsonTag != "" {
structField.name = jsonTag
}
if typeTag := structTag.Get("swaggertype"); typeTag != "" {
parts := strings.Split(typeTag, ",")
if 0 < len(parts) && len(parts) <= 2 {
newSchemaType := parts[0]
newArrayType := structField.arrayType
if len(parts) >= 2 && newSchemaType == "array" {
newArrayType = parts[1]
}
CheckSchemaType(newSchemaType)
CheckSchemaType(newArrayType)
structField.schemaType = newSchemaType
structField.arrayType = newArrayType
}
}
if exampleTag := structTag.Get("example"); exampleTag != "" {
structField.exampleValue = defineTypeOfExample(structField.schemaType, structField.arrayType, exampleTag)
}
if formatTag := structTag.Get("format"); formatTag != "" {
structField.formatType = formatTag
}
if bindingTag := structTag.Get("binding"); bindingTag != "" {
for _, val := range strings.Split(bindingTag, ",") {
if val == "required" {
structField.isRequired = true
break
}
}
}
if validateTag := structTag.Get("validate"); validateTag != "" {
for _, val := range strings.Split(validateTag, ",") {
if val == "required" {
structField.isRequired = true
break
}
}
}
if enumsTag := structTag.Get("enums"); enumsTag != "" {
enumType := structField.schemaType
if structField.schemaType == "array" {
enumType = structField.arrayType
}
for _, e := range strings.Split(enumsTag, ",") {
structField.enums = append(structField.enums, defineType(enumType, e))
}
}
if defaultTag := structTag.Get("default"); defaultTag != "" {
structField.defaultValue = defineType(structField.schemaType, defaultTag)
}
if IsNumericType(structField.schemaType) || IsNumericType(structField.arrayType) {
structField.maximum = getFloatTag(structTag, "maximum")
structField.minimum = getFloatTag(structTag, "minimum")
}
if structField.schemaType == "string" || structField.arrayType == "string" {
structField.maxLength = getIntTag(structTag, "maxLength")
structField.minLength = getIntTag(structTag, "minLength")
}
return structField
}
func getFloatTag(structTag reflect.StructTag, tagName string) *float64 {
strValue := structTag.Get(tagName)
if strValue == "" {
return nil
}
value, err := strconv.ParseFloat(strValue, 64)
if err != nil {
panic(fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err))
}
return &value
}
func getIntTag(structTag reflect.StructTag, tagName string) *int64 {
strValue := structTag.Get(tagName)
if strValue == "" {
return nil
}
value, err := strconv.ParseInt(strValue, 10, 64)
if err != nil {
panic(fmt.Errorf("can't parse numeric value of %q tag: %v", tagName, err))
}
return &value
}
func toSnakeCase(in string) string {
runes := []rune(in)
length := len(runes)
var out []rune
for i := 0; i < length; i++ {
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
out = append(out, '_')
}
out = append(out, unicode.ToLower(runes[i]))
}
return string(out)
}
func toLowerCamelCase(in string) string {
runes := []rune(in)
var out []rune
flag := false
for i, curr := range runes {
if (i == 0 && unicode.IsUpper(curr)) || (flag && unicode.IsUpper(curr)) {
out = append(out, unicode.ToLower(curr))
flag = true
} else {
out = append(out, curr)
flag = false
}
}
return string(out)
}
// defineTypeOfExample example value define the type (object and array unsupported)
func defineTypeOfExample(schemaType, arrayType, exampleValue string) interface{} {
switch schemaType {
case "string":
return exampleValue
case "number":
v, err := strconv.ParseFloat(exampleValue, 64)
if err != nil {
panic(fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err))
}
return v
case "integer":
v, err := strconv.Atoi(exampleValue)
if err != nil {
panic(fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err))
}
return v
case "boolean":
v, err := strconv.ParseBool(exampleValue)
if err != nil {
panic(fmt.Errorf("example value %s can't convert to %s err: %s", exampleValue, schemaType, err))
}
return v
case "array":
values := strings.Split(exampleValue, ",")
result := make([]interface{}, 0)
for _, value := range values {
result = append(result, defineTypeOfExample(arrayType, "", value))
}
return result
default:
panic(fmt.Errorf("%s is unsupported type in example value", schemaType))
}
}
// GetAllGoFileInfo gets all Go source files information for given searchDir.
func (parser *Parser) getAllGoFileInfo(searchDir string) error {
return filepath.Walk(searchDir, parser.visit)
}
func (parser *Parser) visit(path string, f os.FileInfo, err error) error {
if err := Skip(f); err != nil {
return err
}
if ext := filepath.Ext(path); ext == ".go" {
fset := token.NewFileSet() // positions are relative to fset
astFile, err := goparser.ParseFile(fset, path, nil, goparser.ParseComments)
if err != nil {
log.Panicf("ParseFile panic:%+v", err)
}
parser.files[path] = astFile
}
return nil
}
// Skip returns filepath.SkipDir error if match vendor and hidden folder
func Skip(f os.FileInfo) error {
// exclude vendor folder
if f.IsDir() && f.Name() == "vendor" {
return filepath.SkipDir
}
// exclude all hidden folder
if f.IsDir() && len(f.Name()) > 1 && f.Name()[0] == '.' {
return filepath.SkipDir
}
return nil
}
// GetSwagger returns *spec.Swagger which is the root document object for the API specification.
func (parser *Parser) GetSwagger() *spec.Swagger {
return parser.swagger
}

131
vendor/github.com/swaggo/swag/property.go generated vendored Normal file
View File

@ -0,0 +1,131 @@
package swag
import (
"errors"
"fmt"
"go/ast"
"strings"
)
// ErrFailedConvertPrimitiveType Failed to convert for swag to interpretable type
var ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type")
type propertyName struct {
SchemaType string
ArrayType string
CrossPkg string
}
type propertyNewFunc func(schemeType string, crossPkg string) propertyName
func newArrayProperty(schemeType string, crossPkg string) propertyName {
return propertyName{
SchemaType: "array",
ArrayType: schemeType,
CrossPkg: crossPkg,
}
}
func newProperty(schemeType string, crossPkg string) propertyName {
return propertyName{
SchemaType: schemeType,
ArrayType: "string",
CrossPkg: crossPkg,
}
}
func convertFromSpecificToPrimitive(typeName string) (string, error) {
typeName = strings.ToUpper(typeName)
switch typeName {
case "TIME", "OBJECTID", "UUID":
return "string", nil
case "DECIMAL":
return "number", nil
}
return "", ErrFailedConvertPrimitiveType
}
func parseFieldSelectorExpr(astTypeSelectorExpr *ast.SelectorExpr, parser *Parser, propertyNewFunc propertyNewFunc) propertyName {
if primitiveType, err := convertFromSpecificToPrimitive(astTypeSelectorExpr.Sel.Name); err == nil {
return propertyNewFunc(primitiveType, "")
}
if pkgName, ok := astTypeSelectorExpr.X.(*ast.Ident); ok {
if typeDefinitions, ok := parser.TypeDefinitions[pkgName.Name][astTypeSelectorExpr.Sel.Name]; ok {
parser.ParseDefinition(pkgName.Name, typeDefinitions, astTypeSelectorExpr.Sel.Name)
return propertyNewFunc(astTypeSelectorExpr.Sel.Name, pkgName.Name)
}
if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[astTypeSelectorExpr.Sel.Name]; isCustomType {
return propertyName{SchemaType: actualPrimitiveType, ArrayType: actualPrimitiveType}
}
}
fmt.Printf("%s is not supported. but it will be set with string temporary. Please report any problems.", astTypeSelectorExpr.Sel.Name)
return propertyName{SchemaType: "string", ArrayType: "string"}
}
// getPropertyName returns the string value for the given field if it exists, otherwise it panics.
// allowedValues: array, boolean, integer, null, number, object, string
func getPropertyName(field *ast.Field, parser *Parser) propertyName {
if astTypeSelectorExpr, ok := field.Type.(*ast.SelectorExpr); ok {
return parseFieldSelectorExpr(astTypeSelectorExpr, parser, newProperty)
}
// check if it is a custom type
typeName := fmt.Sprintf("%v", field.Type)
if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[typeName]; isCustomType {
return propertyName{SchemaType: actualPrimitiveType, ArrayType: actualPrimitiveType}
}
if astTypeIdent, ok := field.Type.(*ast.Ident); ok {
name := astTypeIdent.Name
schemeType := TransToValidSchemeType(name)
return propertyName{SchemaType: schemeType, ArrayType: schemeType}
}
if ptr, ok := field.Type.(*ast.StarExpr); ok {
if astTypeSelectorExpr, ok := ptr.X.(*ast.SelectorExpr); ok {
return parseFieldSelectorExpr(astTypeSelectorExpr, parser, newProperty)
}
if astTypeIdent, ok := ptr.X.(*ast.Ident); ok {
name := astTypeIdent.Name
schemeType := TransToValidSchemeType(name)
return propertyName{SchemaType: schemeType, ArrayType: schemeType}
}
if astTypeArray, ok := ptr.X.(*ast.ArrayType); ok { // if array
if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
return parseFieldSelectorExpr(astTypeArrayExpr, parser, newArrayProperty)
}
if astTypeArrayIdent, ok := astTypeArray.Elt.(*ast.Ident); ok {
name := TransToValidSchemeType(astTypeArrayIdent.Name)
return propertyName{SchemaType: "array", ArrayType: name}
}
}
}
if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { // if array
if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.SelectorExpr); ok {
return parseFieldSelectorExpr(astTypeArrayExpr, parser, newArrayProperty)
}
if astTypeArrayExpr, ok := astTypeArray.Elt.(*ast.StarExpr); ok {
if astTypeArrayIdent, ok := astTypeArrayExpr.X.(*ast.Ident); ok {
name := TransToValidSchemeType(astTypeArrayIdent.Name)
return propertyName{SchemaType: "array", ArrayType: name}
}
}
itemTypeName := TransToValidSchemeType(fmt.Sprintf("%s", astTypeArray.Elt))
if actualPrimitiveType, isCustomType := parser.CustomPrimitiveTypes[itemTypeName]; isCustomType {
itemTypeName = actualPrimitiveType
}
return propertyName{SchemaType: "array", ArrayType: itemTypeName}
}
if _, ok := field.Type.(*ast.MapType); ok { // if map
//TODO: support map
return propertyName{SchemaType: "object", ArrayType: "object"}
}
if _, ok := field.Type.(*ast.StructType); ok { // if struct
return propertyName{SchemaType: "object", ArrayType: "object"}
}
if _, ok := field.Type.(*ast.InterfaceType); ok { // if interface{}
return propertyName{SchemaType: "object", ArrayType: "object"}
}
panic("not supported" + fmt.Sprint(field.Type))
}

70
vendor/github.com/swaggo/swag/schema.go generated vendored Normal file
View File

@ -0,0 +1,70 @@
package swag
import "fmt"
// CheckSchemaType TODO: NEEDS COMMENT INFO
func CheckSchemaType(typeName string) {
if !IsPrimitiveType(typeName) {
panic(fmt.Errorf("%s is not basic types", typeName))
}
}
// IsPrimitiveType determine whether the type name is a primitive type
func IsPrimitiveType(typeName string) bool {
switch typeName {
case "string", "number", "integer", "boolean", "array", "object":
return true
default:
return false
}
}
// IsNumericType determines whether the swagger type name is a numeric type
func IsNumericType(typeName string) bool {
return typeName == "integer" || typeName == "number"
}
// TransToValidSchemeType indicates type will transfer golang basic type to swagger supported type.
func TransToValidSchemeType(typeName string) string {
switch typeName {
case "uint", "int", "uint8", "int8", "uint16", "int16", "byte":
return "integer"
case "uint32", "int32", "rune":
return "integer"
case "uint64", "int64":
return "integer"
case "float32", "float64":
return "number"
case "bool":
return "boolean"
case "string":
return "string"
default:
return typeName // to support user defined types
}
}
// IsGolangPrimitiveType determine whether the type name is a golang primitive type
func IsGolangPrimitiveType(typeName string) bool {
switch typeName {
case "uint",
"int",
"uint8",
"int8",
"uint16",
"int16",
"byte",
"uint32",
"int32",
"rune",
"uint64",
"int64",
"float32",
"float64",
"bool",
"string":
return true
default:
return false
}
}

41
vendor/github.com/swaggo/swag/swagger.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
package swag
import (
"errors"
"sync"
)
// Name is a unique name be used to register swag instance.
const Name = "swagger"
var (
swaggerMu sync.RWMutex
swag Swagger
)
// Swagger is a interface to read swagger document.
type Swagger interface {
ReadDoc() string
}
// Register registers swagger for given name.
func Register(name string, swagger Swagger) {
swaggerMu.Lock()
defer swaggerMu.Unlock()
if swagger == nil {
panic("swagger is nil")
}
if swag != nil {
panic("Register called twice for swag: " + name)
}
swag = swagger
}
// ReadDoc reads swagger document.
func ReadDoc() (string, error) {
if swag != nil {
return swag.ReadDoc(), nil
}
return "", errors.New("not yet registered swag")
}

4
vendor/github.com/swaggo/swag/version.go generated vendored Normal file
View File

@ -0,0 +1,4 @@
package swag
// Version of swag
const Version = "v1.4.0"