echoswagger/spec.go

194 lines
4.3 KiB
Go
Raw Permalink Normal View History

/*
* Copyright (c) 2019 Alex aka mailoman <alex@webz.asia>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Alex aka mailoman <alex@webz.asia>
* @copyright Copyright (c) 2019 Alex aka mailoman <alex@webz.asia>
* @since 18.12.2019
*
*/
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"
"net/url"
2018-09-01 17:18:38 +02:00
"reflect"
2018-08-31 14:47:37 +02:00
"github.com/labstack/echo/v4"
2018-08-31 14:47:37 +02:00
)
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
func (r *Root) specHandler(docPath string) echo.HandlerFunc {
2019-05-11 16:27:52 +02:00
return func(c echo.Context) error {
spec, err := r.GetSpec(c, docPath)
if err != nil {
return c.String(http.StatusInternalServerError, err.Error())
2019-05-11 16:27:52 +02:00
}
2019-05-13 04:10:24 +02:00
var basePath string
if uri, err := url.ParseRequestURI(c.Request().Referer()); err == nil {
basePath = trimSuffixSlash(uri.Path, docPath)
spec.Host = uri.Host
} else {
basePath = trimSuffixSlash(c.Request().URL.Path, connectPath(docPath, SpecName))
spec.Host = c.Request().Host
}
spec.BasePath = basePath
return c.JSON(http.StatusOK, spec)
}
}
2019-05-13 04:10:24 +02:00
// Generate swagger spec data, without host & basePath info
func (r *Root) GetSpec(c echo.Context, docPath string) (Swagger, error) {
r.once.Do(func() {
r.err = r.genSpec(c)
r.cleanUp()
})
if r.err != nil {
return Swagger{}, r.err
}
2019-05-13 04:10:24 +02:00
return *r.spec, nil
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),
}
(*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
}