mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 20:28:17 +08:00
9402cc8c6a
* fix router supported for handler of package ghttp; fix json tag name issue when it contains for package goai * add proxy example for http server
252 lines
8.0 KiB
Go
252 lines
8.0 KiB
Go
// 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()
|
|
for _, tagName := range gconv.StructTagPriority {
|
|
if tagValue := structField.Tag(tagName); tagValue != "" {
|
|
fieldName = tagValue
|
|
break
|
|
}
|
|
}
|
|
fieldName = gstr.SplitAndTrim(fieldName, ",")[0]
|
|
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.fillMapWithShortTags(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
|
|
}
|