2021-10-02 18:54:06 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
|
|
|
|
// Package goai implements and provides document generating for OpenApi specification.
|
|
|
|
//
|
|
|
|
// https://editor.swagger.io/
|
2021-10-02 14:52:28 +08:00
|
|
|
package goai
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2021-10-02 18:54:06 +08:00
|
|
|
"fmt"
|
2021-10-02 14:52:28 +08:00
|
|
|
"github.com/gogf/gf/errors/gcode"
|
|
|
|
"github.com/gogf/gf/errors/gerror"
|
|
|
|
"github.com/gogf/gf/internal/intlog"
|
|
|
|
"github.com/gogf/gf/internal/json"
|
2021-10-06 21:51:21 +08:00
|
|
|
"github.com/gogf/gf/text/gstr"
|
2021-10-02 14:52:28 +08:00
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
|
|
|
// OpenApiV3 is the structure defined from:
|
2021-10-02 18:54:06 +08:00
|
|
|
// https://swagger.io/specification/
|
2021-10-02 14:52:28 +08:00
|
|
|
// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
|
|
|
|
type OpenApiV3 struct {
|
|
|
|
Config Config `json:"-" yaml:"-"`
|
|
|
|
OpenAPI string `json:"openapi" yaml:"openapi"`
|
|
|
|
Components Components `json:"components,omitempty" yaml:"components,omitempty"`
|
|
|
|
Info Info `json:"info" yaml:"info"`
|
|
|
|
Paths Paths `json:"paths" yaml:"paths"`
|
|
|
|
Security *SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
|
|
|
|
Servers *Servers `json:"servers,omitempty" yaml:"servers,omitempty"`
|
|
|
|
Tags *Tags `json:"tags,omitempty" yaml:"tags,omitempty"`
|
|
|
|
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// ExternalDocs is specified by OpenAPI/Swagger standard version 3.0.
|
|
|
|
type ExternalDocs struct {
|
|
|
|
URL string `json:"url,omitempty"`
|
|
|
|
Description string `json:"description,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
2021-10-02 22:34:39 +08:00
|
|
|
HttpMethodAll = `ALL`
|
2021-10-02 14:52:28 +08:00
|
|
|
HttpMethodGet = `GET`
|
|
|
|
HttpMethodPut = `PUT`
|
|
|
|
HttpMethodPost = `POST`
|
|
|
|
HttpMethodDelete = `DELETE`
|
|
|
|
HttpMethodConnect = `CONNECT`
|
|
|
|
HttpMethodHead = `HEAD`
|
|
|
|
HttpMethodOptions = `OPTIONS`
|
|
|
|
HttpMethodPatch = `PATCH`
|
|
|
|
HttpMethodTrace = `TRACE`
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
TypeNumber = `number`
|
|
|
|
TypeBoolean = `boolean`
|
|
|
|
TypeArray = `array`
|
|
|
|
TypeString = `string`
|
|
|
|
TypeObject = `object`
|
|
|
|
FormatInt32 = `int32`
|
|
|
|
FormatInt64 = `int64`
|
|
|
|
FormatDouble = `double`
|
|
|
|
FormatByte = `byte`
|
|
|
|
FormatBinary = `binary`
|
|
|
|
FormatDate = `date`
|
|
|
|
FormatDateTime = `date-time`
|
|
|
|
FormatPassword = `password`
|
|
|
|
)
|
|
|
|
|
2021-10-02 18:54:06 +08:00
|
|
|
const (
|
|
|
|
ParameterInHeader = `header`
|
|
|
|
ParameterInPath = `path`
|
|
|
|
ParameterInQuery = `query`
|
|
|
|
ParameterInCookie = `cookie`
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-10-08 16:02:53 +08:00
|
|
|
TagNamePath = `path`
|
|
|
|
TagNameMethod = `method`
|
|
|
|
TagNameMime = `mime`
|
|
|
|
TagNameValidate = `v`
|
2021-10-02 18:54:06 +08:00
|
|
|
)
|
|
|
|
|
2021-10-02 15:07:47 +08:00
|
|
|
var (
|
2021-10-11 19:59:15 +08:00
|
|
|
defaultReadContentTypes = []string{`application/json`}
|
|
|
|
defaultWriteContentTypes = []string{`application/json`}
|
2021-10-02 14:52:28 +08:00
|
|
|
)
|
|
|
|
|
2021-10-02 18:54:06 +08:00
|
|
|
// New creates and returns a OpenApiV3 implements object.
|
2021-10-02 14:52:28 +08:00
|
|
|
func New() *OpenApiV3 {
|
|
|
|
oai := &OpenApiV3{}
|
|
|
|
oai.fillWithDefaultValue()
|
|
|
|
return oai
|
|
|
|
}
|
|
|
|
|
2021-10-02 18:54:06 +08:00
|
|
|
// AddInput is the structured parameter for function OpenApiV3.Add.
|
2021-10-02 14:52:28 +08:00
|
|
|
type AddInput struct {
|
2021-10-02 18:54:06 +08:00
|
|
|
Path string // Path specifies the custom path if this is not configured in Meta of struct tag.
|
2021-10-08 11:40:47 +08:00
|
|
|
Prefix string // Prefix specifies the custom route path prefix, which will be added with the path tag in Meta of struct tag.
|
2021-10-02 18:54:06 +08:00
|
|
|
Method string // Method specifies the custom HTTP method if this is not configured in Meta of struct tag.
|
|
|
|
Object interface{} // Object can be an instance of struct or a route function.
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
|
2021-10-02 18:54:06 +08:00
|
|
|
// Add adds an instance of struct or a route function to OpenApiV3 definition implements.
|
|
|
|
func (oai *OpenApiV3) Add(in AddInput) error {
|
2021-10-02 14:52:28 +08:00
|
|
|
var (
|
|
|
|
reflectValue = reflect.ValueOf(in.Object)
|
|
|
|
)
|
|
|
|
for reflectValue.Kind() == reflect.Ptr {
|
|
|
|
reflectValue = reflectValue.Elem()
|
|
|
|
}
|
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Struct:
|
2021-10-02 18:54:06 +08:00
|
|
|
return oai.addSchema(in.Object)
|
2021-10-02 14:52:28 +08:00
|
|
|
|
|
|
|
case reflect.Func:
|
2021-10-02 18:54:06 +08:00
|
|
|
return oai.addPath(addPathInput{
|
2021-10-02 14:52:28 +08:00
|
|
|
Path: in.Path,
|
2021-10-08 11:42:28 +08:00
|
|
|
Prefix: in.Prefix,
|
2021-10-02 14:52:28 +08:00
|
|
|
Method: in.Method,
|
|
|
|
Function: in.Object,
|
|
|
|
})
|
|
|
|
|
|
|
|
default:
|
2021-10-02 18:54:06 +08:00
|
|
|
return gerror.NewCodef(
|
2021-10-02 14:52:28 +08:00
|
|
|
gcode.CodeInvalidParameter,
|
|
|
|
`unsupported parameter type "%s", only struct/function type is supported`,
|
|
|
|
reflect.TypeOf(in.Object).String(),
|
2021-10-02 18:54:06 +08:00
|
|
|
)
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (oai OpenApiV3) String() string {
|
|
|
|
b, err := json.Marshal(oai)
|
|
|
|
if err != nil {
|
|
|
|
intlog.Error(context.TODO(), err)
|
|
|
|
}
|
|
|
|
return string(b)
|
|
|
|
}
|
2021-10-02 18:54:06 +08:00
|
|
|
|
2021-10-08 17:05:11 +08:00
|
|
|
func (oai *OpenApiV3) golangTypeToOAIType(t reflect.Type) string {
|
|
|
|
for t.Kind() == reflect.Ptr {
|
|
|
|
t = t.Elem()
|
|
|
|
}
|
|
|
|
switch t.Kind() {
|
|
|
|
case reflect.String:
|
|
|
|
return TypeString
|
|
|
|
|
|
|
|
case reflect.Struct:
|
|
|
|
switch t.String() {
|
|
|
|
case `time.Time`, `gtime.Time`:
|
|
|
|
return TypeString
|
|
|
|
}
|
|
|
|
return TypeObject
|
|
|
|
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
switch t.String() {
|
|
|
|
case `[]uint8`:
|
|
|
|
return TypeString
|
|
|
|
}
|
|
|
|
return TypeArray
|
|
|
|
|
|
|
|
case reflect.Bool:
|
|
|
|
return TypeBoolean
|
|
|
|
|
|
|
|
case
|
|
|
|
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
|
|
reflect.Float32, reflect.Float64,
|
|
|
|
reflect.Complex64, reflect.Complex128:
|
|
|
|
return TypeNumber
|
|
|
|
|
|
|
|
default:
|
|
|
|
return TypeObject
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// golangTypeToOAIFormat converts and returns OpenAPI parameter format for given golang type `t`.
|
|
|
|
// Note that it does not return standard OpenAPI parameter format but custom format in golang type.
|
|
|
|
func (oai *OpenApiV3) golangTypeToOAIFormat(t reflect.Type) string {
|
|
|
|
format := t.String()
|
|
|
|
switch gstr.TrimLeft(format, "*") {
|
|
|
|
case `[]uint8`:
|
|
|
|
return FormatBinary
|
|
|
|
|
|
|
|
default:
|
|
|
|
return format
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-02 18:54:06 +08:00
|
|
|
func formatRefToBytes(ref string) []byte {
|
|
|
|
return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
|
|
|
|
}
|
2021-10-06 21:51:21 +08:00
|
|
|
|
|
|
|
func golangTypeToSchemaName(t reflect.Type) string {
|
2021-10-11 16:55:38 +08:00
|
|
|
var (
|
|
|
|
s = gstr.TrimLeft(t.String(), "*")
|
|
|
|
)
|
|
|
|
s = gstr.ReplaceByMap(s, map[string]string{
|
|
|
|
` `: ``,
|
|
|
|
`{`: ``,
|
|
|
|
`}`: ``,
|
|
|
|
})
|
|
|
|
return s
|
2021-10-06 21:51:21 +08:00
|
|
|
}
|