mirror of https://github.com/beego/bee.git
swagger结构解析更新
This commit is contained in:
parent
e56f6a4fe7
commit
a20d5b3052
|
@ -37,7 +37,6 @@ import (
|
||||||
"github.com/astaxie/beego/utils"
|
"github.com/astaxie/beego/utils"
|
||||||
"github.com/beego/bee/logger"
|
"github.com/beego/bee/logger"
|
||||||
bu "github.com/beego/bee/utils"
|
bu "github.com/beego/bee/utils"
|
||||||
"sort"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -55,6 +54,7 @@ var controllerList map[string]map[string]*swagger.Item //controllername Paths it
|
||||||
var modelsList map[string]map[string]swagger.Schema
|
var modelsList map[string]map[string]swagger.Schema
|
||||||
var rootapi swagger.Swagger
|
var rootapi swagger.Swagger
|
||||||
var astPkgs []*ast.Package
|
var astPkgs []*ast.Package
|
||||||
|
var finds map[string]string
|
||||||
|
|
||||||
// refer to builtin.go
|
// refer to builtin.go
|
||||||
var basicTypes = map[string]string{
|
var basicTypes = map[string]string{
|
||||||
|
@ -92,6 +92,7 @@ func init() {
|
||||||
controllerList = make(map[string]map[string]*swagger.Item)
|
controllerList = make(map[string]map[string]*swagger.Item)
|
||||||
modelsList = make(map[string]map[string]swagger.Schema)
|
modelsList = make(map[string]map[string]swagger.Schema)
|
||||||
astPkgs = make([]*ast.Package, 0)
|
astPkgs = make([]*ast.Package, 0)
|
||||||
|
finds = make(map[string]string, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParsePackagesFromDir(dirpath string) {
|
func ParsePackagesFromDir(dirpath string) {
|
||||||
|
@ -263,8 +264,17 @@ func GenerateDocs(curpath string) {
|
||||||
for _, l := range stmt.Rhs {
|
for _, l := range stmt.Rhs {
|
||||||
if v, ok := l.(*ast.CallExpr); ok {
|
if v, ok := l.(*ast.CallExpr); ok {
|
||||||
// Analyse NewNamespace, it will return version and the subfunction
|
// Analyse NewNamespace, it will return version and the subfunction
|
||||||
if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
|
//if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
|
||||||
continue
|
// continue
|
||||||
|
//}
|
||||||
|
if sel, ok := v.Fun.(*ast.SelectorExpr); ok {
|
||||||
|
if sel.Sel.String() != "NewNamespace" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else if ident, ok := v.Fun.(*ast.Ident); ok {
|
||||||
|
if ident.String() != "NewNamespace" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
version, params := analyseNewNamespace(v)
|
version, params := analyseNewNamespace(v)
|
||||||
if rootapi.BasePath == "" && version != "" {
|
if rootapi.BasePath == "" && version != "" {
|
||||||
|
@ -392,6 +402,29 @@ func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
|
||||||
return cname
|
return cname
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//从gopath中查询包
|
||||||
|
func findPackage(packagename string) bool {
|
||||||
|
if _, ok := finds[packagename]; ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
finds[packagename] = packagename
|
||||||
|
gopaths := bu.GetGOPATHs()
|
||||||
|
find := false
|
||||||
|
//这里可以通过参数解析指定目录,跳过多余的目录解析
|
||||||
|
for _, v := range gopaths {
|
||||||
|
filepath.Walk(filepath.Join(v, "src"), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if info.IsDir() {
|
||||||
|
if info.Name() == packagename {
|
||||||
|
parsePackageFromDir(path)
|
||||||
|
find = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return find
|
||||||
|
}
|
||||||
|
|
||||||
func analyseControllerPkg(vendorPath, localName, pkgpath string) {
|
func analyseControllerPkg(vendorPath, localName, pkgpath string) {
|
||||||
pkgpath = strings.Trim(pkgpath, "\"")
|
pkgpath = strings.Trim(pkgpath, "\"")
|
||||||
if isSystemPackage(pkgpath) {
|
if isSystemPackage(pkgpath) {
|
||||||
|
@ -444,6 +477,11 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) {
|
||||||
}
|
}
|
||||||
for _, pkg := range astPkgs {
|
for _, pkg := range astPkgs {
|
||||||
for _, fl := range pkg.Files {
|
for _, fl := range pkg.Files {
|
||||||
|
for _, v := range fl.Imports {
|
||||||
|
for _, v1 := range gopaths {
|
||||||
|
parsePackageFromDir(filepath.Join(v1+"/src/", strings.Trim(v.Path.Value, "\"")))
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, d := range fl.Decls {
|
for _, d := range fl.Decls {
|
||||||
switch specDecl := d.(type) {
|
switch specDecl := d.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
|
@ -559,12 +597,20 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
|
||||||
schema.Format = typeFormat[1]
|
schema.Format = typeFormat[1]
|
||||||
} else {
|
} else {
|
||||||
m, mod, realTypes := getModel(schemaName)
|
m, mod, realTypes := getModel(schemaName)
|
||||||
schema.Ref = "#/definitions/" + m
|
if mod.Type != "object" {
|
||||||
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
if sType, ok := basicTypes[mod.Type]; ok {
|
||||||
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
schema.Type = typeFormat[0]
|
||||||
|
schema.Format = typeFormat[1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
schema.Ref = "#/definitions/" + m
|
||||||
|
if _, ok := modelsList[pkgpath+controllerName]; !ok {
|
||||||
|
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
|
||||||
|
}
|
||||||
|
modelsList[pkgpath+controllerName][schemaName] = mod
|
||||||
|
appendModels(pkgpath, controllerName, realTypes)
|
||||||
}
|
}
|
||||||
modelsList[pkgpath+controllerName][schemaName] = mod
|
|
||||||
appendModels(pkgpath, controllerName, realTypes)
|
|
||||||
}
|
}
|
||||||
if isArray {
|
if isArray {
|
||||||
rs.Schema = &swagger.Schema{
|
rs.Schema = &swagger.Schema{
|
||||||
|
@ -756,7 +802,6 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
|
||||||
paraFormat = typeFormat[1]
|
paraFormat = typeFormat[1]
|
||||||
} else {
|
} else {
|
||||||
m, mod, realTypes := getModel(typ)
|
m, mod, realTypes := getModel(typ)
|
||||||
fmt.Printf("类型:%s\n", mod.Type)
|
|
||||||
if mod.Type != "object" {
|
if mod.Type != "object" {
|
||||||
if sType, ok := basicTypes[mod.Type]; ok {
|
if sType, ok := basicTypes[mod.Type]; ok {
|
||||||
typeFormat := strings.Split(sType, ":")
|
typeFormat := strings.Split(sType, ":")
|
||||||
|
@ -879,7 +924,13 @@ func getparams(str string) []string {
|
||||||
|
|
||||||
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) {
|
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) {
|
||||||
strs := strings.Split(str, ".")
|
strs := strings.Split(str, ".")
|
||||||
|
if len(strs) > 2 {
|
||||||
|
strs[0] = strs[len(strs)-2]
|
||||||
|
}
|
||||||
objectname = strs[len(strs)-1]
|
objectname = strs[len(strs)-1]
|
||||||
|
if isBasicType(objectname) {
|
||||||
|
return
|
||||||
|
}
|
||||||
packageName := ""
|
packageName := ""
|
||||||
m.Type = "object"
|
m.Type = "object"
|
||||||
for _, pkg := range astPkgs {
|
for _, pkg := range astPkgs {
|
||||||
|
@ -901,7 +952,12 @@ func getModel(str string) (objectname string, m swagger.Schema, realTypes []stri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.Title == "" {
|
if m.Title == "" {
|
||||||
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
find := findPackage(strs[0])
|
||||||
|
if find {
|
||||||
|
return getModel(strs[0] + "." + objectname)
|
||||||
|
} else {
|
||||||
|
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
||||||
|
}
|
||||||
// TODO remove when all type have been supported
|
// TODO remove when all type have been supported
|
||||||
//os.Exit(1)
|
//os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -919,117 +975,12 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
|
||||||
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d)
|
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d)
|
||||||
}
|
}
|
||||||
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
||||||
_, ok = ts.Type.(*ast.StructType)
|
st, ok := ts.Type.(*ast.StructType)
|
||||||
if !ok {
|
if !ok {
|
||||||
m.Title = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
|
m.Title = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
|
||||||
m.Type = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
|
m.Type = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
|
||||||
return
|
return
|
||||||
switch t := ts.Type.(type) {
|
|
||||||
case *ast.ArrayType:
|
|
||||||
m.Title = k
|
|
||||||
m.Type = "array"
|
|
||||||
if isBasicType(fmt.Sprint(t.Elt)) {
|
|
||||||
typeFormat := strings.Split(basicTypes[fmt.Sprint(t.Elt)], ":")
|
|
||||||
m.Format = typeFormat[0]
|
|
||||||
} else {
|
|
||||||
objectName := packageName + "." + fmt.Sprint(t.Elt)
|
|
||||||
if _, ok := rootapi.Definitions[objectName]; !ok {
|
|
||||||
objectName, _, _ = getModel(objectName)
|
|
||||||
}
|
|
||||||
m.Items = &swagger.Schema{
|
|
||||||
Ref: "#/definitions/" + objectName,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.Ident:
|
|
||||||
parseIdent(t, k, m, astPkgs)
|
|
||||||
case *ast.StructType:
|
|
||||||
parseStruct(t, k, m, realTypes, astPkgs, packageName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// parse as enum, in the package, find out all consts with the same type
|
|
||||||
func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Package) {
|
|
||||||
m.Title = k
|
|
||||||
basicType := fmt.Sprint(st)
|
|
||||||
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
|
|
||||||
basicType = object
|
|
||||||
}
|
|
||||||
if k, ok := basicTypes[basicType]; ok {
|
|
||||||
typeFormat := strings.Split(k, ":")
|
|
||||||
m.Type = typeFormat[0]
|
|
||||||
m.Format = typeFormat[1]
|
|
||||||
}
|
|
||||||
enums := make(map[int]string)
|
|
||||||
enumValues := make(map[int]interface{})
|
|
||||||
for _, pkg := range astPkgs {
|
|
||||||
for _, fl := range pkg.Files {
|
|
||||||
for _, obj := range fl.Scope.Objects {
|
|
||||||
if obj.Kind == ast.Con {
|
|
||||||
vs, ok := obj.Decl.(*ast.ValueSpec)
|
|
||||||
if !ok {
|
|
||||||
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v\n", vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
ti, ok := vs.Type.(*ast.Ident)
|
|
||||||
if !ok {
|
|
||||||
// TODO type inference, iota not support yet
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Only add the enums that are defined by the current identifier
|
|
||||||
if ti.Name != k {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// For all names and values, aggregate them by it's position so that we can sort them later.
|
|
||||||
for i, val := range vs.Values {
|
|
||||||
v, ok := val.(*ast.BasicLit)
|
|
||||||
if !ok {
|
|
||||||
beeLogger.Log.Warnf("Unknown type without BasicLit: %v\n", v)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enums[int(val.Pos())] = fmt.Sprintf("%s = %s", vs.Names[i].Name, v.Value)
|
|
||||||
switch v.Kind {
|
|
||||||
case token.INT:
|
|
||||||
vv, err := strconv.Atoi(v.Value)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enumValues[int(val.Pos())] = vv
|
|
||||||
case token.FLOAT:
|
|
||||||
vv, err := strconv.ParseFloat(v.Value, 64)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v\n", v.Value)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
enumValues[int(val.Pos())] = vv
|
|
||||||
default:
|
|
||||||
enumValues[int(val.Pos())] = strings.Trim(v.Value, `"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Sort the enums by position
|
|
||||||
if len(enums) > 0 {
|
|
||||||
var keys []int
|
|
||||||
for k := range enums {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Ints(keys)
|
|
||||||
for _, k := range keys {
|
|
||||||
m.Enum = append(m.Enum, enums[k])
|
|
||||||
}
|
|
||||||
// Automatically use the first enum value as the example.
|
|
||||||
m.Example = enumValues[keys[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]string, astPkgs []*ast.Package, packageName string) {
|
|
||||||
m.Title = k
|
m.Title = k
|
||||||
if st.Fields.List != nil {
|
if st.Fields.List != nil {
|
||||||
m.Properties = make(map[string]swagger.Propertie)
|
m.Properties = make(map[string]swagger.Propertie)
|
||||||
|
@ -1049,7 +1000,7 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
|
||||||
mp := swagger.Propertie{}
|
mp := swagger.Propertie{}
|
||||||
if isSlice {
|
if isSlice {
|
||||||
mp.Type = "array"
|
mp.Type = "array"
|
||||||
if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
|
if isBasicType(strings.Replace(realType, "[]", "", -1)) {
|
||||||
typeFormat := strings.Split(sType, ":")
|
typeFormat := strings.Split(sType, ":")
|
||||||
mp.Items = &swagger.Propertie{
|
mp.Items = &swagger.Propertie{
|
||||||
Type: typeFormat[0],
|
Type: typeFormat[0],
|
||||||
|
|
Loading…
Reference in New Issue