move package goai from protocol to net

This commit is contained in:
John Guo
2022-05-24 18:53:10 +08:00
parent 331a29024e
commit ea79b3cbb8
31 changed files with 15 additions and 7 deletions

View File

@ -16,10 +16,10 @@ import (
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gtype"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/net/gsvc"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/gsession"
"github.com/gogf/gf/v2/protocol/goai"
)
type (

View File

@ -25,6 +25,7 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/net/ghttp/internal/swaggerui"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/genv"
"github.com/gogf/gf/v2/os/gfile"
@ -32,7 +33,6 @@ import (
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gsession"
"github.com/gogf/gf/v2/os/gtimer"
"github.com/gogf/gf/v2/protocol/goai"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"

View File

@ -10,7 +10,7 @@ import (
"context"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/protocol/goai"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/text/gstr"
)

View File

@ -19,7 +19,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/protocol/goai"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gmeta"

249
net/goai/goai.go Normal file
View File

@ -0,0 +1,249 @@
// 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/
package goai
import (
"context"
"fmt"
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/text/gstr"
)
// OpenApiV3 is the structure defined from:
// https://swagger.io/specification/
// https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md
type OpenApiV3 struct {
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"`
}
const (
HttpMethodGet = `GET`
HttpMethodPut = `PUT`
HttpMethodPost = `POST`
HttpMethodDelete = `DELETE`
HttpMethodConnect = `CONNECT`
HttpMethodHead = `HEAD`
HttpMethodOptions = `OPTIONS`
HttpMethodPatch = `PATCH`
HttpMethodTrace = `TRACE`
)
const (
TypeInteger = `integer`
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`
)
const (
ParameterInHeader = `header`
ParameterInPath = `path`
ParameterInQuery = `query`
ParameterInCookie = `cookie`
)
const (
TagNamePath = `path`
TagNameMethod = `method`
TagNameMime = `mime`
TagNameConsumes = `consumes`
TagNameType = `type`
TagNameDomain = `domain`
)
const (
validationRuleKeyForRequired = `required`
validationRuleKeyForIn = `in:`
)
var (
defaultReadContentTypes = []string{`application/json`}
defaultWriteContentTypes = []string{`application/json`}
shortTypeMapForTag = map[string]string{
"d": "Default",
"sum": "Summary",
"sm": "Summary",
"des": "Description",
"dc": "Description",
"eg": "Example",
"egs": "Examples",
"ed": "ExternalDocs",
}
)
// New creates and returns a OpenApiV3 implements object.
func New() *OpenApiV3 {
oai := &OpenApiV3{}
oai.fillWithDefaultValue()
return oai
}
// AddInput is the structured parameter for function OpenApiV3.Add.
type AddInput struct {
Path string // Path specifies the custom path if this is not configured in Meta of struct tag.
Prefix string // Prefix specifies the custom route path prefix, which will be added with the path tag in Meta of struct tag.
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.
}
// Add adds an instance of struct or a route function to OpenApiV3 definition implements.
func (oai *OpenApiV3) Add(in AddInput) error {
var (
reflectValue = reflect.ValueOf(in.Object)
)
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
switch reflectValue.Kind() {
case reflect.Struct:
return oai.addSchema(in.Object)
case reflect.Func:
return oai.addPath(addPathInput{
Path: in.Path,
Prefix: in.Prefix,
Method: in.Method,
Function: in.Object,
})
default:
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupported parameter type "%s", only struct/function type is supported`,
reflect.TypeOf(in.Object).String(),
)
}
}
func (oai OpenApiV3) String() string {
b, err := json.Marshal(oai)
if err != nil {
intlog.Errorf(context.TODO(), `%+v`, err)
}
return string(b)
}
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:
return TypeInteger
case
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
}
}
func (oai *OpenApiV3) golangTypeToSchemaName(t reflect.Type) string {
var (
pkgPath = ""
schemaName = gstr.TrimLeft(t.String(), "*")
)
// Pointer type has no PkgPath.
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
if pkgPath = t.PkgPath(); pkgPath != "" && pkgPath != "." {
if !oai.Config.IgnorePkgPath {
schemaName = gstr.Replace(pkgPath, `/`, `.`) + gstr.SubStrFrom(schemaName, ".")
}
}
schemaName = gstr.ReplaceByMap(schemaName, map[string]string{
` `: ``,
`{`: ``,
`}`: ``,
})
return schemaName
}
func (oai *OpenApiV3) fileMapWithShortTags(m map[string]string) map[string]string {
for k, v := range shortTypeMapForTag {
if m[v] == "" && m[k] != "" {
m[v] = m[k]
}
}
return m
}
func formatRefToBytes(ref string) []byte {
return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
}
func isValidParameterName(key string) bool {
if key == "-" {
return false
}
return true
}

28
net/goai/goai_callback.go Normal file
View File

