feat: add swag tool
This commit is contained in:
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -15,6 +15,18 @@
|
||||
"init",
|
||||
"/projects/tt",
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Gen_Route",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}",
|
||||
"args": [
|
||||
"gen",
|
||||
"route",
|
||||
"/projects/learn/go-demo",
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -136,8 +136,7 @@ func ParseFile(file string) []RouteDefinition {
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "@Bind") {
|
||||
//@Bind name query key() table() model()
|
||||
//@Bind name query
|
||||
//@Bind name [uri|query|path|body|header|cookie] [key()] [table()] [model()]
|
||||
bindParams = append(bindParams, parseRouteBind(line))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (ps *tagBaseFieldParser) FieldNames() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
var names = make([]string, 0, len(ps.field.Names))
|
||||
names := make([]string, 0, len(ps.field.Names))
|
||||
for _, name := range ps.field.Names {
|
||||
switch ps.p.PropNamingStrategy {
|
||||
case SnakeCase:
|
||||
@@ -247,7 +247,7 @@ func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error {
|
||||
}
|
||||
|
||||
if IsRefSchema(schema) {
|
||||
var newSchema = spec.Schema{}
|
||||
newSchema := spec.Schema{}
|
||||
err := ps.complementSchema(&newSchema, types)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -658,9 +658,11 @@ const (
|
||||
// These code copy from
|
||||
// https://github.com/go-playground/validator/blob/d4271985b44b735c6f76abc7a06532ee997f9476/baked_in.go#L207
|
||||
// ---.
|
||||
var oneofValsCache = map[string][]string{}
|
||||
var oneofValsCacheRWLock = sync.RWMutex{}
|
||||
var splitParamsRegex = regexp.MustCompile(`'[^']*'|\S+`)
|
||||
var (
|
||||
oneofValsCache = map[string][]string{}
|
||||
oneofValsCacheRWLock = sync.RWMutex{}
|
||||
splitParamsRegex = regexp.MustCompile(`'[^']*'|\S+`)
|
||||
)
|
||||
|
||||
func parseOneOfParam2(param string) []string {
|
||||
oneofValsCacheRWLock.RLock()
|
||||
|
||||
@@ -680,7 +680,8 @@ func TestValidTags(t *testing.T) {
|
||||
Names: []*ast.Ident{{Name: "Test"}},
|
||||
Tag: &ast.BasicLit{
|
||||
Value: `json:"test" validate:"required,oneof=one two"`,
|
||||
}},
|
||||
},
|
||||
},
|
||||
).ComplementSchema(&schema)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, schema.Enum)
|
||||
@@ -695,7 +696,8 @@ func TestValidTags(t *testing.T) {
|
||||
Names: []*ast.Ident{{Name: "Test"}},
|
||||
Tag: &ast.BasicLit{
|
||||
Value: `form:"test[]"`,
|
||||
}},
|
||||
},
|
||||
},
|
||||
).FieldNames()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", filednames[0])
|
||||
@@ -706,7 +708,8 @@ func TestValidTags(t *testing.T) {
|
||||
Names: []*ast.Ident{{Name: "Test"}},
|
||||
Tag: &ast.BasicLit{
|
||||
Value: `form:"test"`,
|
||||
}},
|
||||
},
|
||||
},
|
||||
).FieldNames()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", filednames[0])
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestFormat_DefaultExcludes(t *testing.T) {
|
||||
func TestFormat_ParseError(t *testing.T) {
|
||||
fx := setup(t)
|
||||
os.WriteFile(filepath.Join(fx.basedir, "parse_error.go"), []byte(`package main
|
||||
func invalid() {`), 0644)
|
||||
func invalid() {`), 0o644)
|
||||
assert.Error(t, New().Build(&Config{SearchDir: fx.basedir}))
|
||||
}
|
||||
|
||||
@@ -69,9 +69,9 @@ func TestFormat_ReadError(t *testing.T) {
|
||||
|
||||
func TestFormat_WriteError(t *testing.T) {
|
||||
fx := setup(t)
|
||||
os.Chmod(fx.basedir, 0555)
|
||||
os.Chmod(fx.basedir, 0o555)
|
||||
assert.Error(t, New().Build(&Config{SearchDir: fx.basedir}))
|
||||
os.Chmod(fx.basedir, 0755)
|
||||
os.Chmod(fx.basedir, 0o755)
|
||||
}
|
||||
|
||||
func TestFormat_InvalidSearchDir(t *testing.T) {
|
||||
@@ -91,10 +91,10 @@ func setup(t *testing.T) *fixture {
|
||||
}
|
||||
for filename, contents := range testFiles {
|
||||
fullpath := filepath.Join(fx.basedir, filepath.Clean(filename))
|
||||
if err := os.MkdirAll(filepath.Dir(fullpath), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Dir(fullpath), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(fullpath, contents, 0644); err != nil {
|
||||
if err := os.WriteFile(fullpath, contents, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,6 @@ func Test_AlignAttribute(t *testing.T) {
|
||||
`
|
||||
|
||||
testFormat(t, "align.go", contents, want)
|
||||
|
||||
}
|
||||
|
||||
func Test_SyntaxError(t *testing.T) {
|
||||
|
||||
@@ -90,7 +90,7 @@ func (pkgDefs *PackagesDefinitions) getTypeFromGenericParam(genericParam string,
|
||||
TypeSpec: &ast.TypeSpec{
|
||||
Name: ast.NewIdent(string(IgnoreNameOverridePrefix) + "map_" + parts[0] + "_" + typeSpecDef.TypeName()),
|
||||
Type: &ast.MapType{
|
||||
Key: ast.NewIdent(parts[0]), //assume key is string or integer
|
||||
Key: ast.NewIdent(parts[0]), // assume key is string or integer
|
||||
Value: expr,
|
||||
},
|
||||
},
|
||||
@@ -123,7 +123,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi
|
||||
return nil
|
||||
}
|
||||
|
||||
//generic[x,y any,z any] considered, TODO what if the type is not `any`, but a concrete one, such as `int32|int64` or an certain interface{}
|
||||
// generic[x,y any,z any] considered, TODO what if the type is not `any`, but a concrete one, such as `int32|int64` or an certain interface{}
|
||||
var formals []formalParamType
|
||||
for _, field := range original.TypeSpec.TypeParams.List {
|
||||
for _, ident := range field.Names {
|
||||
@@ -206,7 +206,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, origi
|
||||
|
||||
// splitGenericsTypeName splits a generic struct name in his parts
|
||||
func splitGenericsTypeName(fullGenericForm string) (string, []string) {
|
||||
//remove all spaces character
|
||||
// remove all spaces character
|
||||
fullGenericForm = strings.Map(func(r rune) rune {
|
||||
if unicode.IsSpace(r) {
|
||||
return -1
|
||||
@@ -254,7 +254,7 @@ func (pkgDefs *PackagesDefinitions) getParametrizedType(genTypeSpec *genericType
|
||||
}
|
||||
}
|
||||
|
||||
//a primitive type name or a type name in current package
|
||||
// a primitive type name or a type name in current package
|
||||
return &ast.Ident{Name: genTypeSpec.Name}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string, []string]")
|
||||
},
|
||||
}, "test.Field[string, []string]")
|
||||
assert.NotNil(t, typeSpec)
|
||||
assert.Equal(t, "$test.Field-string-array_string", typeSpec.Name())
|
||||
assert.Equal(t, "test.Field-string-array_string", typeSpec.TypeName())
|
||||
@@ -177,7 +178,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string, string]")
|
||||
},
|
||||
}, "test.Field[string, string]")
|
||||
assert.Nil(t, typeSpec)
|
||||
|
||||
// definition contains two type params, but only one is used
|
||||
@@ -188,7 +190,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string]")
|
||||
},
|
||||
}, "test.Field[string]")
|
||||
assert.Nil(t, typeSpec)
|
||||
|
||||
// name is not a valid type name
|
||||
@@ -199,7 +202,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string")
|
||||
},
|
||||
}, "test.Field[string")
|
||||
assert.Nil(t, typeSpec)
|
||||
|
||||
typeSpec = pd.parametrizeGenericType(
|
||||
@@ -209,7 +213,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string, [string]")
|
||||
},
|
||||
}, "test.Field[string, [string]")
|
||||
assert.Nil(t, typeSpec)
|
||||
|
||||
typeSpec = pd.parametrizeGenericType(
|
||||
@@ -219,7 +224,8 @@ func TestParametrizeStruct(t *testing.T) {
|
||||
Name: &ast.Ident{Name: "Field"},
|
||||
TypeParams: &ast.FieldList{List: []*ast.Field{{Names: []*ast.Ident{{Name: "T"}}}, {Names: []*ast.Ident{{Name: "T2"}}}}},
|
||||
Type: &ast.StructType{Struct: 100, Fields: &ast.FieldList{Opening: 101, Closing: 102}},
|
||||
}}, "test.Field[string, ]string]")
|
||||
},
|
||||
}, "test.Field[string, ]string]")
|
||||
assert.Nil(t, typeSpec)
|
||||
}
|
||||
|
||||
|
||||
@@ -598,7 +598,7 @@ func setCollectionFormatParam(param *spec.Parameter, name, schemaType, attr, com
|
||||
return fmt.Errorf("%s is attribute to set to an array. comment=%s got=%s", name, commentLine, schemaType)
|
||||
}
|
||||
|
||||
func setDefault(param *spec.Parameter, schemaType string, value string) error {
|
||||
func setDefault(param *spec.Parameter, schemaType, value string) error {
|
||||
val, err := defineType(schemaType, value)
|
||||
if err != nil {
|
||||
return nil // Don't set a default value if it's not valid
|
||||
@@ -609,7 +609,7 @@ func setDefault(param *spec.Parameter, schemaType string, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func setSchemaExample(param *spec.Parameter, schemaType string, value string) error {
|
||||
func setSchemaExample(param *spec.Parameter, schemaType, value string) error {
|
||||
val, err := defineType(schemaType, value)
|
||||
if err != nil {
|
||||
return nil // Don't set a example value if it's not valid
|
||||
@@ -630,7 +630,7 @@ func setSchemaExample(param *spec.Parameter, schemaType string, value string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func setExample(param *spec.Parameter, schemaType string, value string) error {
|
||||
func setExample(param *spec.Parameter, schemaType, value string) error {
|
||||
val, err := defineType(schemaType, value)
|
||||
if err != nil {
|
||||
return nil // Don't set a example value if it's not valid
|
||||
@@ -642,7 +642,7 @@ func setExample(param *spec.Parameter, schemaType string, value string) error {
|
||||
}
|
||||
|
||||
// defineType enum value define the type (object and array unsupported).
|
||||
func defineType(schemaType string, value string) (v interface{}, err error) {
|
||||
func defineType(schemaType, value string) (v interface{}, err error) {
|
||||
schemaType = TransToValidSchemeType(schemaType)
|
||||
|
||||
switch schemaType {
|
||||
@@ -1187,7 +1187,7 @@ func (operation *Operation) AddResponse(code int, response *spec.Response) {
|
||||
}
|
||||
|
||||
// createParameter returns swagger spec.Parameter for given paramType, description, paramName, schemaType, required.
|
||||
func createParameter(paramType, description, paramName, objectType, schemaType string, format string, required bool, enums []interface{}, collectionFormat string) spec.Parameter {
|
||||
func createParameter(paramType, description, paramName, objectType, schemaType, format string, required bool, enums []interface{}, collectionFormat string) spec.Parameter {
|
||||
// //five possible parameter types. query, path, body, header, form
|
||||
result := spec.Parameter{
|
||||
ParamProps: spec.ParamProps{
|
||||
@@ -1223,7 +1223,7 @@ func createParameter(paramType, description, paramName, objectType, schemaType s
|
||||
return result
|
||||
}
|
||||
|
||||
func getCodeExampleForSummary(summaryName string, dirPath string) ([]byte, error) {
|
||||
func getCodeExampleForSummary(summaryName, dirPath string) ([]byte, error) {
|
||||
dirEntries, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -100,16 +100,16 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr
|
||||
valueExpr.Value = strings.Replace(valueExpr.Value, "_", "", -1)
|
||||
}
|
||||
if len(valueExpr.Value) >= 2 && valueExpr.Value[0] == '0' {
|
||||
var start, base = 2, 8
|
||||
start, base := 2, 8
|
||||
switch valueExpr.Value[1] {
|
||||
case 'x', 'X':
|
||||
//hex
|
||||
// hex
|
||||
base = 16
|
||||
case 'b', 'B':
|
||||
//binary
|
||||
// binary
|
||||
base = 2
|
||||
default:
|
||||
//octet
|
||||
// octet
|
||||
start = 1
|
||||
}
|
||||
if x, err := strconv.ParseInt(valueExpr.Value[start:], base, 64); err == nil {
|
||||
@@ -121,7 +121,7 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr
|
||||
}
|
||||
}
|
||||
|
||||
//a basic literal integer is int type in default, or must have an explicit converting type in front
|
||||
// a basic literal integer is int type in default, or must have an explicit converting type in front
|
||||
if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil {
|
||||
return int(x), nil
|
||||
} else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil {
|
||||
@@ -153,7 +153,7 @@ func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr
|
||||
case *ast.ParenExpr:
|
||||
return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
|
||||
case *ast.CallExpr:
|
||||
//data conversion
|
||||
// data conversion
|
||||
if len(valueExpr.Args) != 1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -272,7 +272,6 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -420,7 +419,7 @@ func (pkgDefs *PackagesDefinitions) removeAllNotUniqueTypes() {
|
||||
}
|
||||
}
|
||||
|
||||
func (pkgDefs *PackagesDefinitions) findTypeSpec(pkgPath string, typeName string) *TypeSpecDef {
|
||||
func (pkgDefs *PackagesDefinitions) findTypeSpec(pkgPath, typeName string) *TypeSpecDef {
|
||||
if pkgDefs.packages == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ func ParseUsingGoList(enabled bool) func(parser *Parser) {
|
||||
}
|
||||
|
||||
// ParseAPI parses general api info for given searchDir and mainAPIFile.
|
||||
func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string, parseDepth int) error {
|
||||
func (parser *Parser) ParseAPI(searchDir, mainAPIFile string, parseDepth int) error {
|
||||
return parser.ParseAPIMultiSearchDir([]string{searchDir}, mainAPIFile, parseDepth)
|
||||
}
|
||||
|
||||
@@ -902,7 +902,7 @@ func isGeneralAPIComment(comments []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) {
|
||||
func getMarkdownForTag(tagName, dirPath string) ([]byte, error) {
|
||||
if tagName == "" {
|
||||
// this happens when parsing the @description.markdown attribute
|
||||
// it will be called properly another time with tagName="api"
|
||||
@@ -970,7 +970,6 @@ func getTagsFromComment(comment string) (tags []string) {
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (parser *Parser) matchTag(tag string) bool {
|
||||
@@ -1342,8 +1341,8 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error)
|
||||
|
||||
if len(typeSpecDef.Enums) > 0 {
|
||||
var varnames []string
|
||||
var enumComments = make(map[string]string)
|
||||
var enumDescriptions = make([]string, 0, len(typeSpecDef.Enums))
|
||||
enumComments := make(map[string]string)
|
||||
enumDescriptions := make([]string, 0, len(typeSpecDef.Enums))
|
||||
for _, value := range typeSpecDef.Enums {
|
||||
definition.Enum = append(definition.Enum, value.Value)
|
||||
varnames = append(varnames, value.key)
|
||||
@@ -1409,8 +1408,7 @@ func (parser *Parser) fillDefinitionDescription(definition *spec.Schema, file *a
|
||||
if typeSpec.Name != nil {
|
||||
typeName = typeSpec.Name.Name
|
||||
}
|
||||
definition.Description, err =
|
||||
parser.extractDeclarationDescription(typeName, typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc)
|
||||
definition.Description, err = parser.extractDeclarationDescription(typeName, typeSpec.Doc, typeSpec.Comment, generalDeclaration.Doc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package swag
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
@@ -184,7 +185,7 @@ func IsComplexSchema(schema *spec.Schema) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
//Object included, such as Object or []Object
|
||||
// Object included, such as Object or []Object
|
||||
for _, st := range schema.Type {
|
||||
if st == OBJECT {
|
||||
return true
|
||||
@@ -254,7 +255,7 @@ func BuildCustomSchema(types []string) (*spec.Schema, error) {
|
||||
}
|
||||
|
||||
// MergeSchema merge schemas
|
||||
func MergeSchema(dst *spec.Schema, src *spec.Schema) *spec.Schema {
|
||||
func MergeSchema(dst, src *spec.Schema) *spec.Schema {
|
||||
if len(src.Type) > 0 {
|
||||
dst.Type = src.Type
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (i *Spec) ReadDoc() string {
|
||||
},
|
||||
"escape": func(v interface{}) string {
|
||||
// escape tabs
|
||||
var str = strings.ReplaceAll(v.(string), "\t", "\\t")
|
||||
str := strings.ReplaceAll(v.(string), "\t", "\\t")
|
||||
// replace " with \", and if that results in \\", replace that with \\\"
|
||||
str = strings.ReplaceAll(str, "\"", "\\\"")
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ func (t *TypeSpecDef) SetSchemaName() {
|
||||
|
||||
// AstFileInfo information of an ast.File.
|
||||
type AstFileInfo struct {
|
||||
//FileSet the FileSet object which is used to parse this go source file
|
||||
// FileSet the FileSet object which is used to parse this go source file
|
||||
FileSet *token.FileSet
|
||||
|
||||
// File ast.File
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package swag
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFieldsByAnySpace(t *testing.T) {
|
||||
@@ -15,14 +16,16 @@ func TestFieldsByAnySpace(t *testing.T) {
|
||||
args args
|
||||
want []string
|
||||
}{
|
||||
{"test1",
|
||||
{
|
||||
"test1",
|
||||
args{
|
||||
" aa bb cc dd ff",
|
||||
2,
|
||||
},
|
||||
[]string{"aa", "bb\tcc dd \t\tff"},
|
||||
},
|
||||
{"test2",
|
||||
{
|
||||
"test2",
|
||||
args{
|
||||
` aa "bb cc dd ff"`,
|
||||
2,
|
||||
|
||||
Reference in New Issue
Block a user