2021-01-01 20:30:52 +08:00
|
|
|
package generate
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
|
|
|
"net/http"
|
|
|
|
"reflect"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
func applyGenerate(p Plugin) (*swaggerObject, error) {
|
|
|
|
|
|
|
|
s := swaggerObject{
|
|
|
|
Swagger: "2.0",
|
|
|
|
Schemes: []string{"http", "https"},
|
|
|
|
Consumes: []string{"application/json"},
|
|
|
|
Produces: []string{"application/json"},
|
|
|
|
Paths: make(swaggerPathsObject),
|
|
|
|
Definitions: make(swaggerDefinitionsObject),
|
|
|
|
StreamDefinitions: make(swaggerDefinitionsObject),
|
|
|
|
Info: swaggerInfoObject{
|
|
|
|
Title: p.Api.Info.Title,
|
|
|
|
Version: p.Api.Info.Version,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
requestResponseRefs := refMap{}
|
|
|
|
renderServiceRoutes(p.Api.Service, p.Api.Service.Groups, s.Paths, requestResponseRefs)
|
|
|
|
m := messageMap{}
|
|
|
|
renderReplyAsDefinition(s.Definitions, m, p.Api.Types, requestResponseRefs)
|
|
|
|
return &s, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swaggerPathsObject, requestResponseRefs refMap) {
|
|
|
|
|
|
|
|
for _, group := range groups {
|
|
|
|
|
|
|
|
for _, route := range group.Routes {
|
|
|
|
|
|
|
|
path := route.Path
|
|
|
|
if strings.Contains(path, "/:") {
|
|
|
|
|
|
|
|
}
|
|
|
|
parameters := swaggerParametersObject{}
|
|
|
|
|
|
|
|
reqRef := fmt.Sprintf("#/definitions/%s", route.RequestType.Name)
|
2021-01-03 16:10:42 +08:00
|
|
|
if len(route.RequestType.Name) > 0 {
|
|
|
|
var schema = swaggerSchemaObject{
|
|
|
|
schemaCore: schemaCore{
|
|
|
|
Ref: reqRef,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
parameters = append(parameters, swaggerParameterObject{
|
|
|
|
Name: "body",
|
|
|
|
In: "body",
|
|
|
|
Required: true,
|
|
|
|
Schema: &schema,
|
|
|
|
})
|
2021-01-01 20:30:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pathItemObject, ok := paths[path]
|
|
|
|
if !ok {
|
|
|
|
pathItemObject = swaggerPathItemObject{}
|
|
|
|
}
|
|
|
|
desc := "A successful response."
|
|
|
|
respRef := fmt.Sprintf("#/definitions/%s", route.ResponseType.Name)
|
|
|
|
if len(route.ResponseType.Name) < 1 {
|
|
|
|
respRef = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
tags := service.Name
|
|
|
|
if group.Annotations != nil && len(group.Annotations) > 0 {
|
|
|
|
if groupName, ok := group.Annotations[0].Properties["group"]; ok {
|
|
|
|
tags = groupName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
operationObject := &swaggerOperationObject{
|
|
|
|
Tags: []string{tags},
|
|
|
|
Parameters: parameters,
|
|
|
|
Responses: swaggerResponsesObject{
|
|
|
|
"200": swaggerResponseObject{
|
|
|
|
Description: desc,
|
|
|
|
Schema: swaggerSchemaObject{
|
|
|
|
schemaCore: schemaCore{
|
|
|
|
Ref: respRef,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
operationObject.OperationID = fmt.Sprintf("%s", route.Path)
|
|
|
|
|
|
|
|
for _, param := range operationObject.Parameters {
|
|
|
|
if param.Schema != nil && param.Schema.Ref != "" {
|
|
|
|
requestResponseRefs[param.Schema.Ref] = struct{}{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(route.Annotations) > 0 {
|
|
|
|
operationObject.Summary, _ = strconv.Unquote(route.Annotations[0].Properties["summary"])
|
|
|
|
operationObject.Description, _ = strconv.Unquote(route.Annotations[0].Properties["description"])
|
|
|
|
}
|
|
|
|
|
|
|
|
switch strings.ToUpper(route.Method) {
|
|
|
|
case http.MethodGet:
|
|
|
|
pathItemObject.Get = operationObject
|
|
|
|
case http.MethodPost:
|
|
|
|
pathItemObject.Post = operationObject
|
|
|
|
case http.MethodDelete:
|
|
|
|
pathItemObject.Delete = operationObject
|
|
|
|
case http.MethodPut:
|
|
|
|
pathItemObject.Put = operationObject
|
|
|
|
}
|
|
|
|
|
|
|
|
paths[path] = pathItemObject
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func renderReplyAsDefinition(d swaggerDefinitionsObject, m messageMap, p []spec.Type, refs refMap) {
|
|
|
|
for _, i2 := range p {
|
|
|
|
schema := swaggerSchemaObject{
|
|
|
|
schemaCore: schemaCore{
|
|
|
|
Type: "object",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
schema.Title = i2.Name
|
|
|
|
|
|
|
|
for _, member := range i2.Members {
|
|
|
|
kv := keyVal{Value: schemaOfField(member)}
|
|
|
|
kv.Key = member.Name
|
|
|
|
if schema.Properties == nil {
|
|
|
|
schema.Properties = &swaggerSchemaObjectProperties{}
|
|
|
|
}
|
|
|
|
*schema.Properties = append(*schema.Properties, kv)
|
|
|
|
}
|
|
|
|
d[i2.Name] = schema
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func schemaOfField(member spec.Member) swaggerSchemaObject {
|
|
|
|
ret := swaggerSchemaObject{}
|
|
|
|
|
|
|
|
var core schemaCore
|
|
|
|
|
|
|
|
kind := swaggerMapTypes[member.Type]
|
|
|
|
var props *swaggerSchemaObjectProperties
|
|
|
|
|
|
|
|
switch ft := kind; ft {
|
|
|
|
case reflect.Invalid:
|
|
|
|
// []Struct
|
|
|
|
refTypeName := strings.Replace(member.Type, "[", "", 1)
|
|
|
|
refTypeName = strings.Replace(refTypeName, "]", "", 1)
|
|
|
|
core = schemaCore{
|
|
|
|
Ref: "#/definitions/" + refTypeName,
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ftype, format, ok := primitiveSchema(ft, member.Type)
|
|
|
|
if ok {
|
|
|
|
core = schemaCore{Type: ftype, Format: format}
|
|
|
|
} else {
|
|
|
|
core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ft := kind; ft {
|
|
|
|
case reflect.Slice, reflect.Invalid:
|
|
|
|
ret = swaggerSchemaObject{
|
|
|
|
schemaCore: schemaCore{
|
|
|
|
Type: "array",
|
|
|
|
Items: (*swaggerItemsObject)(&core),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
ret = swaggerSchemaObject{
|
|
|
|
schemaCore: core,
|
|
|
|
Properties: props,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
2021-01-03 16:10:42 +08:00
|
|
|
// https://swagger.io/specification/ Data Types
|
2021-01-01 20:30:52 +08:00
|
|
|
func primitiveSchema(kind reflect.Kind, t string) (ftype, format string, ok bool) {
|
|
|
|
switch kind {
|
|
|
|
case reflect.Int:
|
|
|
|
return "integer", "int32", true
|
2021-01-03 16:10:42 +08:00
|
|
|
case reflect.Int64:
|
|
|
|
return "integer", "int64", true
|
2021-01-01 20:30:52 +08:00
|
|
|
case reflect.Bool:
|
|
|
|
return "boolean", "boolean", true
|
|
|
|
case reflect.String:
|
|
|
|
return "string", "", true
|
|
|
|
case reflect.Slice:
|
|
|
|
return strings.Replace(t, "[]", "", -1), "", true
|
|
|
|
default:
|
|
|
|
return "", "", false
|
|
|
|
}
|
|
|
|
}
|