@ -0,0 +1,28 @@
// 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/internal/json"
)
// Callback is specified by OpenAPI/Swagger standard version 3.0.
type Callback map[string]*Path
type Callbacks map[string]*CallbackRef
type CallbackRef struct {
Ref string
Value *Callback
}
func (r CallbackRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

View File

@ -0,0 +1,24 @@
// 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
// Components is specified by OpenAPI/Swagger standard version 3.0.
type Components struct {
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
type RequestBodies map[string]*RequestBodyRef

31
net/goai/goai_config.go Normal file
View File

@ -0,0 +1,31 @@
// 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
// Config provides extra configuration feature for OpenApiV3 implements.
type Config struct {
ReadContentTypes []string // ReadContentTypes specifies the default MIME types for consuming if MIME types are not configured.
WriteContentTypes []string // WriteContentTypes specifies the default MIME types for producing if MIME types are not configured.
CommonRequest interface{} // Common request structure for all paths.
CommonRequestDataField string // Common request field name to be replaced with certain business request structure. Eg: `Data`, `Request.`.
CommonResponse interface{} // Common response structure for all paths.
CommonResponseDataField string // Common response field name to be replaced with certain business response structure. Eg: `Data`, `Response.`.
IgnorePkgPath bool // Ignores package name for schema name.
}
// fillWithDefaultValue fills configuration object of `oai` with default values if these are not configured.
func (oai *OpenApiV3) fillWithDefaultValue() {
if oai.OpenAPI == "" {
oai.OpenAPI = `3.0.0`
}
if len(oai.Config.ReadContentTypes) == 0 {
oai.Config.ReadContentTypes = defaultReadContentTypes
}
if len(oai.Config.WriteContentTypes) == 0 {
oai.Config.WriteContentTypes = defaultWriteContentTypes
}
}

33
net/goai/goai_example.go Normal file
View File

@ -0,0 +1,33 @@
// 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/internal/json"
)
// Example is specified by OpenAPI/Swagger 3.0 standard.
type Example struct {
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
type ExampleRef struct {
Ref string
Value *Example
}
func (r ExampleRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

View File

@ -0,0 +1,35 @@
// 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/internal/json"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
)
// ExternalDocs is specified by OpenAPI/Swagger standard version 3.0.
type ExternalDocs struct {
URL string `json:"url,omitempty"`
Description string `json:"description,omitempty"`
}
func (ed *ExternalDocs) UnmarshalValue(value interface{}) error {
var valueBytes = gconv.Bytes(value)
if json.Valid(valueBytes) {
return json.UnmarshalUseNumber(valueBytes, ed)
}
var (
valueString = string(valueBytes)
valueArray = gstr.Split(valueString, "|")
)
ed.URL = valueArray[0]
if len(valueArray) > 1 {
ed.Description = valueArray[1]
}
return nil
}

31
net/goai/goai_header.go Normal file
View File

@ -0,0 +1,31 @@
// 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/internal/json"
)
// Header is specified by OpenAPI/Swagger 3.0 standard.
// See https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#headerObject
type Header struct {
Parameter
}
type Headers map[string]HeaderRef
type HeaderRef struct {
Ref string
Value *Header
}
func (r HeaderRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

30
net/goai/goai_info.go Normal file
View File

@ -0,0 +1,30 @@
// 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
// Info is specified by OpenAPI/Swagger standard version 3.0.
type Info struct {
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"`
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"`
URL string `json:"url,omitempty"`
}

35
net/goai/goai_link.go Normal file
View File

@ -0,0 +1,35 @@
// 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/internal/json"
)
// Link is specified by OpenAPI/Swagger standard version 3.0.
type Link struct {
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
type LinkRef struct {
Ref string
Value *Link
}
func (r LinkRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

View File

@ -0,0 +1,27 @@
// 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
// MediaType is specified by OpenAPI/Swagger 3.0 standard.
type MediaType struct {
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.
type Content map[string]MediaType
// Encoding is specified by OpenAPI/Swagger 3.0 standard.
type Encoding struct {
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

@ -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
import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
// Operation represents "operation" specified by OpenAPI/Swagger 3.0 standard.
type Operation struct {
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"`
XExtensions XExtensions `json:"-"`
}
func (oai *OpenApiV3) tagMapToOperation(tagMap map[string]string, operation *Operation) error {
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, operation); err != nil {
return gerror.Wrap(err, `mapping struct tags to Operation failed`)
}
oai.tagMapToXExtensions(mergedTagMap, operation.XExtensions)
return nil
}
func (o Operation) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempOperation Operation // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempOperation(o)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range o.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}

View File

@ -0,0 +1,63 @@
// 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/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
// 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"`
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"`
XExtensions XExtensions `json:"-"`
}
func (oai *OpenApiV3) tagMapToParameter(tagMap map[string]string, parameter *Parameter) error {
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, parameter); err != nil {
return gerror.Wrap(err, `mapping struct tags to Parameter failed`)
}
oai.tagMapToXExtensions(mergedTagMap, parameter.XExtensions)
return nil
}
func (p Parameter) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempParameter Parameter // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempParameter(p)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range p.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}

View File

@ -0,0 +1,101 @@
// 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 (
"fmt"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
// Parameters is specified by OpenAPI/Swagger 3.0 standard.
type Parameters []ParameterRef
type ParameterRef struct {
Ref string
Value *Parameter
}
func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path, method string) (*ParameterRef, error) {
var (
tagMap = field.TagMap()
parameter = &Parameter{
Name: field.TagJsonName(),
XExtensions: make(XExtensions),
}
)
if parameter.Name == "" {
parameter.Name = field.Name()
}
if len(tagMap) > 0 {
if err := oai.tagMapToParameter(tagMap, parameter); err != nil {
return nil, err
}
}
if parameter.In == "" {
// Automatically detect its "in" attribute.
if gstr.ContainsI(path, fmt.Sprintf(`{%s}`, parameter.Name)) {
parameter.In = ParameterInPath
} else {
// Default the parameter input to "query" if method is "GET/DELETE".
switch gstr.ToUpper(method) {
case HttpMethodGet, HttpMethodDelete:
parameter.In = ParameterInQuery
default:
return nil, nil
}
}
}
switch parameter.In {
case ParameterInPath:
// Required for path parameter.
parameter.Required = true
case ParameterInCookie, ParameterInHeader, ParameterInQuery:
default:
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid tag value "%s" for In`, parameter.In)
}
// Necessary schema or content.
schemaRef, err := oai.newSchemaRefWithGolangType(field.Type().Type, tagMap)
if err != nil {
return nil, err
}
parameter.Schema = schemaRef
// Ignore parameter.
if !isValidParameterName(parameter.Name) {
return nil, nil
}
// Required check.
if parameter.Schema.Value != nil && parameter.Schema.Value.ValidationRules != "" {
validationRuleArray := gstr.Split(parameter.Schema.Value.ValidationRules, "|")
if gset.NewStrSetFrom(validationRuleArray).Contains(validationRuleKeyForRequired) {
parameter.Required = true
}
}
return &ParameterRef{
Ref: "",
Value: parameter,
}, nil
}
func (r ParameterRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

323
net/goai/goai_path.go Normal file
View File

@ -0,0 +1,323 @@
// 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 (
"reflect"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
)
type Path struct {
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"`
XExtensions XExtensions `json:"-"`
}
// Paths are specified by OpenAPI/Swagger standard version 3.0.
type Paths map[string]Path
const (
responseOkKey = `200`
)
type addPathInput struct {
Path string // Precise route path.
Prefix string // Route path prefix.
Method string // Route method.
Function interface{} // Uniformed function.
}
func (oai *OpenApiV3) addPath(in addPathInput) error {
if oai.Paths == nil {
oai.Paths = map[string]Path{}
}
var (
reflectType = reflect.TypeOf(in.Function)
)
if reflectType.NumIn() != 2 || reflectType.NumOut() != 2 {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`unsupported function "%s" for OpenAPI Path register, there should be input & output structures`,
reflectType.String(),
)
}
var (
inputObject reflect.Value
outputObject reflect.Value
)
// Create instance according input/output types.
if reflectType.In(1).Kind() == reflect.Ptr {
inputObject = reflect.New(reflectType.In(1).Elem()).Elem()
} else {
inputObject = reflect.New(reflectType.In(1)).Elem()
}
if reflectType.Out(0).Kind() == reflect.Ptr {
outputObject = reflect.New(reflectType.Out(0).Elem()).Elem()
} else {
outputObject = reflect.New(reflectType.Out(0)).Elem()
}
var (
mime string
path = Path{XExtensions: make(XExtensions)}
inputMetaMap = gmeta.Data(inputObject.Interface())
outputMetaMap = gmeta.Data(outputObject.Interface())
isInputStructEmpty = oai.doesStructHasNoFields(inputObject.Interface())
inputStructTypeName = oai.golangTypeToSchemaName(inputObject.Type())
outputStructTypeName = oai.golangTypeToSchemaName(outputObject.Type())
operation = Operation{
Responses: map[string]ResponseRef{},
XExtensions: make(XExtensions),
}
)
// Path check.
if in.Path == "" {
in.Path = gmeta.Get(inputObject.Interface(), TagNamePath).String()
if in.Prefix != "" {
in.Path = gstr.TrimRight(in.Prefix, "/") + "/" + gstr.TrimLeft(in.Path, "/")
}
}
if in.Path == "" {
return gerror.NewCodef(
gcode.CodeMissingParameter,
`missing necessary path parameter "%s" for input struct "%s", missing tag in attribute Meta?`,
TagNamePath, inputStructTypeName,
)
}
if v, ok := oai.Paths[in.Path]; ok {
path = v
}
// Method check.
if in.Method == "" {
in.Method = gmeta.Get(inputObject.Interface(), TagNameMethod).String()
}
if in.Method == "" {
return gerror.NewCodef(
gcode.CodeMissingParameter,
`missing necessary method parameter "%s" for input struct "%s", missing tag in attribute Meta?`,
TagNameMethod, inputStructTypeName,
)
}
if err := oai.addSchema(inputObject.Interface(), outputObject.Interface()); err != nil {
return err
}
if len(inputMetaMap) > 0 {
if err := oai.tagMapToPath(inputMetaMap, &path); err != nil {
return err
}
if err := oai.tagMapToOperation(inputMetaMap, &operation); err != nil {
return err
}
// Allowed request mime.
if mime = inputMetaMap[TagNameMime]; mime == "" {
mime = inputMetaMap[TagNameConsumes]
}
}
// =================================================================================================================
// Request.
// =================================================================================================================
if operation.RequestBody == nil {
operation.RequestBody = &RequestBodyRef{}
}
if operation.RequestBody.Value == nil {
var (
requestBody = RequestBody{
Required: true,
Content: map[string]MediaType{},
}
)
// Supported mime types of request.
var (
contentTypes = oai.Config.ReadContentTypes
tagMimeValue = gmeta.Get(inputObject.Interface(), TagNameMime).String()
)
if tagMimeValue != "" {
contentTypes = gstr.SplitAndTrim(tagMimeValue, ",")
}
for _, v := range contentTypes {
if isInputStructEmpty {
requestBody.Content[v] = MediaType{}
} else {
schemaRef, err := oai.getRequestSchemaRef(getRequestSchemaRefInput{
BusinessStructName: inputStructTypeName,
RequestObject: oai.Config.CommonRequest,
RequestDataField: oai.Config.CommonRequestDataField,
})
if err != nil {
return err
}
requestBody.Content[v] = MediaType{
Schema: schemaRef,
}
}
}
operation.RequestBody = &RequestBodyRef{
Value: &requestBody,
}
}
// It also sets request parameters.
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: inputObject.Interface(),
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
if operation.Parameters == nil {
operation.Parameters = []ParameterRef{}
}
parameterRef, err := oai.newParameterRefWithStructMethod(structField, in.Path, in.Method)
if err != nil {
return err
}
if parameterRef != nil {
operation.Parameters = append(operation.Parameters, *parameterRef)
}
}
// =================================================================================================================
// Response.
// =================================================================================================================
if _, ok := operation.Responses[responseOkKey]; !ok {
var (
response = Response{
Content: map[string]MediaType{},
XExtensions: make(XExtensions),
}
)
if len(outputMetaMap) > 0 {
if err := oai.tagMapToResponse(outputMetaMap, &response); err != nil {
return err
}
}
// Supported mime types of response.
var (
contentTypes = oai.Config.ReadContentTypes
tagMimeValue = gmeta.Get(outputObject.Interface(), TagNameMime).String()
refInput = getResponseSchemaRefInput{
BusinessStructName: outputStructTypeName,
CommonResponseObject: oai.Config.CommonResponse,
CommonResponseDataField: oai.Config.CommonResponseDataField,
}
)
if tagMimeValue != "" {
contentTypes = gstr.SplitAndTrim(tagMimeValue, ",")
}
for _, v := range contentTypes {
// If customized response mime type, it then ignores common response feature.
if tagMimeValue != "" {
refInput.CommonResponseObject = nil
refInput.CommonResponseDataField = ""
}
schemaRef, err := oai.getResponseSchemaRef(refInput)
if err != nil {
return err
}
response.Content[v] = MediaType{
Schema: schemaRef,
}
}
operation.Responses[responseOkKey] = ResponseRef{Value: &response}
}
// Assign to certain operation attribute.
switch gstr.ToUpper(in.Method) {
case HttpMethodGet:
// GET operations cannot have a requestBody.
operation.RequestBody = nil
path.Get = &operation
case HttpMethodPut:
path.Put = &operation
case HttpMethodPost:
path.Post = &operation
case HttpMethodDelete:
// DELETE operations cannot have a requestBody.
operation.RequestBody = nil
path.Delete = &operation
case HttpMethodConnect:
// Nothing to do for Connect.
case HttpMethodHead:
path.Head = &operation
case HttpMethodOptions:
path.Options = &operation
case HttpMethodPatch:
path.Patch = &operation
case HttpMethodTrace:
path.Trace = &operation
default:
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid method "%s"`, in.Method)
}
oai.Paths[in.Path] = path
return nil
}
func (oai *OpenApiV3) doesStructHasNoFields(s interface{}) bool {
return reflect.TypeOf(s).NumField() == 0
}
func (oai *OpenApiV3) tagMapToPath(tagMap map[string]string, path *Path) error {
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, path); err != nil {
return gerror.Wrap(err, `mapping struct tags to Path failed`)
}
oai.tagMapToXExtensions(mergedTagMap, path.XExtensions)
return nil
}
func (p Path) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempPath Path // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempPath(p)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range p.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}

View File

@ -0,0 +1,103 @@
// 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 (
"reflect"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
// RequestBody is specified by OpenAPI/Swagger 3.0 standard.
type RequestBody struct {
Description string `json:"description,omitempty"`
Required bool `json:"required,omitempty"`
Content Content `json:"content,omitempty"`
}
type RequestBodyRef struct {
Ref string
Value *RequestBody
}
func (r RequestBodyRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}
type getRequestSchemaRefInput struct {
BusinessStructName string
RequestObject interface{}
RequestDataField string
}
func (oai *OpenApiV3) getRequestSchemaRef(in getRequestSchemaRefInput) (*SchemaRef, error) {
if oai.Config.CommonRequest == nil {
return &SchemaRef{
Ref: in.BusinessStructName,
}, nil
}
var (
dataFieldsPartsArray = gstr.Split(in.RequestDataField, ".")
bizRequestStructSchemaRef = oai.Components.Schemas.Get(in.BusinessStructName)
schema, err = oai.structToSchema(in.RequestObject)
)
if err != nil {
return nil, err
}
if in.RequestDataField == "" && bizRequestStructSchemaRef != nil {
// Normal request.
bizRequestStructSchemaRef.Value.Properties.Iterator(func(key string, ref SchemaRef) bool {
schema.Properties.Set(key, ref)
return true
})
} else {
// Common request.
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: in.RequestObject,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
var fieldName = structField.Name()
if jsonName := structField.TagJsonName(); jsonName != "" {
fieldName = jsonName
}
switch len(dataFieldsPartsArray) {
case 1:
if structField.Name() == dataFieldsPartsArray[0] {
if err = oai.tagMapToSchema(structField.TagMap(), bizRequestStructSchemaRef.Value); err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *bizRequestStructSchemaRef)
break
}
default:
if structField.Name() == dataFieldsPartsArray[0] {
var structFieldInstance = reflect.New(structField.Type().Type).Elem()
schemaRef, err := oai.getRequestSchemaRef(getRequestSchemaRefInput{
BusinessStructName: in.BusinessStructName,
RequestObject: structFieldInstance,
RequestDataField: gstr.Join(dataFieldsPartsArray[1:], "."),
})
if err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *schemaRef)
break
}
}
}
}
return &SchemaRef{
Value: schema,
}, nil
}

53
net/goai/goai_response.go Normal file
View File

@ -0,0 +1,53 @@
// 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/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
// Response is specified by OpenAPI/Swagger 3.0 standard.
type Response struct {
Description string `json:"description"`
Headers Headers `json:"headers,omitempty"`
Content Content `json:"content,omitempty"`
Links Links `json:"links,omitempty"`
XExtensions XExtensions `json:"-"`
}
func (oai *OpenApiV3) tagMapToResponse(tagMap map[string]string, response *Response) error {
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, response); err != nil {
return gerror.Wrap(err, `mapping struct tags to Response failed`)
}
oai.tagMapToXExtensions(mergedTagMap, response.XExtensions)
return nil
}
func (r Response) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempResponse Response // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempResponse(r)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range r.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}

View File

@ -0,0 +1,101 @@
// 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 (
"reflect"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
)
type ResponseRef struct {
Ref string
Value *Response
}
// Responses is specified by OpenAPI/Swagger 3.0 standard.
type Responses map[string]ResponseRef
func (r ResponseRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}
type getResponseSchemaRefInput struct {
BusinessStructName string // The business struct name.
CommonResponseObject interface{} // Common response object.
CommonResponseDataField string // Common response data field.
}
func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*SchemaRef, error) {
if in.CommonResponseObject == nil {
return &SchemaRef{
Ref: in.BusinessStructName,
}, nil
}
var (
dataFieldsPartsArray = gstr.Split(in.CommonResponseDataField, ".")
bizResponseStructSchemaRef = oai.Components.Schemas.Get(in.BusinessStructName)
schema, err = oai.structToSchema(in.CommonResponseObject)
)
if err != nil {
return nil, err
}
if in.CommonResponseDataField == "" && bizResponseStructSchemaRef != nil {
// Normal response.
bizResponseStructSchemaRef.Value.Properties.Iterator(func(key string, ref SchemaRef) bool {
schema.Properties.Set(key, ref)
return true
})
} else {
// Common response.
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: in.CommonResponseObject,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
for _, structField := range structFields {
var fieldName = structField.Name()
if jsonName := structField.TagJsonName(); jsonName != "" {
fieldName = jsonName
}
switch len(dataFieldsPartsArray) {
case 1:
if structField.Name() == dataFieldsPartsArray[0] {
if err = oai.tagMapToSchema(structField.TagMap(), bizResponseStructSchemaRef.Value); err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *bizResponseStructSchemaRef)
break
}
default:
// Recursively creating common response object schema.
if structField.Name() == dataFieldsPartsArray[0] {
var structFieldInstance = reflect.New(structField.Type().Type).Elem()
schemaRef, err := oai.getResponseSchemaRef(getResponseSchemaRefInput{
BusinessStructName: in.BusinessStructName,
CommonResponseObject: structFieldInstance,
CommonResponseDataField: gstr.Join(dataFieldsPartsArray[1:], "."),
})
if err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *schemaRef)
break
}
}
}
}
return &SchemaRef{
Value: schema,
}, nil
}

54
net/goai/goai_security.go Normal file
View File

@ -0,0 +1,54 @@
// 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/internal/json"
)
type SecurityScheme struct {
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
type SecuritySchemeRef struct {
Ref string
Value *SecurityScheme
}
type SecurityRequirements []SecurityRequirement
type SecurityRequirement map[string][]string
type OAuthFlows struct {
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"`
TokenURL string `json:"tokenUrl,omitempty"`
RefreshURL string `json:"refreshUrl,omitempty"`
Scopes map[string]string `json:"scopes"`
}
func (r SecuritySchemeRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

24
net/goai/goai_server.go Normal file
View File

@ -0,0 +1,24 @@
// 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
// Server is specified by OpenAPI/Swagger standard version 3.0.
type Server struct {
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"`
Default string `json:"default,omitempty"`
Description string `json:"description,omitempty"`
}
// Servers is specified by OpenAPI/Swagger standard version 3.0.
type Servers []Server

247
net/goai/goai_shema.go Normal file
View File

@ -0,0 +1,247 @@
// 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 (
"reflect"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/container/gset"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gstructs"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gmeta"
"github.com/gogf/gf/v2/util/gvalid"
)
// Schema is specified by OpenAPI/Swagger 3.0 standard.
type Schema struct {
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"`
XExtensions XExtensions `json:"-"`
ValidationRules string `json:"-"`
}
func (s Schema) MarshalJSON() ([]byte, error) {
var (
b []byte
m map[string]json.RawMessage
err error
)
type tempSchema Schema // To prevent JSON marshal recursion error.
if b, err = json.Marshal(tempSchema(s)); err != nil {
return nil, err
}
if err = json.Unmarshal(b, &m); err != nil {
return nil, err
}
for k, v := range s.XExtensions {
if b, err = json.Marshal(v); err != nil {
return nil, err
}
m[k] = b
}
return json.Marshal(m)
}
// Discriminator is specified by OpenAPI/Swagger standard version 3.0.
type Discriminator struct {
PropertyName string `json:"propertyName"`
Mapping map[string]string `json:"mapping,omitempty"`
}
// addSchema creates schemas with objects.
// Note that the `object` can be array alias like: `type Res []Item`.
func (oai *OpenApiV3) addSchema(object ...interface{}) error {
for _, v := range object {
if err := oai.doAddSchemaSingle(v); err != nil {
return err
}
}
return nil
}
func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
if oai.Components.Schemas.refs == nil {
oai.Components.Schemas.refs = gmap.NewListMap()
}
var (
reflectType = reflect.TypeOf(object)
structTypeName = oai.golangTypeToSchemaName(reflectType)
)
// Already added.
if oai.Components.Schemas.Get(structTypeName) != nil {
return nil
}
// Take the holder first.
oai.Components.Schemas.Set(structTypeName, SchemaRef{})
schema, err := oai.structToSchema(object)
if err != nil {
return err
}
oai.Components.Schemas.Set(structTypeName, SchemaRef{
Ref: "",
Value: schema,
})
return nil
}
// structToSchema converts and returns given struct object as Schema.
func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
var (
tagMap = gmeta.Data(object)
schema = &Schema{
Properties: createSchemas(),
XExtensions: make(XExtensions),
}
ignoreProperties []interface{}
)
if len(tagMap) > 0 {
if err := oai.tagMapToSchema(tagMap, schema); err != nil {
return nil, err
}
}
if schema.Type != "" && schema.Type != TypeObject {
return schema, nil
}
// []struct.
if utils.IsArray(object) {
schema.Type = TypeArray
subSchemaRef, err := oai.newSchemaRefWithGolangType(reflect.TypeOf(object).Elem(), nil)
if err != nil {
return nil, err
}
schema.Items = subSchemaRef
if len(schema.Enum) > 0 {
schema.Items.Value.Enum = schema.Enum
schema.Enum = nil
}
return schema, nil
}
// struct.
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
Pointer: object,
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
})
schema.Type = TypeObject
for _, structField := range structFields {
if !gstr.IsLetterUpper(structField.Name()[0]) {
continue
}
var fieldName = structField.Name()
if jsonName := structField.TagJsonName(); jsonName != "" {
fieldName = jsonName
}
schemaRef, err := oai.newSchemaRefWithGolangType(
structField.Type().Type,
structField.TagMap(),
)
if err != nil {
return nil, err
}
schema.Properties.Set(fieldName, *schemaRef)
}
schema.Properties.Iterator(func(key string, ref SchemaRef) bool {
if ref.Value != nil && ref.Value.ValidationRules != "" {
validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.ValidationRules, "|"))
if validationRuleSet.Contains(validationRuleKeyForRequired) {
schema.Required = append(schema.Required, key)
}
}
if !isValidParameterName(key) {
ignoreProperties = append(ignoreProperties, key)
}
return true
})
if len(ignoreProperties) > 0 {
schema.Properties.Removes(ignoreProperties)
}
return schema, nil
}
func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) error {
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
if err := gconv.Struct(mergedTagMap, schema); err != nil {
return gerror.Wrap(err, `mapping struct tags to Schema failed`)
}
oai.tagMapToXExtensions(mergedTagMap, schema.XExtensions)
// Validation info to OpenAPI schema pattern.
for _, tag := range gvalid.GetTags() {
if validationTagValue, ok := tagMap[tag]; ok {
_, validationRules, _ := gvalid.ParseTagValue(validationTagValue)
schema.ValidationRules = validationRules
// Enum checks.
if len(schema.Enum) == 0 {
for _, rule := range gstr.SplitAndTrim(validationRules, "|") {
if gstr.HasPrefix(rule, validationRuleKeyForIn) {
var (
isAllEnumNumber = true
enumArray = gstr.SplitAndTrim(rule[len(validationRuleKeyForIn):], ",")
)
for _, enum := range enumArray {
if !gstr.IsNumeric(enum) {
isAllEnumNumber = false
break
}
}
if isAllEnumNumber {
schema.Enum = gconv.Interfaces(gconv.Int64s(enumArray))
} else {
schema.Enum = gconv.Interfaces(enumArray)
}
}
}
}
break
}
}
return nil
}

