优化schema生成

This commit is contained in:
白茶清欢 2025-02-09 15:00:25 +08:00
parent 27f2ccf2aa
commit e4e8702fd4
3 changed files with 121 additions and 29 deletions

View File

@ -44,6 +44,7 @@ type PathConfig struct {
Head *PathItemOperationConfig `json:"head,omitempty"` // 定义适用于此路径的 HEAD 操作。
Patch *PathItemOperationConfig `json:"patch,omitempty"` // 定义适用于此路径的 PATCH 操作。
Trace *PathItemOperationConfig `json:"trace,omitempty"` // 定义适用于此路径的 TRACE 操作。
Connect *PathItemOperationConfig `json:"connect,omitempty"` // 定义适用于此路径的 CONNECT 操作。
}
// PathItemConfig 接口的具体配置
@ -130,6 +131,7 @@ type Schema struct {
Required []string `json:"required,omitempty"` // 必传属性列表
Enum []any `json:"enum,omitempty"` // 枚举值列表
Type string `json:"type"` // 类型
Ref string `json:"$ref"` // 类型引用
}
// Property 是从 JSON Schema 提取出来的,但是做了一些调整以适应 OpenAPI Specification。

View File

@ -8,9 +8,12 @@
package api_doc
import (
"errors"
"fmt"
"git.zhangdeman.cn/gateway/api-doc/define"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/wrapper"
"net/http"
"reflect"
"strings"
)
@ -164,23 +167,96 @@ func (g *Generate) AddApi(baseCfg *define.UriBaseConfig, paramList []*define.Par
return nil
}
// AddApiFromInAndOut 通过请求参数的
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:22 2025/2/9
func (g *Generate) AddApiFromInAndOut(baseCfg *define.UriBaseConfig, paramType reflect.Type, resultType reflect.Type) error {
if nil == baseCfg {
return errors.New("baseCfg is nil")
}
if baseCfg.Method == "" {
return errors.New("baseCfg.Method is empty")
}
if baseCfg.Uri == "" {
return errors.New("baseCfg.Uri is empty")
}
baseCfg.Method = strings.ToUpper(baseCfg.Method)
paramMethod := []string{
http.MethodGet, http.MethodHead, http.MethodConnect, http.MethodOptions, http.MethodTrace,
}
if wrapper.ArrayType(paramMethod).Has(baseCfg.Method) >= 0 {
// Get类请求, TODO : get类解析
return nil
}
// post类解析
defaultPkgPath := wrapper.String(strings.ReplaceAll(strings.TrimLeft(baseCfg.Uri, "/"), "/", "_")).SnakeCaseToCamel()
paramPkgPath := defaultPkgPath + "Param"
resultPkgPath := defaultPkgPath + "Result"
g.AddComponentsSchema(paramPkgPath, paramType)
g.AddComponentsSchema(resultPkgPath, resultType)
if _, exist := g.docData.Paths[baseCfg.Uri]; !exist {
g.docData.Paths[baseCfg.Uri] = &define.PathConfig{}
}
cfg := &define.PathItemOperationConfig{
Tags: baseCfg.TagList,
Summary: baseCfg.Summary,
Description: baseCfg.Description,
ExternalDocs: nil,
OperationID: defaultPkgPath + baseCfg.Method,
Parameters: nil,
RequestBody: &define.RequestBody{
Required: true,
Description: "",
Content: map[string]*define.Media{},
Ref: "",
},
Responses: nil,
Callbacks: nil,
Deprecated: false,
Security: nil,
Servers: nil,
}
for _, itemOutputType := range baseCfg.OutputContentType {
cfg.RequestBody.Content[itemOutputType] = &define.Media{
Schema: &define.Schema{
Ref: g.getSchemaRes(paramPkgPath + "." + paramType.Name()),
},
Example: "",
Examples: nil,
Encoding: nil,
}
}
switch baseCfg.Method {
case http.MethodGet:
g.docData.Paths[baseCfg.Uri].Get = cfg
case http.MethodHead:
g.docData.Paths[baseCfg.Uri].Head = cfg
case http.MethodPost:
g.docData.Paths[baseCfg.Uri].Post = cfg
case http.MethodPut:
g.docData.Paths[baseCfg.Uri].Put = cfg
case http.MethodPatch:
g.docData.Paths[baseCfg.Uri].Patch = cfg
case http.MethodDelete:
g.docData.Paths[baseCfg.Uri].Delete = cfg
case http.MethodConnect:
g.docData.Paths[baseCfg.Uri].Connect = cfg
case http.MethodOptions:
g.docData.Paths[baseCfg.Uri].Options = cfg
case http.MethodTrace:
g.docData.Paths[baseCfg.Uri].Trace = cfg
}
return nil
}
// AddComponentsSchema 添加schema
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:25 2025/2/8
func (g *Generate) AddComponentsSchema(pkgPath string, inputType reflect.Type) {
if nil == g.docData {
g.docData = &define.OpenapiDoc{}
}
if nil == g.docData.Components {
g.docData.Components = &define.Components{
Schemas: map[string]*define.Schema{},
}
}
if nil == g.docData.Components.Schemas {
g.docData.Components.Schemas = make(map[string]*define.Schema)
}
func (g *Generate) AddComponentsSchema(pkgPath string, inputType reflect.Type) string {
if len(pkgPath) == 0 {
pkgPath = inputType.PkgPath()
}
@ -191,19 +267,20 @@ func (g *Generate) AddComponentsSchema(pkgPath string, inputType reflect.Type) {
if inputType.Kind() == reflect.Ptr {
inputType = inputType.Elem()
}
schemaName := schemaPrefix + inputType.Name()
if inputType.Kind() == reflect.Map {
// map, 直接添加公共
if _, exist := g.docData.Components.Schemas[schemaPrefix+inputType.Name()]; !exist {
g.docData.Components.Schemas[schemaPrefix+inputType.Name()] = &define.Schema{
if _, exist := g.docData.Components.Schemas[schemaName]; !exist {
g.docData.Components.Schemas[schemaName] = &define.Schema{
Type: consts.SwaggerDataTypeObject,
}
}
return
return schemaName
}
// 数组
if inputType.Kind() == reflect.Slice || inputType.Kind() == reflect.Array {
g.parseSliceItem(inputType)
return
return schemaName
}
// 结构体
if inputType.Kind() == reflect.Struct {
@ -218,6 +295,7 @@ func (g *Generate) AddComponentsSchema(pkgPath string, inputType reflect.Type) {
fmt.Println(inputType.Field(i).Name)
}
}
return schemaName
}
// parseSliceItem 解析数组每一项
@ -238,3 +316,15 @@ func (g *Generate) parseSliceItem(inputType reflect.Type) string {
}
return sliceItemType.PkgPath() + "." + sliceItemType.String()
}
// getSchemaRes 获取引用的类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:25 2025/2/9
func (g *Generate) getSchemaRes(schemaName string) string {
if "" == schemaName {
return ""
}
return "#/components/schemas/" + schemaName
}

View File

@ -8,12 +8,10 @@
package api_doc
import (
"encoding/json"
"fmt"
"git.zhangdeman.cn/gateway/api-doc/define"
"git.zhangdeman.cn/zhangdeman/serialize"
"os"
"os/user"
"net/http"
"reflect"
"testing"
)
@ -31,17 +29,19 @@ func Test_parser_Openapi3(t *testing.T) {
List []A `json:"list"`
}
var bArr []*B
g := Generate{}
g := NewOpenapiDoc()
g.AddApiFromInAndOut(&define.UriBaseConfig{
Uri: "/a/b/c/d",
Method: http.MethodPost,
ContentType: []string{"application/x-www-form-urlencoded"},
OutputContentType: []string{"application/yml", "application/json", "application/xml"},
TagList: []string{"测试文档生成"},
Summary: "测试接口",
Description: "",
ParamList: nil,
ResultList: nil,
}, reflect.TypeOf(bArr), reflect.TypeOf(bArr))
fmt.Println(g.parseSliceItem(reflect.TypeOf(bArr)))
current, _ := user.Current()
byteData, _ := os.ReadFile(current.HomeDir + "/Downloads/test-openapi-doc.json")
var data define.OpenapiDoc
err := json.Unmarshal(byteData, &data)
if nil != err {
fmt.Println("解析失败 : " + err.Error())
} else {
fmt.Println("解析成功")
}
}
func TestParseForSwagger(t *testing.T) {