2021-10-02 18:54:06 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
|
2021-10-02 14:52:28 +08:00
|
|
|
package goai
|
|
|
|
|
|
|
|
import (
|
2023-04-20 16:30:42 +08:00
|
|
|
"fmt"
|
2021-11-13 23:34:16 +08:00
|
|
|
"reflect"
|
|
|
|
|
2021-10-11 21:41:56 +08:00
|
|
|
"github.com/gogf/gf/v2/internal/json"
|
2022-08-24 21:20:17 +08:00
|
|
|
"github.com/gogf/gf/v2/text/gstr"
|
2023-02-08 14:16:12 +08:00
|
|
|
"github.com/gogf/gf/v2/util/gconv"
|
2023-04-20 16:30:42 +08:00
|
|
|
"github.com/gogf/gf/v2/util/gtag"
|
2021-10-02 14:52:28 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type SchemaRefs []SchemaRef
|
|
|
|
|
|
|
|
type SchemaRef struct {
|
|
|
|
Ref string
|
|
|
|
Value *Schema
|
|
|
|
}
|
|
|
|
|
2023-04-26 19:34:22 +08:00
|
|
|
// isEmbeddedStructDefinition checks and returns whether given golang type is embedded struct definition, like:
|
2022-11-01 20:12:21 +08:00
|
|
|
//
|
|
|
|
// struct A struct{
|
|
|
|
// B struct{
|
|
|
|
// // ...
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
2022-08-24 21:20:17 +08:00
|
|
|
// The `B` in `A` is called `embedded struct definition`.
|
|
|
|
func (oai *OpenApiV3) isEmbeddedStructDefinition(golangType reflect.Type) bool {
|
|
|
|
s := golangType.String()
|
2022-11-01 20:12:21 +08:00
|
|
|
return gstr.Contains(s, `struct {`)
|
2022-08-24 21:20:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// newSchemaRefWithGolangType creates a new Schema and returns its SchemaRef.
|
2021-10-02 18:54:06 +08:00
|
|
|
func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap map[string]string) (*SchemaRef, error) {
|
2021-10-02 14:52:28 +08:00
|
|
|
var (
|
2022-08-24 21:20:17 +08:00
|
|
|
err error
|
2021-10-02 14:52:28 +08:00
|
|
|
oaiType = oai.golangTypeToOAIType(golangType)
|
2021-10-02 15:07:47 +08:00
|
|
|
oaiFormat = oai.golangTypeToOAIFormat(golangType)
|
2023-04-20 16:30:42 +08:00
|
|
|
typeName = golangType.Name()
|
|
|
|
pkgPath = golangType.PkgPath()
|
2021-10-02 18:54:06 +08:00
|
|
|
schemaRef = &SchemaRef{}
|
2021-10-02 14:52:28 +08:00
|
|
|
schema = &Schema{
|
2022-03-29 20:31:00 +08:00
|
|
|
Type: oaiType,
|
|
|
|
Format: oaiFormat,
|
|
|
|
XExtensions: make(XExtensions),
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
)
|
2023-04-20 16:30:42 +08:00
|
|
|
if pkgPath == "" {
|
|
|
|
switch golangType.Kind() {
|
|
|
|
case reflect.Ptr, reflect.Array, reflect.Slice:
|
|
|
|
pkgPath = golangType.Elem().PkgPath()
|
|
|
|
typeName = golangType.Elem().Name()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type enums.
|
|
|
|
var typeId = fmt.Sprintf(`%s.%s`, pkgPath, typeName)
|
|
|
|
if enums := gtag.GetEnumsByType(typeId); enums != "" {
|
|
|
|
schema.Enum = make([]interface{}, 0)
|
|
|
|
if err = json.Unmarshal([]byte(enums), &schema.Enum); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-02 14:52:28 +08:00
|
|
|
if len(tagMap) > 0 {
|
2022-03-02 20:00:40 +08:00
|
|
|
if err := oai.tagMapToSchema(tagMap, schema); err != nil {
|
|
|
|
return nil, err
|
2022-03-01 11:43:42 +08:00
|
|
|
}
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
schemaRef.Value = schema
|
|
|
|
switch oaiType {
|
2023-08-28 21:52:22 +08:00
|
|
|
case TypeString, TypeFile:
|
2023-02-08 14:16:12 +08:00
|
|
|
// Nothing to do.
|
|
|
|
case TypeInteger:
|
|
|
|
if schemaRef.Value.Default != nil {
|
|
|
|
schemaRef.Value.Default = gconv.Int64(schemaRef.Value.Default)
|
|
|
|
}
|
|
|
|
// keep the default value as nil.
|
2023-10-31 20:18:39 +08:00
|
|
|
|
|
|
|
// example value needs to be converted just like default value
|
|
|
|
if schemaRef.Value.Example != nil {
|
|
|
|
schemaRef.Value.Example = gconv.Int64(schemaRef.Value.Example)
|
|
|
|
}
|
|
|
|
// keep the example value as nil.
|
2023-02-08 14:16:12 +08:00
|
|
|
case TypeNumber:
|
|
|
|
if schemaRef.Value.Default != nil {
|
|
|
|
schemaRef.Value.Default = gconv.Float64(schemaRef.Value.Default)
|
|
|
|
}
|
|
|
|
// keep the default value as nil.
|
2023-10-31 20:18:39 +08:00
|
|
|
|
|
|
|
// example value needs to be converted just like default value
|
|
|
|
if schemaRef.Value.Example != nil {
|
|
|
|
schemaRef.Value.Example = gconv.Float64(schemaRef.Value.Example)
|
|
|
|
}
|
|
|
|
// keep the example value as nil.
|
2023-02-08 14:16:12 +08:00
|
|
|
case TypeBoolean:
|
|
|
|
if schemaRef.Value.Default != nil {
|
|
|
|
schemaRef.Value.Default = gconv.Bool(schemaRef.Value.Default)
|
|
|
|
}
|
|
|
|
// keep the default value as nil.
|
2023-10-31 20:18:39 +08:00
|
|
|
|
|
|
|
// example value needs to be converted just like default value
|
|
|
|
if schemaRef.Value.Example != nil {
|
|
|
|
schemaRef.Value.Example = gconv.Bool(schemaRef.Value.Example)
|
|
|
|
}
|
|
|
|
// keep the example value as nil.
|
2021-10-02 14:52:28 +08:00
|
|
|
case
|
|
|
|
TypeArray:
|
2021-10-02 18:54:06 +08:00
|
|
|
subSchemaRef, err := oai.newSchemaRefWithGolangType(golangType.Elem(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
schema.Items = subSchemaRef
|
2022-05-17 11:15:29 +08:00
|
|
|
if len(schema.Enum) > 0 {
|
|
|
|
schema.Items.Value.Enum = schema.Enum
|
|
|
|
schema.Enum = nil
|
|
|
|
}
|
2021-10-02 14:52:28 +08:00
|
|
|
|
|
|
|
case
|
|
|
|
TypeObject:
|
2021-10-14 15:03:39 +08:00
|
|
|
for golangType.Kind() == reflect.Ptr {
|
|
|
|
golangType = golangType.Elem()
|
|
|
|
}
|
|
|
|
switch golangType.Kind() {
|
|
|
|
case reflect.Map:
|
|
|
|
// Specially for map type.
|
2021-10-02 18:54:06 +08:00
|
|
|
subSchemaRef, err := oai.newSchemaRefWithGolangType(golangType.Elem(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
schema.AdditionalProperties = subSchemaRef
|
|
|
|
return schemaRef, nil
|
2021-10-14 15:03:39 +08:00
|
|
|
|
|
|
|
case reflect.Interface:
|
|
|
|
// Specially for interface type.
|
|
|
|
var (
|
|
|
|
structTypeName = oai.golangTypeToSchemaName(golangType)
|
|
|
|
)
|
2022-03-07 19:46:05 +08:00
|
|
|
if oai.Components.Schemas.Get(structTypeName) == nil {
|
2021-10-14 15:03:39 +08:00
|
|
|
if err := oai.addSchema(reflect.New(golangType).Interface()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
schemaRef.Ref = structTypeName
|
|
|
|
schemaRef.Value = nil
|
|
|
|
|
|
|
|
default:
|
2022-08-24 21:20:17 +08:00
|
|
|
golangTypeInstance := reflect.New(golangType).Elem().Interface()
|
|
|
|
if oai.isEmbeddedStructDefinition(golangType) {
|
|
|
|
schema, err = oai.structToSchema(golangTypeInstance)
|
|
|
|
if err != nil {
|
2021-10-14 15:03:39 +08:00
|
|
|
return nil, err
|
|
|
|
}
|
2022-08-24 21:20:17 +08:00
|
|
|
schemaRef.Ref = ""
|
|
|
|
schemaRef.Value = schema
|
|
|
|
} else {
|
|
|
|
var structTypeName = oai.golangTypeToSchemaName(golangType)
|
|
|
|
if oai.Components.Schemas.Get(structTypeName) == nil {
|
|
|
|
if err := oai.addSchema(golangTypeInstance); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
schemaRef.Ref = structTypeName
|
|
|
|
schemaRef.Value = nil
|
2021-10-02 18:54:06 +08:00
|
|
|
}
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
}
|
2021-10-02 18:54:06 +08:00
|
|
|
return schemaRef, nil
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r SchemaRef) MarshalJSON() ([]byte, error) {
|
|
|
|
if r.Ref != "" {
|
2021-10-02 18:54:06 +08:00
|
|
|
return formatRefToBytes(r.Ref), nil
|
2021-10-02 14:52:28 +08:00
|
|
|
}
|
|
|
|
return json.Marshal(r.Value)
|
|
|
|
}
|