107
net/goai/goai_shema_ref.go Normal file
View File

@ -0,0 +1,107 @@
// 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 (
"reflect"
"github.com/gogf/gf/v2/internal/json"
)
type SchemaRefs []SchemaRef
type SchemaRef struct {
Ref string
Value *Schema
}
func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap map[string]string) (*SchemaRef, error) {
var (
oaiType = oai.golangTypeToOAIType(golangType)
oaiFormat = oai.golangTypeToOAIFormat(golangType)
schemaRef = &SchemaRef{}
schema = &Schema{
Type: oaiType,
Format: oaiFormat,
XExtensions: make(XExtensions),
}
)
if len(tagMap) > 0 {
if err := oai.tagMapToSchema(tagMap, schema); err != nil {
return nil, err
}
}
schemaRef.Value = schema
switch oaiType {
case
TypeInteger,
TypeNumber,
TypeString,
TypeBoolean:
// Nothing to do.
case
TypeArray:
subSchemaRef, err := oai.newSchemaRefWithGolangType(golangType.Elem(), nil)
if err != nil {
return nil, err
}
schema.Items = subSchemaRef
if len(schema.Enum) > 0 {
schema.Items.Value.Enum = schema.Enum
schema.Enum = nil
}
case
TypeObject:
for golangType.Kind() == reflect.Ptr {
golangType = golangType.Elem()
}
switch golangType.Kind() {
case reflect.Map:
// Specially for map type.
subSchemaRef, err := oai.newSchemaRefWithGolangType(golangType.Elem(), nil)
if err != nil {
return nil, err
}
schema.AdditionalProperties = subSchemaRef
return schemaRef, nil
case reflect.Interface:
// Specially for interface type.
var (
structTypeName = oai.golangTypeToSchemaName(golangType)
)
if oai.Components.Schemas.Get(structTypeName) == nil {
if err := oai.addSchema(reflect.New(golangType).Interface()); err != nil {
return nil, err
}
}
schemaRef.Ref = structTypeName
schemaRef.Value = nil
default:
// Normal struct object.
var structTypeName = oai.golangTypeToSchemaName(golangType)
if oai.Components.Schemas.Get(structTypeName) == nil {
if err := oai.addSchema(reflect.New(golangType).Elem().Interface()); err != nil {
return nil, err
}
}
schemaRef.Ref = structTypeName
schemaRef.Value = nil
}
}
return schemaRef, nil
}
func (r SchemaRef) MarshalJSON() ([]byte, error) {
if r.Ref != "" {
return formatRefToBytes(r.Ref), nil
}
return json.Marshal(r.Value)
}

