2018-08-31 14:47:37 +02:00
|
|
|
package echoswagger
|
|
|
|
|
|
|
|
import (
|
2018-09-01 17:18:38 +02:00
|
|
|
"encoding/xml"
|
2018-08-31 14:47:37 +02:00
|
|
|
"net/http"
|
2018-11-03 02:51:41 +01:00
|
|
|
"net/url"
|
2018-09-01 17:18:38 +02:00
|
|
|
"reflect"
|
2018-08-31 14:47:37 +02:00
|
|
|
|
|
|
|
"github.com/labstack/echo"
|
|
|
|
)
|
|
|
|
|
2018-09-01 17:18:38 +02:00
|
|
|
const (
|
|
|
|
DefPrefix = "#/definitions/"
|
|
|
|
SwaggerVersion = "2.0"
|
2019-05-11 16:27:52 +02:00
|
|
|
SpecName = "swagger.json"
|
2018-09-01 17:18:38 +02:00
|
|
|
)
|
2018-08-31 14:47:37 +02:00
|
|
|
|
2019-05-11 16:27:52 +02:00
|
|
|
func (r *Root) SpecHandler(docPath string) echo.HandlerFunc {
|
|
|
|
return func(c echo.Context) error {
|
|
|
|
r.once.Do(func() {
|
|
|
|
r.err = r.genSpec(c)
|
|
|
|
r.cleanUp()
|
|
|
|
})
|
|
|
|
if r.err != nil {
|
|
|
|
return c.String(http.StatusInternalServerError, r.err.Error())
|
|
|
|
}
|
|
|
|
var basePath string
|
|
|
|
if uri, err := url.ParseRequestURI(c.Request().Referer()); err == nil {
|
|
|
|
basePath = trimSuffixSlash(uri.Path, docPath)
|
|
|
|
r.spec.Host = uri.Host
|
|
|
|
} else {
|
|
|
|
basePath = trimSuffixSlash(c.Request().URL.Path, connectPath(docPath, SpecName))
|
|
|
|
r.spec.Host = c.Request().Host
|
|
|
|
}
|
|
|
|
r.spec.BasePath = connectPath(basePath)
|
|
|
|
return c.JSON(http.StatusOK, r.spec)
|
2018-11-03 02:51:41 +01:00
|
|
|
}
|
2018-08-31 14:47:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Root) genSpec(c echo.Context) error {
|
|
|
|
r.spec.Swagger = SwaggerVersion
|
|
|
|
r.spec.Paths = make(map[string]interface{})
|
|
|
|
|
|
|
|
for i := range r.groups {
|
|
|
|
group := &r.groups[i]
|
|
|
|
r.spec.Tags = append(r.spec.Tags, &group.tag)
|
|
|
|
for j := range group.apis {
|
|
|
|
a := &group.apis[j]
|
2018-09-01 17:18:38 +02:00
|
|
|
if err := a.operation.addSecurity(r.spec.SecurityDefinitions, group.security); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := r.transfer(a); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range r.apis {
|
|
|
|
if err := r.transfer(&r.apis[i]); err != nil {
|
|
|
|
return err
|
2018-08-31 14:47:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-01 17:18:38 +02:00
|
|
|
for k, v := range *r.defs {
|
|
|
|
r.spec.Definitions[k] = v.Schema
|
|
|
|
}
|
2018-08-31 14:47:37 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-01 17:18:38 +02:00
|
|
|
func (r *Root) transfer(a *api) error {
|
|
|
|
if err := a.operation.addSecurity(r.spec.SecurityDefinitions, a.security); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
path := toSwaggerPath(a.route.Path)
|
|
|
|
if len(a.operation.Responses) == 0 {
|
|
|
|
a.operation.Responses["default"] = &Response{
|
|
|
|
Description: "successful operation",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if p, ok := r.spec.Paths[path]; ok {
|
2018-09-03 16:52:30 +02:00
|
|
|
p.(*Path).oprationAssign(a.route.Method, &a.operation)
|
2018-09-01 17:18:38 +02:00
|
|
|
} else {
|
|
|
|
p := &Path{}
|
2018-09-03 16:52:30 +02:00
|
|
|
p.oprationAssign(a.route.Method, &a.operation)
|
2018-09-01 17:18:38 +02:00
|
|
|
r.spec.Paths[path] = p
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Path) oprationAssign(method string, operation *Operation) {
|
|
|
|
switch method {
|
|
|
|
case echo.GET:
|
|
|
|
p.Get = operation
|
|
|
|
case echo.POST:
|
|
|
|
p.Post = operation
|
|
|
|
case echo.PUT:
|
|
|
|
p.Put = operation
|
|
|
|
case echo.DELETE:
|
|
|
|
p.Delete = operation
|
2018-09-03 16:52:30 +02:00
|
|
|
case echo.OPTIONS:
|
|
|
|
p.Options = operation
|
|
|
|
case echo.HEAD:
|
|
|
|
p.Head = operation
|
|
|
|
case echo.PATCH:
|
|
|
|
p.Patch = operation
|
2018-09-01 17:18:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Root) cleanUp() {
|
|
|
|
r.echo = nil
|
|
|
|
r.groups = nil
|
|
|
|
r.apis = nil
|
|
|
|
r.defs = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// addDefinition adds definition specification and returns
|
|
|
|
// key of RawDefineDic
|
|
|
|
func (r *RawDefineDic) addDefinition(v reflect.Value) string {
|
|
|
|
exist, key := r.getKey(v)
|
|
|
|
if exist {
|
|
|
|
return key
|
2018-08-31 14:47:37 +02:00
|
|
|
}
|
|
|
|
|
2018-09-01 17:18:38 +02:00
|
|
|
schema := &JSONSchema{
|
|
|
|
Type: "object",
|
|
|
|
Properties: make(map[string]*JSONSchema),
|
|
|
|
}
|
|
|
|
|
2018-09-18 05:59:50 +02:00
|
|
|
(*r)[key] = RawDefine{
|
|
|
|
Value: v,
|
|
|
|
Schema: schema,
|
|
|
|
}
|
|
|
|
|
2018-09-01 17:18:38 +02:00
|
|
|
for i := 0; i < v.NumField(); i++ {
|
|
|
|
f := v.Type().Field(i)
|
|
|
|
name := getFieldName(f, ParamInBody)
|
|
|
|
if name == "-" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if f.Type == reflect.TypeOf(xml.Name{}) {
|
|
|
|
schema.handleXMLTags(f)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
sp := r.genSchema(v.Field(i))
|
|
|
|
sp.handleXMLTags(f)
|
|
|
|
if sp.XML != nil {
|
|
|
|
sp.handleChildXMLTags(sp.XML.Name, r)
|
|
|
|
}
|
|
|
|
schema.Properties[name] = sp
|
|
|
|
|
|
|
|
schema.handleSwaggerTags(f, name)
|
|
|
|
}
|
|
|
|
|
|
|
|
if schema.XML == nil {
|
|
|
|
schema.XML = &XMLSchema{}
|
|
|
|
}
|
|
|
|
if schema.XML.Name == "" {
|
|
|
|
schema.XML.Name = v.Type().Name()
|
2018-08-31 14:47:37 +02:00
|
|
|
}
|
2018-09-01 17:18:38 +02:00
|
|
|
return key
|
2018-08-31 14:47:37 +02:00
|
|
|
}
|