mirror of
https://github.com/go-home-admin/toolset.git
synced 2025-12-24 13:37:52 +08:00
1. 新增生成ts工具make:ts
2. 生成js工具增加http_from、info_tags选项 3. 修复生成js的部分字段类型缺失的问题 4. 修复生成swagger时的required在部分场景不正确问题
This commit is contained in:
29
README.md
29
README.md
@@ -119,7 +119,7 @@ service Controller {
|
||||
message TResponse {}
|
||||
````
|
||||
|
||||
# 生成 js+注释 文件
|
||||
# 生成 js 接口结构文件
|
||||
````shell
|
||||
user@macOs toolset % toolset make:js -h
|
||||
Usage:
|
||||
@@ -132,9 +132,34 @@ Option:
|
||||
-out js文件输出路径
|
||||
-tag 只生成指定tag的请求
|
||||
-debug 是否显示明细
|
||||
-root 获取项目跟路径, 默认当前目录
|
||||
-root 获取项目跟路径, 默认当前目录
|
||||
-http_from 指定import的http函数位置
|
||||
-info_tags 指定注释中的tag显示于接口说明
|
||||
-h 显示帮助信息
|
||||
Has:
|
||||
Description:
|
||||
根据swagger生成js请求文件
|
||||
````
|
||||
|
||||
# 生成 ts 接口结构文件
|
||||
比js更完整,每个参数带有注释,同时生成枚举对象
|
||||
````shell
|
||||
user@macOs toolset % toolset make:ts -h
|
||||
Usage:
|
||||
make:js
|
||||
-in = @root/web/swagger.json
|
||||
-out = @root/resources/src/api/swagger_gen.ts
|
||||
Arguments:
|
||||
Option:
|
||||
-in swagger.json路径, 可本地可远程
|
||||
-out ts文件输出路径
|
||||
-tag 只生成指定tag的请求
|
||||
-debug 是否显示明细
|
||||
-root 获取项目跟路径, 默认当前目录
|
||||
-http_from 指定import的http函数位置
|
||||
-info_tags 指定注释中的tag显示于接口说明
|
||||
-h 显示帮助信息
|
||||
Has:
|
||||
Description:
|
||||
根据swagger生成ts请求文件
|
||||
````
|
||||
@@ -39,6 +39,15 @@ func (j *Js) Configure() command.Configure {
|
||||
Name: "tag",
|
||||
Description: "只生成指定tag的请求",
|
||||
},
|
||||
{
|
||||
Name: "http_from",
|
||||
Description: "指定import的http函数位置",
|
||||
Default: "@/utils/request",
|
||||
},
|
||||
{
|
||||
Name: "info_tags",
|
||||
Description: "指定注释中的tag显示于接口说明",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -89,8 +98,8 @@ func (j *Js) Execute(input command.Input) {
|
||||
fixSwaggerType(&swagger)
|
||||
|
||||
tag := input.GetOption("tag")
|
||||
str := `import http from '@/utils/request'
|
||||
`
|
||||
infoTags := input.GetOptions("info_tags")
|
||||
str := fmt.Sprintf("import http from '%s'\n", input.GetOption("http_from"))
|
||||
for _, url := range sortPathMap(swagger.Paths) {
|
||||
paths := swagger.Paths[url]
|
||||
re, _ := regexp.Compile("\\$\\[.+\\]")
|
||||
@@ -113,6 +122,14 @@ func (j *Js) Execute(input command.Input) {
|
||||
for _, method := range methods {
|
||||
if method.cm {
|
||||
isResponse = false
|
||||
//Tags说明
|
||||
var tagInfo string
|
||||
for _, s := range infoTags {
|
||||
info := getTagInfo(method.e.Description, s)
|
||||
if info != "" {
|
||||
tagInfo += fmt.Sprintf("\n * @%s %s", s, info)
|
||||
}
|
||||
}
|
||||
var paramNames []string
|
||||
paramStr := genJsRequest(method.e.Parameters, swagger)
|
||||
var dataStr string
|
||||
@@ -122,7 +139,7 @@ func (j *Js) Execute(input command.Input) {
|
||||
}
|
||||
if len(urlParams) > 0 {
|
||||
for _, urlParam := range urlParams {
|
||||
paramStr += "\n * @param " + urlParam.Name + " " + urlParam.Type
|
||||
paramStr += "\n * @param {string|number} " + urlParam.Name
|
||||
paramNames = append(paramNames, urlParam.Name)
|
||||
}
|
||||
}
|
||||
@@ -135,15 +152,16 @@ func (j *Js) Execute(input command.Input) {
|
||||
}
|
||||
str += fmt.Sprintf(`
|
||||
/**
|
||||
* %v%v
|
||||
* @returns {Promise<{code:Number,data:%v,message:string}>}
|
||||
* %v%v%v
|
||||
* @returns {Promise<{code:number,data:%v,message:string}>}
|
||||
* @callback
|
||||
*/
|
||||
export async function %v%v(%v) {
|
||||
return await http.%v(%v%v)
|
||||
}
|
||||
`,
|
||||
method.e.Description,
|
||||
method.e.Summary,
|
||||
tagInfo,
|
||||
paramStr,
|
||||
response,
|
||||
parser.StringToHump(strings.Trim(strings.ReplaceAll(funcName, "/", "_"), "_")),
|
||||
@@ -189,6 +207,10 @@ func genJsRequest(p openapi.Parameters, swagger openapi.Spec) string {
|
||||
} else if parameter.Items != nil {
|
||||
t = getObjectStrFromRef(parameter.Items.Ref, swagger) + t
|
||||
}
|
||||
case "", "object":
|
||||
if parameter.Schema != nil {
|
||||
t = getObjectStrFromRef(parameter.Schema.Ref, swagger)
|
||||
}
|
||||
}
|
||||
if i != 0 {
|
||||
str += ","
|
||||
@@ -302,6 +324,9 @@ func getObjectStrFromRef(ref string, swagger openapi.Spec) string {
|
||||
def := strings.Replace(ref, "#/definitions/", "", 1)
|
||||
var params []string
|
||||
if _, ok := swagger.Definitions[def]; ok {
|
||||
if isEnum(swagger.Definitions[def]) {
|
||||
return "number"
|
||||
}
|
||||
for key, schema := range swagger.Definitions[def].Properties {
|
||||
if !isResponse && !parser.InArrString(key, swagger.Definitions[def].Required) {
|
||||
key = key + "?"
|
||||
@@ -325,14 +350,14 @@ func getJsType(schema *openapi.Schema, swagger openapi.Spec, ref string) string
|
||||
if ref == schema.Items.Ref {
|
||||
t = "{}" + t
|
||||
} else {
|
||||
t = getObjectStrFromRef(schema.Items.Ref, swagger) + t
|
||||
t = getJsType(schema.Items, swagger, schema.Items.Ref) + t
|
||||
}
|
||||
}
|
||||
case "object":
|
||||
if ref == schema.Items.Ref {
|
||||
case "object", "":
|
||||
if ref == schema.Ref {
|
||||
t = "{}"
|
||||
} else {
|
||||
t = getObjectStrFromRef(schema.Items.Ref, swagger)
|
||||
t = getObjectStrFromRef(schema.Ref, swagger)
|
||||
}
|
||||
}
|
||||
return t
|
||||
|
||||
@@ -571,9 +571,10 @@ func findMessage(message string, nowDirProtoc []parser.ProtocFileParser, allProt
|
||||
}
|
||||
|
||||
func filterRequired(doc string) (string, bool) {
|
||||
re := regexp.MustCompile("@(tag|Tag|TAG)\\(\\\"([a-zA-Z]+)\"[,\\s\\\"]+([a-zA-Z]+)\"\\)")
|
||||
re := regexp.MustCompile("(?i)[//\\s]+@(tag)\\(\\\"binding\"([,\\s\\\"]+[^\\)]+)\\)")
|
||||
arr := re.FindStringSubmatch(doc)
|
||||
if len(arr) == 4 && strings.ToLower(arr[2]) == "binding" && strings.ToLower(arr[3]) == "required" {
|
||||
r := regexp.MustCompile("(?i)[,\\s\\\"]required[,\\s\\\"]")
|
||||
if len(arr) == 3 && r.MatchString(arr[2]) {
|
||||
doc = strings.Trim(re.ReplaceAllString(doc, ""), "\r\n")
|
||||
return doc, true
|
||||
}
|
||||
|
||||
318
console/commands/ts.go
Normal file
318
console/commands/ts.go
Normal file
@@ -0,0 +1,318 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/console/commands/openapi"
|
||||
"github.com/go-home-admin/toolset/parser"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Ts @Bean
|
||||
type Ts struct {
|
||||
enums map[string]string
|
||||
params map[string]string
|
||||
objects map[string]string
|
||||
swagger openapi.Spec
|
||||
}
|
||||
|
||||
func (t *Ts) Init() {
|
||||
t.enums = make(map[string]string)
|
||||
t.params = make(map[string]string)
|
||||
t.objects = make(map[string]string)
|
||||
}
|
||||
|
||||
func (t *Ts) Configure() command.Configure {
|
||||
return command.Configure{
|
||||
Name: "make:ts",
|
||||
Description: "根据swagger生成ts结构及接口请求方法",
|
||||
Input: command.Argument{
|
||||
Option: []command.ArgParam{
|
||||
{
|
||||
Name: "in",
|
||||
Description: "swagger.json路径, 可本地可远程",
|
||||
Default: "@root/web/swagger.json",
|
||||
},
|
||||
{
|
||||
Name: "out",
|
||||
Description: "ts文件输出路径",
|
||||
Default: "@root/resources/src/api/swagger_gen.ts",
|
||||
},
|
||||
{
|
||||
Name: "tag",
|
||||
Description: "只生成指定tag的请求",
|
||||
},
|
||||
{
|
||||
Name: "http_from",
|
||||
Description: "指定import的http函数位置",
|
||||
Default: "@/utils/request",
|
||||
},
|
||||
{
|
||||
Name: "info_tags",
|
||||
Description: "指定注释中的tag显示于接口说明",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Ts) Execute(input command.Input) {
|
||||
root := getRootPath()
|
||||
in := input.GetOption("in")
|
||||
in = strings.Replace(in, "@root", root, 1)
|
||||
inSwaggerStr := ""
|
||||
if strings.Index(in, "http") == 0 {
|
||||
// 远程获取文件
|
||||
req, _ := http.NewRequest("GET", in, nil)
|
||||
res, _ := http.DefaultClient.Do(req)
|
||||
defer res.Body.Close()
|
||||
//得到返回结果
|
||||
body, _ := io.ReadAll(res.Body)
|
||||
inSwaggerStr = string(body)
|
||||
} else {
|
||||
body, _ := os.ReadFile(in)
|
||||
inSwaggerStr = string(body)
|
||||
}
|
||||
out := input.GetOption("out")
|
||||
out = strings.Replace(out, "@root", root, 1)
|
||||
|
||||
t.swagger = openapi.Spec{
|
||||
Swagger: "2.0",
|
||||
Info: openapi.Info{
|
||||
Title: "2",
|
||||
Description: "2",
|
||||
Version: "2",
|
||||
},
|
||||
Host: "api.swagger.com",
|
||||
Schemes: []string{"https"},
|
||||
BasePath: "/",
|
||||
Produces: []string{"application/json"},
|
||||
Paths: make(map[string]*openapi.Path),
|
||||
Definitions: map[string]*openapi.Schema{
|
||||
"google.protobuf.Any": {
|
||||
Type: "object",
|
||||
},
|
||||
},
|
||||
Parameters: nil,
|
||||
Extensions: nil,
|
||||
GlobalOptions: nil,
|
||||
}
|
||||
_ = json.Unmarshal([]byte(inSwaggerStr), &t.swagger)
|
||||
fixSwaggerType(&t.swagger)
|
||||
|
||||
tag := input.GetOption("tag")
|
||||
infoTags := input.GetOptions("info_tags")
|
||||
str := fmt.Sprintf("import http from '%s'\n", input.GetOption("http_from"))
|
||||
for _, url := range sortPathMap(t.swagger.Paths) {
|
||||
paths := t.swagger.Paths[url]
|
||||
re, _ := regexp.Compile("\\$\\[.+\\]")
|
||||
url = re.ReplaceAllString(url, "")
|
||||
url, funcName, params := analysisUrl(url)
|
||||
urlQuery := make([]*openapi.Parameter, 0)
|
||||
for _, p := range params {
|
||||
urlQuery = append(urlQuery, &openapi.Parameter{
|
||||
Name: p,
|
||||
Required: true,
|
||||
Type: "string",
|
||||
})
|
||||
}
|
||||
methods := make([]makeJsCache, 0)
|
||||
methods = append(methods, makeJsCache{e: paths.Get, cm: canMakeJs(paths.Get, tag), method: "get"})
|
||||
methods = append(methods, makeJsCache{e: paths.Put, cm: canMakeJs(paths.Put, tag), method: "put"})
|
||||
methods = append(methods, makeJsCache{e: paths.Post, cm: canMakeJs(paths.Post, tag), method: "post"})
|
||||
methods = append(methods, makeJsCache{e: paths.Patch, cm: canMakeJs(paths.Patch, tag), method: "patch"})
|
||||
methods = append(methods, makeJsCache{e: paths.Delete, cm: canMakeJs(paths.Delete, tag), method: "delete"})
|
||||
for _, method := range methods {
|
||||
if !method.cm {
|
||||
continue
|
||||
}
|
||||
//Tags说明
|
||||
var tagInfo string
|
||||
for _, s := range infoTags {
|
||||
info := getTagInfo(method.e.Description, s)
|
||||
if info != "" {
|
||||
tagInfo += fmt.Sprintf("\n * @%s %s", s, info)
|
||||
}
|
||||
}
|
||||
//func名
|
||||
fName := parser.StringToHump(strings.Trim(strings.ReplaceAll(funcName, "/", "_"), "_"))
|
||||
//请求参数
|
||||
var paramStr, hasParams string
|
||||
if len(method.e.Parameters) > 0 {
|
||||
typeName := fName + parser.StringToHump(method.method) + "Params"
|
||||
paramStr = "params: " + typeName
|
||||
hasParams = "\n params,"
|
||||
t.genTsParams(typeName, method.e.Parameters)
|
||||
}
|
||||
//URL参数
|
||||
for _, urlParam := range urlQuery {
|
||||
paramStr += fmt.Sprintf(", %s: %s", urlParam.Name, "string|number")
|
||||
}
|
||||
paramStr = strings.Trim(paramStr, ", ")
|
||||
//响应结构
|
||||
var response string
|
||||
if _, ok := method.e.Responses["200"]; ok {
|
||||
if method.e.Responses["200"].Schema != nil {
|
||||
response = t.genType(method.e.Responses["200"].Schema.Ref)
|
||||
}
|
||||
}
|
||||
str += fmt.Sprintf(`
|
||||
/**
|
||||
* %v%v
|
||||
*/
|
||||
export const %v%v = (%v) => {
|
||||
return http.%v(
|
||||
%v,%v
|
||||
) as Promise<{code:number,data:%v,message:string}>;
|
||||
}
|
||||
`,
|
||||
strings.Trim(method.e.Summary, " "),
|
||||
tagInfo,
|
||||
fName,
|
||||
parser.StringToHump(method.method),
|
||||
paramStr,
|
||||
method.method,
|
||||
url,
|
||||
hasParams,
|
||||
response,
|
||||
)
|
||||
}
|
||||
}
|
||||
//插入枚举
|
||||
str += "\n"
|
||||
for _, e := range t.enums {
|
||||
str += e
|
||||
}
|
||||
//插入请求参数
|
||||
str += "\n"
|
||||
for _, e := range t.params {
|
||||
str += e
|
||||
}
|
||||
//插入结构
|
||||
str += "\n"
|
||||
for _, e := range t.objects {
|
||||
str += e
|
||||
}
|
||||
_ = os.WriteFile(out, []byte(str), 0766)
|
||||
}
|
||||
|
||||
func (t *Ts) genTsParams(typeName string, params []*openapi.Parameter) {
|
||||
str := "export type " + typeName + " = {\n"
|
||||
for _, parameter := range params {
|
||||
ty := t.getTsTypeFromParameter(parameter)
|
||||
if !parameter.Required {
|
||||
parameter.Name = parameter.Name + "?"
|
||||
}
|
||||
if parameter.Description != "" {
|
||||
str += fmt.Sprintf(" // %s\n", t.clearEmpty(parameter.Description))
|
||||
}
|
||||
str += fmt.Sprintf(" %v: %v;\n", parameter.Name, ty)
|
||||
}
|
||||
str += "}\n"
|
||||
t.params[typeName] = str
|
||||
}
|
||||
|
||||
func (t *Ts) genType(ref string) string {
|
||||
def := strings.Replace(ref, "#/definitions/", "", 1)
|
||||
key := strings.ReplaceAll(def, ".", "_")
|
||||
if _, ok := t.objects[key]; ok {
|
||||
return key
|
||||
}
|
||||
if _, ok := t.enums[key]; ok {
|
||||
return key
|
||||
}
|
||||
if _, ok := t.swagger.Definitions[def]; ok {
|
||||
if isEnum(t.swagger.Definitions[def]) {
|
||||
t.genEnums(key, t.swagger.Definitions[def])
|
||||
return key
|
||||
}
|
||||
if len(t.swagger.Definitions[def].Properties) == 0 {
|
||||
return "{}"
|
||||
}
|
||||
str := fmt.Sprintf("export type %s = {\n", key)
|
||||
for k, schema := range t.swagger.Definitions[def].Properties {
|
||||
if schema.Description != "" {
|
||||
str += fmt.Sprintf(" // %s\n", t.clearEmpty(schema.Description))
|
||||
}
|
||||
str += fmt.Sprintf(" %s: %s;\n", k, t.getTsTypeFromSchema(schema, ref))
|
||||
}
|
||||
str += "}\n"
|
||||
t.objects[key] = str
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func (t *Ts) getTsTypeFromParameter(param *openapi.Parameter) string {
|
||||
if param.Schema != nil {
|
||||
return t.getTsTypeFromSchema(param.Schema, param.Format)
|
||||
}
|
||||
return t.getTsTypeFromSchema(&openapi.Schema{
|
||||
Description: param.Description,
|
||||
Ref: param.Ref,
|
||||
Type: param.Type,
|
||||
Format: param.Format,
|
||||
Enum: param.Enum,
|
||||
Items: param.Items,
|
||||
}, "")
|
||||
}
|
||||
|
||||
func (t *Ts) getTsTypeFromSchema(schema *openapi.Schema, ref string) string {
|
||||
ty := schema.Type
|
||||
switch schema.Type {
|
||||
case "integer", "Number":
|
||||
ty = "number"
|
||||
case "array":
|
||||
if schema.Items != nil {
|
||||
ty = t.getTsTypeFromSchema(schema.Items, ref)
|
||||
}
|
||||
ty += "[]"
|
||||
case "", "object":
|
||||
if ref == schema.Ref {
|
||||
//结构引用自己,防止死循环
|
||||
ty = strings.ReplaceAll(strings.Replace(ref, "#/definitions/", "", 1), ".", "_")
|
||||
} else if schema.Ref != "" {
|
||||
ty = t.genType(schema.Ref)
|
||||
}
|
||||
}
|
||||
return ty
|
||||
}
|
||||
|
||||
func (t *Ts) genEnums(enumName string, schema *openapi.Schema) {
|
||||
str := fmt.Sprintf("export enum %s {\n", enumName)
|
||||
for k, item := range schema.Properties {
|
||||
desc := t.clearEmpty(strings.TrimLeft(item.Description, "enum|"))
|
||||
if desc != "" {
|
||||
str += fmt.Sprintf(" // %s\n", desc)
|
||||
}
|
||||
str += fmt.Sprintf(" %s = %s,\n", item.Type, k)
|
||||
}
|
||||
str += "}\n"
|
||||
t.enums[enumName] = str
|
||||
}
|
||||
|
||||
func (t *Ts) clearEmpty(str string) string {
|
||||
return strings.Trim(strings.ReplaceAll(str, "\n", ""), " ")
|
||||
}
|
||||
|
||||
func isEnum(schema *openapi.Schema) bool {
|
||||
if first, ok := schema.Properties["0"]; ok && strings.Index(first.Description, "enum") == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getTagInfo(doc, tag string) string {
|
||||
re := regexp.MustCompile("@(tag|Tag|TAG)\\(\\\"([^\"]+)\"[,\\s\\\"]+([^\"]+)\"\\)")
|
||||
arr := re.FindAllStringSubmatch(doc, -1)
|
||||
for _, item := range arr {
|
||||
if strings.ToLower(item[2]) == strings.ToLower(tag) {
|
||||
return strings.ToLower(item[3])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -15,6 +15,7 @@ var _OrmCommandSingle *OrmCommand
|
||||
var _ProtocCommandSingle *ProtocCommand
|
||||
var _RouteCommandSingle *RouteCommand
|
||||
var _SwaggerCommandSingle *SwaggerCommand
|
||||
var _TsSingle *Ts
|
||||
|
||||
func GetAllProvider() []interface{} {
|
||||
return []interface{}{
|
||||
@@ -28,6 +29,7 @@ func GetAllProvider() []interface{} {
|
||||
NewProtocCommand(),
|
||||
NewRouteCommand(),
|
||||
NewSwaggerCommand(),
|
||||
NewTs(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,3 +103,10 @@ func NewSwaggerCommand() *SwaggerCommand {
|
||||
}
|
||||
return _SwaggerCommandSingle
|
||||
}
|
||||
func NewTs() *Ts {
|
||||
if _TsSingle == nil {
|
||||
_TsSingle = &Ts{}
|
||||
providers.AfterProvider(_TsSingle, "")
|
||||
}
|
||||
return _TsSingle
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user