69
net/goai/goai_shemas.go Normal file
View File

@ -0,0 +1,69 @@
// 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"
)
type Schemas struct {
refs *gmap.ListMap // map[string]SchemaRef
}
func createSchemas() Schemas {
return Schemas{
refs: gmap.NewListMap(),
}
}
func (s *Schemas) init() {
if s.refs == nil {
s.refs = gmap.NewListMap()
}
}
func (s *Schemas) Get(name string) *SchemaRef {
s.init()
value := s.refs.Get(name)
if value != nil {
ref := value.(SchemaRef)
return &ref
}
return nil
}
func (s *Schemas) Set(name string, ref SchemaRef) {
s.init()
s.refs.Set(name, ref)
}
func (s *Schemas) Removes(names []interface{}) {
s.init()
s.refs.Removes(names)
}
func (s *Schemas) Map() map[string]SchemaRef {
s.init()
m := make(map[string]SchemaRef)
s.refs.Iterator(func(key, value interface{}) bool {
m[key.(string)] = value.(SchemaRef)
return true
})
return m
}
func (s *Schemas) Iterator(f func(key string, ref SchemaRef) bool) {
s.init()
s.refs.Iterator(func(key, value interface{}) bool {
return f(key.(string), value.(SchemaRef))
})
}
func (s Schemas) MarshalJSON() ([]byte, error) {
s.init()
return s.refs.MarshalJSON()
}

