add sort feature for path of openapi

This commit is contained in:
John Guo
2022-03-24 21:56:37 +08:00
parent 96a135834a
commit 6664437b06
21 changed files with 350 additions and 288 deletions

View File

@ -36,9 +36,9 @@ type SortedArray struct {
// NewSortedArray creates and returns an empty sorted array.
// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default.
// The parameter `comparator` used to compare values to sort in array,
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
// if it returns value < 0, means `a` < `b`; the `a` will be inserted before `b`;
// if it returns value = 0, means `a` = `b`; the `a` will be replaced by `b`;
// if it returns value > 0, means `a` > `b`; the `a` will be inserted after `b`;
func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
return NewSortedArraySize(0, comparator, safe...)
}

View File

@ -9,7 +9,7 @@ import (
)
type HelloReq struct {
g.Meta `path:"/hello" method:"get"`
g.Meta `path:"/hello" method:"get" sort:"1"`
Name string `v:"required" dc:"Your name"`
}
type HelloRes struct {

View File

@ -11,7 +11,6 @@ import (
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/protocol/goai"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
@ -25,35 +24,23 @@ func (s *Server) initOpenApi() {
err error
method string
)
for routeKey, registeredItems := range s.routesMap {
array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, routeKey)
for _, registeredItem := range registeredItems {
item := RouterItem{
Server: s.config.Name,
Domain: array[4],
Type: registeredItem.Handler.Type,
Middleware: array[1],
Method: array[2],
Route: array[3],
Handler: registeredItem.Handler,
}
switch item.Type {
case HandlerTypeMiddleware, HandlerTypeHook:
continue
}
method = item.Method
if gstr.Equal(method, defaultMethod) {
method = ""
}
if item.Handler.Info.Func == nil {
err = s.openapi.Add(goai.AddInput{
Path: item.Route,
Method: method,
Object: item.Handler.Info.Value.Interface(),
})
if err != nil {
s.Logger().Fatalf(ctx, `%+v`, err)
}
for _, item := range s.GetRoutes() {
switch item.Type {
case HandlerTypeMiddleware, HandlerTypeHook:
continue
}
method = item.Method
if gstr.Equal(method, defaultMethod) {
method = ""
}
if item.Handler.Info.Func == nil {
err = s.openapi.Add(goai.AddInput{
Path: item.Route,
Method: method,
Object: item.Handler.Info.Value.Interface(),
})
if err != nil {
s.Logger().Fatalf(ctx, `%+v`, err)
}
}
}
@ -61,13 +48,11 @@ func (s *Server) initOpenApi() {
// openapiSpec is a build-in handler automatic producing for openapi specification json file.
func (s *Server) openapiSpec(r *Request) {
var (
err error
)
var err error
if s.config.OpenApiPath == "" {
r.Response.Write(`OpenApi specification file producing is disabled`)
} else {
err = r.Response.WriteJson(s.openapi)
err = r.Response.WriteJson(s.openapi.MustJson())
}
if err != nil {

View File

@ -10,10 +10,13 @@
package goai
import (
"bytes"
"context"
"fmt"
"reflect"
"github.com/gogf/gf/v2/container/garray"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
@ -25,15 +28,15 @@ import (
// https://swagger.io/specification/
// 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"`
Config Config `json:"-"`
OpenAPI string `json:"openapi"`
Components Components `json:"components,omitempty"`
Info Info `json:"info"`
Paths Paths `json:"paths"`
Security *SecurityRequirements `json:"security,omitempty"`
Servers *Servers `json:"servers,omitempty"`
Tags *Tags `json:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
}
// ExternalDocs is specified by OpenAPI/Swagger standard version 3.0.
@ -238,6 +241,63 @@ func (oai *OpenApiV3) fileMapWithShortTags(m map[string]string) map[string]strin
return m
}
func (oai OpenApiV3) MustJson() []byte {
b, err := oai.Json()
if err != nil {
panic(err)
}
return b
}
// Json converts current OpenApi object to JSON format.
// Note that this function support `Sort` feature of Path which sets the Path in custom sequence.
func (oai OpenApiV3) Json() ([]byte, error) {
// Marshal Paths in sequence by `Sort` attribute.
type PathItem struct {
Name string
Path Path
}
array := garray.NewSortedArray(func(a, b interface{}) int {
path1 := a.(PathItem)
path2 := b.(PathItem)
return path1.Path.Sort - path2.Path.Sort
})
for name, path := range oai.Paths {
array.Add(PathItem{
Name: name,
Path: path,
})
}
buffer := bytes.NewBuffer(nil)
buffer.WriteString("{")
array.Iterator(func(k int, v interface{}) bool {
if buffer.Len() > 1 {
buffer.WriteString(",")
}
item := v.(PathItem)
buffer.WriteString(fmt.Sprintf(`"%s":%s`, item.Name, gjson.MustEncodeString(item.Path)))
return true
})
buffer.WriteString("}")
// Produce JSON content.
oaiJsonBytes, err := json.Marshal(oai)
if err != nil {
return nil, err
}
oaiJson, err := gjson.LoadJson(oaiJsonBytes)
if err != nil {
return nil, err
}
pathJson, err := gjson.LoadJson(buffer.Bytes())
if err != nil {
return nil, err
}
if err = oaiJson.Set(`paths`, pathJson); err != nil {
return nil, err
}
return oaiJson.ToJson()
}
func formatRefToBytes(ref string) []byte {
return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
}

View File

@ -8,15 +8,15 @@ package goai
// Components is specified by OpenAPI/Swagger standard version 3.0.
type Components struct {
Schemas Schemas `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Parameters ParametersMap `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
RequestBodies RequestBodies `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Responses Responses `json:"responses,omitempty" yaml:"responses,omitempty"`
SecuritySchemes SecuritySchemes `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"`
Links Links `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Schemas Schemas `json:"schemas,omitempty"`
Parameters ParametersMap `json:"parameters,omitempty"`
Headers Headers `json:"headers,omitempty"`
RequestBodies RequestBodies `json:"requestBodies,omitempty"`
Responses Responses `json:"responses,omitempty"`
SecuritySchemes SecuritySchemes `json:"securitySchemes,omitempty"`
Examples Examples `json:"examples,omitempty"`
Links Links `json:"links,omitempty"`
Callbacks Callbacks `json:"callbacks,omitempty"`
}
type ParametersMap map[string]*ParameterRef

View File

@ -12,10 +12,10 @@ import (
// Example is specified by OpenAPI/Swagger 3.0 standard.
type Example struct {
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Value interface{} `json:"value,omitempty" yaml:"value,omitempty"`
ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Value interface{} `json:"value,omitempty"`
ExternalValue string `json:"externalValue,omitempty"`
}
type Examples map[string]*ExampleRef

View File

@ -8,23 +8,23 @@ package goai
// Info is specified by OpenAPI/Swagger standard version 3.0.
type Info struct {
Title string `json:"title" yaml:"title"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
License *License `json:"license,omitempty" yaml:"license,omitempty"`
Version string `json:"version" yaml:"version"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
TermsOfService string `json:"termsOfService,omitempty"`
Contact *Contact `json:"contact,omitempty"`
License *License `json:"license,omitempty"`
Version string `json:"version"`
}
// Contact is specified by OpenAPI/Swagger standard version 3.0.
type Contact struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
Email string `json:"email,omitempty" yaml:"email,omitempty"`
Name string `json:"name,omitempty"`
URL string `json:"url,omitempty"`
Email string `json:"email,omitempty"`
}
// License is specified by OpenAPI/Swagger standard version 3.0.
type License struct {
Name string `json:"name" yaml:"name"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
Name string `json:"name"`
URL string `json:"url,omitempty"`
}

View File

@ -12,12 +12,12 @@ import (
// Link is specified by OpenAPI/Swagger standard version 3.0.
type Link struct {
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Parameters map[string]interface{} `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Server *Server `json:"server,omitempty" yaml:"server,omitempty"`
RequestBody interface{} `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
OperationID string `json:"operationId,omitempty"`
OperationRef string `json:"operationRef,omitempty"`
Description string `json:"description,omitempty"`
Parameters map[string]interface{} `json:"parameters,omitempty"`
Server *Server `json:"server,omitempty"`
RequestBody interface{} `json:"requestBody,omitempty"`
}
type Links map[string]LinkRef

View File

@ -8,10 +8,10 @@ package goai
// MediaType is specified by OpenAPI/Swagger 3.0 standard.
type MediaType struct {
Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
Examples Examples `json:"examples,omitempty" yaml:"examples,omitempty"`
Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"`
Schema *SchemaRef `json:"schema,omitempty"`
Example interface{} `json:"example,omitempty"`
Examples Examples `json:"examples,omitempty"`
Encoding map[string]*Encoding `json:"encoding,omitempty"`
}
// Content is specified by OpenAPI/Swagger 3.0 standard.
@ -19,9 +19,9 @@ type Content map[string]MediaType
// Encoding is specified by OpenAPI/Swagger 3.0 standard.
type Encoding struct {
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
ContentType string `json:"contentType,omitempty"`
Headers Headers `json:"headers,omitempty"`
Style string `json:"style,omitempty"`
Explode *bool `json:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty"`
}

View File

@ -8,16 +8,16 @@ package goai
// Operation represents "operation" specified by OpenAPI/Swagger 3.0 standard.
type Operation struct {
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"`
RequestBody *RequestBodyRef `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
Responses Responses `json:"responses" yaml:"responses"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Callbacks *Callbacks `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Security *SecurityRequirements `json:"security,omitempty" yaml:"security,omitempty"`
Servers *Servers `json:"servers,omitempty" yaml:"servers,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Tags []string `json:"tags,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
OperationID string `json:"operationId,omitempty"`
Parameters Parameters `json:"parameters,omitempty"`
RequestBody *RequestBodyRef `json:"requestBody,omitempty"`
Responses Responses `json:"responses"`
Deprecated bool `json:"deprecated,omitempty"`
Callbacks *Callbacks `json:"callbacks,omitempty"`
Security *SecurityRequirements `json:"security,omitempty"`
Servers *Servers `json:"servers,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
}

View File

@ -21,19 +21,19 @@ import (
// Parameter is specified by OpenAPI/Swagger 3.0 standard.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#parameterObject
type Parameter struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
In string `json:"in,omitempty" yaml:"in,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Schema *SchemaRef `json:"schema,omitempty" yaml:"schema,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
Examples *Examples `json:"examples,omitempty" yaml:"examples,omitempty"`
Content *Content `json:"content,omitempty" yaml:"content,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Description string `json:"description,omitempty"`
Style string `json:"style,omitempty"`
Explode *bool `json:"explode,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Required bool `json:"required,omitempty"`
Schema *SchemaRef `json:"schema,omitempty"`
Example interface{} `json:"example,omitempty"`
Examples *Examples `json:"examples,omitempty"`
Content *Content `json:"content,omitempty"`
}
// Parameters is specified by OpenAPI/Swagger 3.0 standard.

View File

@ -18,22 +18,26 @@ import (
)
type Path struct {
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Connect *Operation `json:"connect,omitempty" yaml:"connect,omitempty"`
Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"`
Servers Servers `json:"servers,omitempty" yaml:"servers,omitempty"`
Parameters Parameters `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Ref string `json:"$ref,omitempty"`
Summary string `json:"summary,omitempty"`
Description string `json:"description,omitempty"`
Connect *Operation `json:"connect,omitempty"`
Delete *Operation `json:"delete,omitempty"`
Get *Operation `json:"get,omitempty"`
Head *Operation `json:"head,omitempty"`
Options *Operation `json:"options,omitempty"`
Patch *Operation `json:"patch,omitempty"`
Post *Operation `json:"post,omitempty"`
Put *Operation `json:"put,omitempty"`
Trace *Operation `json:"trace,omitempty"`
Servers Servers `json:"servers,omitempty"`
Parameters Parameters `json:"parameters,omitempty"`
Sort int `json:"-"`
}
// Paths are specified by OpenAPI/Swagger standard version 3.0.
type Paths map[string]Path
const (
responseOkKey = `200`
)
@ -46,6 +50,10 @@ type addPathInput struct {
}
func (oai *OpenApiV3) addPath(in addPathInput) error {
if oai.Paths == nil {
oai.Paths = map[string]Path{}
}
var (
reflectType = reflect.TypeOf(in.Function)
)
@ -99,8 +107,8 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
)
}
if v := oai.Paths.Get(in.Path); v != nil {
path = *v
if v, ok := oai.Paths[in.Path]; ok {
path = v
}
// Method check.
@ -273,7 +281,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
default:
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid method "%s"`, in.Method)
}
oai.Paths.Set(in.Path, path)
oai.Paths[in.Path] = path
return nil
}

View File

@ -1,52 +0,0 @@
// 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
import (
"github.com/gogf/gf/v2/container/gmap"
)
// Paths are specified by OpenAPI/Swagger standard version 3.0.
type Paths struct {
paths *gmap.ListMap // map[string]Path
}
func (p *Paths) init() {
if p.paths == nil {
p.paths = gmap.NewListMap()
}
}
func (p *Paths) Get(name string) *Path {
p.init()
value := p.paths.Get(name)
if value != nil {
path := value.(Path)
return &path
}
return nil
}
func (p *Paths) Set(name string, path Path) {
p.init()
p.paths.Set(name, path)
}
func (p *Paths) Map() map[string]Path {
p.init()
m := make(map[string]Path)
p.paths.Iterator(func(key, value interface{}) bool {
m[key.(string)] = value.(Path)
return true
})
return m
}
func (p Paths) MarshalJSON() ([]byte, error) {
p.init()
return p.paths.MarshalJSON()
}

View File

@ -16,9 +16,9 @@ import (
// RequestBody is specified by OpenAPI/Swagger 3.0 standard.
type RequestBody struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Content Content `json:"content,omitempty" yaml:"content,omitempty"`
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
Content Content `json:"content,omitempty"`
}
type RequestBodyRef struct {

View File

@ -16,10 +16,10 @@ import (
// Response is specified by OpenAPI/Swagger 3.0 standard.
type Response struct {
Description string `json:"description" yaml:"description"`
Headers Headers `json:"headers,omitempty" yaml:"headers,omitempty"`
Content Content `json:"content,omitempty" yaml:"content,omitempty"`
Links Links `json:"links,omitempty" yaml:"links,omitempty"`
Description string `json:"description"`
Headers Headers `json:"headers,omitempty"`
Content Content `json:"content,omitempty"`
Links Links `json:"links,omitempty"`
}
// Responses is specified by OpenAPI/Swagger 3.0 standard.

View File

@ -11,14 +11,14 @@ import (
)
type SecurityScheme struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
In string `json:"in,omitempty" yaml:"in,omitempty"`
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"`
Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"`
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"`
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
In string `json:"in,omitempty"`
Scheme string `json:"scheme,omitempty"`
BearerFormat string `json:"bearerFormat,omitempty"`
Flows *OAuthFlows `json:"flows,omitempty"`
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty"`
}
type SecuritySchemes map[string]SecuritySchemeRef
@ -33,17 +33,17 @@ type SecurityRequirements []SecurityRequirement
type SecurityRequirement map[string][]string
type OAuthFlows struct {
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"`
Implicit *OAuthFlow `json:"implicit,omitempty"`
Password *OAuthFlow `json:"password,omitempty"`
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty"`
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty"`
}
type OAuthFlow struct {
AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
RefreshURL string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"`
Scopes map[string]string `json:"scopes" yaml:"scopes"`
AuthorizationURL string `json:"authorizationUrl,omitempty"`
TokenURL string `json:"tokenUrl,omitempty"`
RefreshURL string `json:"refreshUrl,omitempty"`
Scopes map[string]string `json:"scopes"`
}
func (r SecuritySchemeRef) MarshalJSON() ([]byte, error) {

View File

@ -8,16 +8,16 @@ package goai
// Server is specified by OpenAPI/Swagger standard version 3.0.
type Server struct {
URL string `json:"url" yaml:"url"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Variables map[string]*ServerVariable `json:"variables,omitempty" yaml:"variables,omitempty"`
URL string `json:"url"`
Description string `json:"description,omitempty"`
Variables map[string]*ServerVariable `json:"variables,omitempty"`
}
// ServerVariable is specified by OpenAPI/Swagger standard version 3.0.
type ServerVariable struct {
Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"`
Default string `json:"default,omitempty" yaml:"default,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
Default string `json:"default,omitempty"`
Description string `json:"description,omitempty"`
}
// Servers is specified by OpenAPI/Swagger standard version 3.0.

View File

@ -22,48 +22,48 @@ import (
// Schema is specified by OpenAPI/Swagger 3.0 standard.
type Schema struct {
OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
AnyOf SchemaRefs `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
AllOf SchemaRefs `json:"allOf,omitempty" yaml:"allOf,omitempty"`
Not *SchemaRef `json:"not,omitempty" yaml:"not,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
ExclusiveMin bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
ExclusiveMax bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"`
Nullable bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
XML interface{} `json:"xml,omitempty" yaml:"xml,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Min *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
Max *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
MinLength uint64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
MaxLength *uint64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
MinItems uint64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
MaxItems *uint64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
Items *SchemaRef `json:"items,omitempty" yaml:"items,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Properties Schemas `json:"properties,omitempty" yaml:"properties,omitempty"`
MinProps uint64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
MaxProps *uint64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty" yaml:"additionalProperties"`
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
OneOf SchemaRefs `json:"oneOf,omitempty"`
AnyOf SchemaRefs `json:"anyOf,omitempty"`
AllOf SchemaRefs `json:"allOf,omitempty"`
Not *SchemaRef `json:"not,omitempty"`
Type string `json:"type,omitempty"`
Title string `json:"title,omitempty"`
Format string `json:"format,omitempty"`
Description string `json:"description,omitempty"`
Enum []interface{} `json:"enum,omitempty"`
Default interface{} `json:"default,omitempty"`
Example interface{} `json:"example,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
ExclusiveMin bool `json:"exclusiveMinimum,omitempty"`
ExclusiveMax bool `json:"exclusiveMaximum,omitempty"`
Nullable bool `json:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
WriteOnly bool `json:"writeOnly,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`
XML interface{} `json:"xml,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
Min *float64 `json:"minimum,omitempty"`
Max *float64 `json:"maximum,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
MinLength uint64 `json:"minLength,omitempty"`
MaxLength *uint64 `json:"maxLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
MinItems uint64 `json:"minItems,omitempty"`
MaxItems *uint64 `json:"maxItems,omitempty"`
Items *SchemaRef `json:"items,omitempty"`
Required []string `json:"required,omitempty"`
Properties Schemas `json:"properties,omitempty"`
MinProps uint64 `json:"minProperties,omitempty"`
MaxProps *uint64 `json:"maxProperties,omitempty"`
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"`
Discriminator *Discriminator `json:"discriminator,omitempty"`
}
// Discriminator is specified by OpenAPI/Swagger standard version 3.0.
type Discriminator struct {
PropertyName string `json:"propertyName" yaml:"propertyName"`
Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
PropertyName string `json:"propertyName"`
Mapping map[string]string `json:"mapping,omitempty"`
}
// addSchema creates schemas with objects.

View File

@ -11,7 +11,7 @@ type Tags []Tag
// Tag is specified by OpenAPI/Swagger 3.0 standard.
type Tag struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
}

View File

@ -0,0 +1,61 @@
// 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_test
import (
"context"
"testing"
"github.com/gogf/gf/v2/protocol/goai"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gmeta"
)
func TestOpenApiV3_Path_Sort(t *testing.T) {
type Req1 struct {
gmeta.Meta `method:"GET" sort:"1"`
Name1 string `json:"name1" in:"query" `
}
type Res1 struct{}
type Req2 struct {
gmeta.Meta `method:"GET" sort:"2"`
Name2 string `json:"name2" in:"query"`
}
type Res2 struct{}
f1 := func(ctx context.Context, req *Req1) (res *Res1, err error) {
return
}
f2 := func(ctx context.Context, req *Req2) (res *Res2, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
for i := 0; i < 100; i++ {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/index2",
Object: f2,
})
t.AssertNil(err)
err = oai.Add(goai.AddInput{
Path: "/index1",
Object: f1,
})
t.AssertNil(err)
b, err := oai.Json()
t.AssertNil(err)
t.Assert(gstr.Contains(string(b), `"paths":{"/index1":`), true)
}
})
}

View File

@ -118,13 +118,13 @@ func TestOpenApiV3_Add(t *testing.T) {
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Get(`Params`).Value.Type, goai.TypeArray)
// Paths.
t.Assert(len(oai.Paths.Map()), 1)
t.AssertNE(oai.Paths.Map()[`/test1/{appId}`].Put, nil)
t.Assert(len(oai.Paths.Map()[`/test1/{appId}`].Put.Tags), 1)
t.Assert(len(oai.Paths.Map()[`/test1/{appId}`].Put.Parameters), 2)
t.AssertNE(oai.Paths.Map()[`/test1/{appId}`].Post, nil)
t.Assert(len(oai.Paths.Map()[`/test1/{appId}`].Post.Tags), 1)
t.Assert(len(oai.Paths.Map()[`/test1/{appId}`].Post.Parameters), 2)
t.Assert(len(oai.Paths), 1)
t.AssertNE(oai.Paths[`/test1/{appId}`].Put, nil)
t.Assert(len(oai.Paths[`/test1/{appId}`].Put.Tags), 1)
t.Assert(len(oai.Paths[`/test1/{appId}`].Put.Parameters), 2)
t.AssertNE(oai.Paths[`/test1/{appId}`].Post, nil)
t.Assert(len(oai.Paths[`/test1/{appId}`].Post.Tags), 1)
t.Assert(len(oai.Paths[`/test1/{appId}`].Post.Parameters), 2)
})
}
@ -223,15 +223,15 @@ func TestOpenApiV3_Add_AutoDetectIn(t *testing.T) {
fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 2)
t.Assert(len(oai.Paths.Map()), 1)
t.AssertNE(oai.Paths.Map()[path].Get, nil)
t.Assert(len(oai.Paths.Map()[path].Get.Parameters), 3)
t.Assert(oai.Paths.Map()[path].Get.Parameters[0].Value.Name, `Name`)
t.Assert(oai.Paths.Map()[path].Get.Parameters[0].Value.In, goai.ParameterInPath)
t.Assert(oai.Paths.Map()[path].Get.Parameters[1].Value.Name, `Product`)
t.Assert(oai.Paths.Map()[path].Get.Parameters[1].Value.In, goai.ParameterInPath)
t.Assert(oai.Paths.Map()[path].Get.Parameters[2].Value.Name, `Region`)
t.Assert(oai.Paths.Map()[path].Get.Parameters[2].Value.In, goai.ParameterInQuery)
t.Assert(len(oai.Paths), 1)
t.AssertNE(oai.Paths[path].Get, nil)
t.Assert(len(oai.Paths[path].Get.Parameters), 3)
t.Assert(oai.Paths[path].Get.Parameters[0].Value.Name, `Name`)
t.Assert(oai.Paths[path].Get.Parameters[0].Value.In, goai.ParameterInPath)
t.Assert(oai.Paths[path].Get.Parameters[1].Value.Name, `Product`)
t.Assert(oai.Paths[path].Get.Parameters[1].Value.In, goai.ParameterInPath)
t.Assert(oai.Paths[path].Get.Parameters[2].Value.Name, `Region`)
t.Assert(oai.Paths[path].Get.Parameters[2].Value.In, goai.ParameterInQuery)
})
}
@ -275,8 +275,8 @@ func TestOpenApiV3_CommonRequest(t *testing.T) {
t.AssertNil(err)
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
})
}
@ -320,8 +320,8 @@ func TestOpenApiV3_CommonRequest_WithoutDataField_Setting(t *testing.T) {
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 5)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 5)
})
}
@ -360,8 +360,8 @@ func TestOpenApiV3_CommonRequest_EmptyRequest(t *testing.T) {
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
})
}
@ -415,9 +415,9 @@ func TestOpenApiV3_CommonRequest_SubDataField(t *testing.T) {
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 4)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Get(`Request`).Value.Properties.Map()), 4)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Get(`Request`).Value.Properties.Map()), 4)
})
}
@ -460,13 +460,13 @@ func TestOpenApiV3_CommonResponse(t *testing.T) {
})
t.AssertNil(err)
//g.Dump(oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map())
//g.Dump(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map())
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(
oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get("data").Value.Description,
oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get("data").Value.Description,
`Result data for certain request according API definition`,
)
})
@ -512,8 +512,8 @@ func TestOpenApiV3_CommonResponse_WithoutDataField_Setting(t *testing.T) {
// Schema asserts.
fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 8)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 8)
})
}
@ -552,9 +552,9 @@ func TestOpenApiV3_CommonResponse_EmptyResponse(t *testing.T) {
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(oai.Paths.Map()["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Ref, `github.com.gogf.gf.v2.protocol.goai_test.Req`)
t.Assert(len(oai.Paths.Map()["/index"].Put.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Ref, `github.com.gogf.gf.v2.protocol.goai_test.Req`)
t.Assert(len(oai.Paths["/index"].Put.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
})
}
@ -608,9 +608,9 @@ func TestOpenApiV3_CommonResponse_SubDataField(t *testing.T) {
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 4)
t.Assert(len(oai.Paths.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
t.Assert(len(oai.Paths.Map()["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get(`Response`).Value.Properties.Map()), 7)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get(`Response`).Value.Properties.Map()), 7)
})
}
@ -664,7 +664,7 @@ func TestOpenApiV3_ShortTags(t *testing.T) {
// fmt.Println(oai.String())
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(oai.Paths.Map()[`/test1/{appId}`].Summary, `CreateResourceReq sum`)
t.Assert(oai.Paths[`/test1/{appId}`].Summary, `CreateResourceReq sum`)
t.Assert(oai.
Components.
Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).