17
net/goai/goai_tags.go Normal file
View File

@ -0,0 +1,17 @@
// 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
// Tags is specified by OpenAPI/Swagger 3.0 standard.
type Tags []Tag
// Tag is specified by OpenAPI/Swagger 3.0 standard.
type Tag struct {
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
}

View File

@ -0,0 +1,22 @@
// 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/text/gstr"
)
// XExtensions stores the `x-` custom extensions.
type XExtensions map[string]string
func (oai *OpenApiV3) tagMapToXExtensions(tagMap map[string]string, extensions XExtensions) {
for k, v := range tagMap {
if gstr.HasPrefix(k, "x-") || gstr.HasPrefix(k, "X-") {
extensions[k] = v
}
}
}

View File

@ -0,0 +1,903 @@
// 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"
"fmt"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gmeta"
)
func Test_Basic(t *testing.T) {
type CommonReq struct {
AppId int64 `json:"appId" v:"required" in:"cookie" description:"应用Id"`
ResourceId string `json:"resourceId" in:"query" description:"资源Id"`
}
type SetSpecInfo struct {
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" description:"StorageType"`
Shards int32 `description:"shards 分片数"`
Params []string `description:"默认参数(json 串-ClickHouseParams)"`
}
type CreateResourceReq struct {
CommonReq
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default"`
Name string `description:"实例名称"`
Product string `description:"业务类型"`
Region string `v:"required" description:"区域"`
SetMap map[string]*SetSpecInfo `v:"required" description:"配置Map"`
SetSlice []SetSpecInfo `v:"required" description:"配置Slice"`
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
req = new(CreateResourceReq)
)
err = oai.Add(goai.AddInput{
Object: req,
})
t.AssertNil(err)
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 2)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Type, goai.TypeObject)
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Map()), 7)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`appId`).Value.Type, goai.TypeInteger)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`resourceId`).Value.Type, goai.TypeString)
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Map()), 3)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Get(`Params`).Value.Type, goai.TypeArray)
})
}
func TestOpenApiV3_Add(t *testing.T) {
type CommonReq struct {
AppId int64 `json:"appId" v:"required" in:"path" description:"应用Id"`
ResourceId string `json:"resourceId" in:"query" description:"资源Id"`
}
type SetSpecInfo struct {
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" description:"StorageType"`
Shards int32 `description:"shards 分片数"`
Params []string `description:"默认参数(json 串-ClickHouseParams)"`
}
type CreateResourceReq struct {
CommonReq
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default"`
Name string `description:"实例名称"`
Product string `description:"业务类型"`
Region string `v:"required" description:"区域"`
SetMap map[string]*SetSpecInfo `v:"required" description:"配置Map"`
SetSlice []SetSpecInfo `v:"required" description:"配置Slice"`
}
type CreateResourceRes struct {
gmeta.Meta `description:"Demo Response Struct"`
FlowId int64 `description:"创建实例流程id"`
}
f := func(ctx context.Context, req *CreateResourceReq) (res *CreateResourceRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPut,
Object: f,
})
t.AssertNil(err)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPost,
Object: f,
})
t.AssertNil(err)
// fmt.Println(oai.String())
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Type, goai.TypeObject)
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Map()), 7)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`appId`).Value.Type, goai.TypeInteger)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`resourceId`).Value.Type, goai.TypeString)
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Map()), 3)
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), 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)
})
}
func TestOpenApiV3_Add_Recursive(t *testing.T) {
type CategoryTreeItem struct {
Id uint `json:"id"`
ParentId uint `json:"parent_id"`
Items []*CategoryTreeItem `json:"items,omitempty"`
}
type CategoryGetTreeReq struct {
gmeta.Meta `path:"/category-get-tree" method:"GET" tags:"default"`
ContentType string `in:"query"`
}
type CategoryGetTreeRes struct {
List []*CategoryTreeItem
}
f := func(ctx context.Context, req *CategoryGetTreeReq) (res *CategoryGetTreeRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/tree",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`).Value.Type, goai.TypeObject)
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`).Value.Properties.Map()), 3)
})
}
func TestOpenApiV3_Add_EmptyReqAndRes(t *testing.T) {
type CaptchaIndexReq struct {
gmeta.Meta `method:"PUT" summary:"获取默认的验证码" description:"注意直接返回的是图片二进制内容" tags:"前台-验证码"`
}
type CaptchaIndexRes struct {
gmeta.Meta `mime:"png" description:"验证码二进制内容" `
}
f := func(ctx context.Context, req *CaptchaIndexReq) (res *CaptchaIndexRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/tree",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
fmt.Println(oai.String())
})
}
func TestOpenApiV3_Add_AutoDetectIn(t *testing.T) {
type Req struct {
gmeta.Meta `method:"get" tags:"default"`
Name string
Product string
Region string
}
type Res struct {
gmeta.Meta `description:"Demo Response Struct"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
path = `/test/{product}/{name}`
)
err = oai.Add(goai.AddInput{
Path: path,
Method: goai.HttpMethodGet,
Object: f,
})
t.AssertNil(err)
fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 2)
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)
})
}
func TestOpenApiV3_CommonRequest(t *testing.T) {
type CommonRequest struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"PUT"`
Product string `json:"product" v:"required" description:"Unique product key"`
Name string `json:"name" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonRequest = CommonRequest{}
oai.Config.CommonRequestDataField = `Data`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
t.Assert(len(oai.Components.Schemas.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)
})
}
func TestOpenApiV3_CommonRequest_WithoutDataField_Setting(t *testing.T) {
type CommonRequest struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"PUT"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonRequest = CommonRequest{}
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 5)
})
}
func TestOpenApiV3_CommonRequest_EmptyRequest(t *testing.T) {
type CommonRequest struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"Put"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct{}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonRequest = CommonRequest{}
oai.Config.CommonRequestDataField = `Data`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.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)
})
}
func TestOpenApiV3_CommonRequest_SubDataField(t *testing.T) {
type CommonReqError struct {
Code string `description:"错误码"`
Message string `description:"错误描述"`
}
type CommonReqRequest struct {
RequestId string `description:"RequestId"`
Error *CommonReqError `json:",omitempty" description:"执行错误信息"`
}
type CommonReq struct {
Request CommonReqRequest
}
type Req struct {
gmeta.Meta `method:"Put"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonRequest = CommonReq{}
oai.Config.CommonRequestDataField = `Request.`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.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)
})
}
func TestOpenApiV3_CommonResponse(t *testing.T) {
type CommonResponse struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"GET"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonResponse{}
oai.Config.CommonResponseDataField = `Data`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
//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), 1)
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
t.Assert(
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`,
)
})
}
func TestOpenApiV3_CommonResponse_WithoutDataField_Setting(t *testing.T) {
type CommonResponse struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"GET"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonResponse{}
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.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()), 8)
})
}
func TestOpenApiV3_CommonResponse_EmptyResponse(t *testing.T) {
type CommonResponse struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type Req struct {
gmeta.Meta `method:"PUT"`
Product string `json:"product" v:"required" description:"Unique product key"`
Name string `json:"name" v:"required" description:"Instance name"`
}
type Res struct{}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonResponse{}
oai.Config.CommonResponseDataField = `Data`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.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)
})
}
func TestOpenApiV3_CommonResponse_SubDataField(t *testing.T) {
type CommonResError struct {
Code string `description:"错误码"`
Message string `description:"错误描述"`
}
type CommonResResponse struct {
RequestId string `description:"RequestId"`
Error *CommonResError `json:",omitempty" description:"执行错误信息"`
}
type CommonRes struct {
Response CommonResResponse
}
type Req struct {
gmeta.Meta `method:"GET"`
Product string `json:"product" in:"query" v:"required" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type Res struct {
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonRes{}
oai.Config.CommonResponseDataField = `Response.`
err = oai.Add(goai.AddInput{
Path: "/index",
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 4)
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)
})
}
func TestOpenApiV3_ShortTags(t *testing.T) {
type CommonReq struct {
AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"`
ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"`
}
type SetSpecInfo struct {
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"`
Shards int32 `des:"shards 分片数" sum:"Shards Summary"`
Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"`
}
type CreateResourceReq struct {
CommonReq
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"`
Name string `des:"实例名称"`
Product string `des:"业务类型"`
Region string `v:"required" des:"区域"`
SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"`
SetSlice []SetSpecInfo `v:"required" des:"配置Slice"`
}
type CreateResourceRes struct {
gmeta.Meta `des:"Demo Response Struct"`
FlowId int64 `des:"创建实例流程id"`
}
f := func(ctx context.Context, req *CreateResourceReq) (res *CreateResourceRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPut,
Object: f,
})
t.AssertNil(err)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPost,
Object: f,
})
t.AssertNil(err)
// fmt.Println(oai.String())
// Schema asserts.
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(oai.Paths[`/test1/{appId}`].Summary, `CreateResourceReq sum`)
t.Assert(oai.
Components.
Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).
Value.Properties.Get(`resourceId`).Value.Description, `资源Id`)
})
}
func TestOpenApiV3_HtmlResponse(t *testing.T) {
type Req struct {
g.Meta `path:"/test" method:"get" summary:"展示内容详情页面" tags:"内容"`
Id uint `json:"id" v:"min:1#请选择查看的内容" dc:"内容id"`
}
type Res struct {
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/test",
Method: goai.HttpMethodGet,
Object: f,
})
t.AssertNil(err)
// fmt.Println(oai.String())
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.Res`).Value.Type, goai.TypeString)
})
}
func TestOpenApiV3_HtmlResponseWithCommonResponse(t *testing.T) {
type CommonResError struct {
Code string `description:"错误码"`
Message string `description:"错误描述"`
}
type CommonResResponse struct {
RequestId string `description:"RequestId"`
Error *CommonResError `json:",omitempty" description:"执行错误信息"`
}
type CommonRes struct {
Response CommonResResponse
}
type Req struct {
g.Meta `path:"/test" method:"get" summary:"展示内容详情页面" tags:"内容"`
Id uint `json:"id" v:"min:1#请选择查看的内容" dc:"内容id"`
}
type Res struct {
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
}
f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonRes{}
oai.Config.CommonResponseDataField = `Response.`
err = oai.Add(goai.AddInput{
Path: "/test",
Method: goai.HttpMethodGet,
Object: f,
})
t.AssertNil(err)
// fmt.Println(oai.String())
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.Res`).Value.Type, goai.TypeString)
})
}
func Test_Required_In_Schema(t *testing.T) {
type CommonReq struct {
AppId int64 `json:"appId" v:"required" in:"cookie" description:"应用Id"`
ResourceId string `json:"resourceId" in:"query" description:"资源Id"`
}
type SetSpecInfo struct {
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" description:"StorageType"`
Shards int32 `description:"shards 分片数"`
Params []string `description:"默认参数(json 串-ClickHouseParams)"`
}
type CreateResourceReq struct {
CommonReq
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default"`
Name string `description:"实例名称"`
Product string `description:"业务类型"`
Region string `v:"required|min:1" description:"区域"`
SetMap map[string]*SetSpecInfo `v:"required|min:1" description:"配置Map"`
SetSlice []SetSpecInfo `v:"required|min:1" description:"配置Slice"`
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
req = new(CreateResourceReq)
)
err = oai.Add(goai.AddInput{
Object: req,
})
t.AssertNil(err)
var (
schemaKey1 = `github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`
schemaKey2 = `github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`
)
t.Assert(oai.Components.Schemas.Map()[schemaKey1].Value.Required, g.Slice{
"appId",
"Region",
"SetMap",
"SetSlice",
})
t.Assert(oai.Components.Schemas.Map()[schemaKey2].Value.Required, g.Slice{
"StorageType",
})
t.Assert(oai.Components.Schemas.Map()[schemaKey2].Value.Properties.Map()["StorageType"].Value.Enum, g.Slice{
"CLOUD_PREMIUM",
"CLOUD_SSD",
"CLOUD_HSSD",
})
})
}
func Test_Properties_In_Sequence(t *testing.T) {
type ResourceCreateReq struct {
g.Meta `path:"/resource" tags:"OSS Resource" method:"put" x-sort:"1" summary:"创建实例(发货)"`
AppId uint64 `v:"required" dc:"应用Id"`
Uin string `v:"required" dc:"主用户账号,该资源隶属于的账号"`
CreateUin string `v:"required" dc:"创建实例的用户账号"`
Product string `v:"required" dc:"业务类型" eg:"tdach"`
Region string `v:"required" dc:"地域" eg:"ap-guangzhou"`
Zone string `v:"required" dc:"区域" eg:"ap-guangzhou-1"`
Tenant string `v:"required" dc:"业务自定义数据,透传到底层"`
VpcId string `dc:"业务Vpc Id, TCS场景下非必须"`
SubnetId string `dc:"业务Vpc子网Id"`
Name string `dc:"自定义实例名称默认和ResourceId一致"`
ClusterPreset string `dc:"业务自定义Cluster定义透传到底层"`
Engine string `dc:"引擎名称例如TxLightning"`
Version string `dc:"引擎版本例如10.3.213 (兼容ClickHouse 21.3.12)"`
SkipUpdateStatus bool `dc:"是否跳过状态更新继续保持creating" ed:"http://goframe.org"`
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
req = new(ResourceCreateReq)
)
err = oai.Add(goai.AddInput{
Object: req,
})
t.AssertNil(err)
fmt.Println(oai)
})
}
func TestOpenApiV3_Ignore_Parameter(t *testing.T) {
type CommonResponse struct {
Code int `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Data interface{} `json:"data" description:"Result data for certain request according API definition"`
}
type ProductSearchReq struct {
gmeta.Meta `path:"/test" method:"get"`
Product string `json:"-" in:"query" v:"required|max:5" description:"Unique product key"`
Name string `json:"name" in:"query" v:"required" description:"Instance name"`
}
type ProductSearchRes struct {
ID int64 `json:"-" description:"Product ID"`
Product string `json:"product" v:"required" description:"Unique product key"`
TemplateName string `json:"templateName" v:"required" description:"Workflow template name"`
Version string `json:"version" v:"required" description:"Workflow template version"`
TxID string `json:"txID" v:"required" description:"Transaction ID for creating instance"`
Globals string `json:"globals" description:"Globals"`
}
f := func(ctx context.Context, req *ProductSearchReq) (res *ProductSearchRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
oai.Config.CommonResponse = CommonResponse{}
err = oai.Add(goai.AddInput{
Object: f,
})
t.AssertNil(err)
// Schema asserts.
// fmt.Println(oai.String())
t.Assert(len(oai.Components.Schemas.Map()), 3)
t.Assert(len(oai.Paths), 1)
t.Assert(len(oai.Paths["/test"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 8)
})
}
func Test_EnumOfSchemaItems(t *testing.T) {
type CreateResourceReq struct {
gmeta.Meta `path:"/CreateResourceReq" method:"POST"`
Members []string `v:"required|in:a,b,c"`
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
req = new(CreateResourceReq)
)
err = oai.Add(goai.AddInput{
Object: req,
})
t.AssertNil(err)
t.Assert(
oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.
Properties.Get(`Members`).Value.
Items.Value.Enum,
g.Slice{"a", "b", "c"},
)
})
}

728
net/goai/testdata/example.yaml vendored Normal file
View File

@ -0,0 +1,728 @@
openapi: 3.0.1
info:
title: Swagger Petstore
description: 'This is a sample server Petstore server. You can find out more about Swagger
at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For
this sample, you can use the api key `special-key` to test the authorization filters.'
termsOfService: http://swagger.io/terms/
contact:
email: apiteam@swagger.io
license:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
version: 1.0.0
externalDocs:
description: Find out more about Swagger
url: http://swagger.io
servers:
- url: https://petstore.swagger.io/v2
- url: http://petstore.swagger.io/v2
tags:
- name: pet
description: Everything about your Pets
externalDocs:
description: Find out more
url: http://swagger.io
- name: store
description: Access to Petstore orders
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: http://swagger.io
paths:
/pet:
put:
tags:
- pet
summary: Update an existing pet
operationId: updatePet
requestBody:
description: Pet object that needs to be added to the store
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
400:
description: Invalid ID supplied
content: {}
404:
description: Pet not found
content: {}
405:
description: Validation exception
content: {}
security:
- petstore_auth:
- write:pets
- read:pets
x-codegen-request-body-name: body
post:
tags:
- pet
summary: Add a new pet to the store
operationId: addPet
requestBody:
description: Pet object that needs to be added to the store
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
application/xml:
schema:
$ref: '#/components/schemas/Pet'
required: true
responses:
405:
description: Invalid input
content: {}
security:
- petstore_auth:
- write:pets
- read:pets
x-codegen-request-body-name: body
/pet/findByStatus:
get:
tags:
- pet
summary: Finds Pets by status
description: Multiple status values can be provided with comma separated strings
operationId: findPetsByStatus
parameters:
- name: status
in: query
description: Status values that need to be considered for filter
required: true
style: form
explode: true
schema:
type: array
items:
type: string
default: available
enum:
- available
- pending
- sold
responses:
200:
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
400:
description: Invalid status value
content: {}
security:
- petstore_auth:
- write:pets
- read:pets
/pet/findByTags:
get:
tags:
- pet
summary: Finds Pets by tags
description: Multiple tags can be provided with comma separated strings. Use tag1,
tag2, tag3 for testing.
operationId: findPetsByTags
parameters:
- name: tags
in: query
description: Tags to filter by
required: true
style: form
explode: true
schema:
type: array
items:
type: string
responses:
200:
description: successful operation
content:
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Pet'
400:
description: Invalid tag value
content: {}
deprecated: true
security:
- petstore_auth:
- write:pets
- read:pets
/pet/{petId}:
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
responses:
200:
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Pet'
application/json:
schema:
$ref: '#/components/schemas/Pet'
400:
description: Invalid ID supplied
content: {}
404:
description: Pet not found
content: {}
security:
- api_key: []
post:
tags:
- pet
summary: Updates a pet in the store with form data
operationId: updatePetWithForm
parameters:
- name: petId
in: path
description: ID of pet that needs to be updated
required: true
schema:
type: integer
format: int64
requestBody:
content:
application/x-www-form-urlencoded:
schema:
properties:
name:
type: string
description: Updated name of the pet
status:
type: string
description: Updated status of the pet
responses:
405:
description: Invalid input
content: {}
security:
- petstore_auth:
- write:pets
- read:pets
delete:
tags:
- pet
summary: Deletes a pet
operationId: deletePet
parameters:
- name: api_key
in: header
schema:
type: string
- name: petId
in: path
description: Pet id to delete
required: true
schema:
type: integer
format: int64
responses:
400:
description: Invalid ID supplied
content: {}
404:
description: Pet not found
content: {}
security:
- petstore_auth:
- write:pets
- read:pets
/pet/{petId}/uploadImage:
post:
tags:
- pet
summary: uploads an image
operationId: uploadFile
parameters:
- name: petId
in: path
description: ID of pet to update
required: true
schema:
type: integer
format: int64
requestBody:
content:
multipart/form-data:
schema:
properties:
additionalMetadata:
type: string
description: Additional data to pass to server
file:
type: string
description: file to upload
format: binary
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
security:
- petstore_auth:
- write:pets
- read:pets
/store/inventory:
get:
tags:
- store
summary: Returns pet inventories by status
description: Returns a map of status codes to quantities
operationId: getInventory
responses:
200:
description: successful operation
content:
application/json:
schema:
type: object
additionalProperties:
type: integer
format: int32
security:
- api_key: []
/store/order:
post:
tags:
- store
summary: Place an order for a pet
operationId: placeOrder
requestBody:
description: order placed for purchasing the pet
content:
'*/*':
schema:
$ref: '#/components/schemas/Order'
required: true
responses:
200:
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
400:
description: Invalid Order
content: {}
x-codegen-request-body-name: body
/store/order/{orderId}:
get:
tags:
- store
summary: Find purchase order by ID
description: For valid response try integer IDs with value >= 1 and <= 10. Other
values will generated exceptions
operationId: getOrderById
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
schema:
maximum: 10.0
minimum: 1.0
type: integer
format: int64
responses:
200:
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/Order'
application/json:
schema:
$ref: '#/components/schemas/Order'
400:
description: Invalid ID supplied
content: {}
404:
description: Order not found
content: {}
delete:
tags:
- store
summary: Delete purchase order by ID
description: For valid response try integer IDs with positive integer value. Negative
or non-integer values will generate API errors
operationId: deleteOrder
parameters:
- name: orderId
in: path
description: ID of the order that needs to be deleted
required: true
schema:
minimum: 1.0
type: integer
format: int64
responses:
400:
description: Invalid ID supplied
content: {}
404:
description: Order not found
content: {}
/user:
post:
tags:
- user
summary: Create user
description: This can only be done by the logged in user.
operationId: createUser
requestBody:
description: Created user object
content:
'*/*':
schema:
$ref: '#/components/schemas/User'
required: true
responses:
default:
description: successful operation
content: {}
x-codegen-request-body-name: body
/user/createWithArray:
post:
tags:
- user
summary: Creates list of users with given input array
operationId: createUsersWithArrayInput
requestBody:
description: List of user object
content:
'*/*':
schema:
type: array
items:
$ref: '#/components/schemas/User'
required: true
responses:
default:
description: successful operation
content: {}
x-codegen-request-body-name: body
/user/createWithList:
post:
tags:
- user
summary: Creates list of users with given input array
operationId: createUsersWithListInput
requestBody:
description: List of user object
content:
'*/*':
schema:
type: array
items:
$ref: '#/components/schemas/User'
required: true
responses:
default:
description: successful operation
content: {}
x-codegen-request-body-name: body
/user/login:
get:
tags:
- user
summary: Logs user into the system
operationId: loginUser
parameters:
- name: username
in: query
description: The user name for login
required: true
schema:
type: string
- name: password
in: query
description: The password for login in clear text
required: true
schema:
type: string
responses:
200:
description: successful operation
headers:
X-Rate-Limit:
description: calls per hour allowed by the user
schema:
type: integer
format: int32
X-Expires-After:
description: date in UTC when token expires
schema:
type: string
format: date-time
content:
application/xml:
schema:
type: string
application/json:
schema:
type: string
400:
description: Invalid username/password supplied
content: {}
/user/logout:
get:
tags:
- user
summary: Logs out current logged in user session
operationId: logoutUser
responses:
default:
description: successful operation
content: {}
/user/{username}:
get:
tags:
- user
summary: Get user by user name
operationId: getUserByName
parameters:
- name: username
in: path
description: 'The name that needs to be fetched. Use user1 for testing. '
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/xml:
schema:
$ref: '#/components/schemas/User'
application/json:
schema:
$ref: '#/components/schemas/User'
400:
description: Invalid username supplied
content: {}
404:
description: User not found
content: {}
put:
tags:
- user
summary: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
parameters:
- name: username
in: path
description: name that need to be updated
required: true
schema:
type: string
requestBody:
description: Updated user object
content:
'*/*':
schema:
$ref: '#/components/schemas/User'
required: true
responses:
400:
description: Invalid user supplied
content: {}
404:
description: User not found
content: {}
x-codegen-request-body-name: body
delete:
tags:
- user
summary: Delete user
description: This can only be done by the logged in user.
operationId: deleteUser
parameters:
- name: username
in: path
description: The name that needs to be deleted
required: true
schema:
type: string
responses:
400:
description: Invalid username supplied
content: {}
404:
description: User not found
content: {}
components:
schemas:
Order:
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
description: User Status
format: int32
xml:
name: User
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
required:
- name
- photoUrls
type: object
properties:
id:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
wrapped: true
items:
$ref: '#/components/schemas/Tag'
status:
type: string
description: pet status in the store
enum:
- available
- pending
- sold
xml:
name: Pet
ApiResponse:
type: object
properties:
code:
type: integer
format: int32
type:
type: string
message:
type: string
securitySchemes:
petstore_auth:
type: oauth2
flows:
implicit:
authorizationUrl: http://petstore.swagger.io/oauth/dialog
scopes:
write:pets: modify pets in your account
read:pets: read your pets
api_key:
type: apiKey
name: api_key
in: header