|
|
@ -48,6 +48,12 @@ const ( |
|
|
|
aform = "multipart/form-data" |
|
|
|
) |
|
|
|
|
|
|
|
const ( |
|
|
|
astTypeArray = "array" |
|
|
|
astTypeObject = "object" |
|
|
|
astTypeMap = "map" |
|
|
|
) |
|
|
|
|
|
|
|
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
|
|
|
|
var controllerComments map[string]string |
|
|
|
var importlist map[string]string |
|
|
@ -78,11 +84,13 @@ var basicTypes = map[string]string{ |
|
|
|
"byte": "string:byte", |
|
|
|
"rune": "string:byte", |
|
|
|
// builtin golang objects
|
|
|
|
"time.Time": "string:datetime", |
|
|
|
"time.Time": "string:datetime", |
|
|
|
"json.RawMessage": "object:", |
|
|
|
} |
|
|
|
|
|
|
|
var stdlibObject = map[string]string{ |
|
|
|
"&{time Time}": "time.Time", |
|
|
|
"&{time Time}": "time.Time", |
|
|
|
"&{json RawMessage}": "json.RawMessage", |
|
|
|
} |
|
|
|
|
|
|
|
func init() { |
|
|
@ -586,7 +594,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { |
|
|
|
} |
|
|
|
if isArray { |
|
|
|
rs.Schema = &swagger.Schema{ |
|
|
|
Type: "array", |
|
|
|
Type: astTypeArray, |
|
|
|
Items: &schema, |
|
|
|
} |
|
|
|
} else { |
|
|
@ -640,7 +648,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { |
|
|
|
m, mod, realTypes := getModel(p[2]) |
|
|
|
if isArray { |
|
|
|
para.Schema = &swagger.Schema{ |
|
|
|
Type: "array", |
|
|
|
Type: astTypeArray, |
|
|
|
Items: &swagger.Schema{ |
|
|
|
Ref: "#/definitions/" + m, |
|
|
|
}, |
|
|
@ -781,7 +789,7 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s |
|
|
|
isArray = true |
|
|
|
} |
|
|
|
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" || |
|
|
|
typ == "array" || typ == "file" { |
|
|
|
typ == astTypeArray || typ == "file" { |
|
|
|
paraType = typ |
|
|
|
} else if sType, ok := basicTypes[typ]; ok { |
|
|
|
typeFormat := strings.Split(sType, ":") |
|
|
@ -801,14 +809,14 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s |
|
|
|
if isArray { |
|
|
|
if para.In == "body" { |
|
|
|
para.Schema = &swagger.Schema{ |
|
|
|
Type: "array", |
|
|
|
Type: astTypeArray, |
|
|
|
Items: &swagger.Schema{ |
|
|
|
Type: paraType, |
|
|
|
Format: paraFormat, |
|
|
|
}, |
|
|
|
} |
|
|
|
} else { |
|
|
|
para.Type = "array" |
|
|
|
para.Type = astTypeArray |
|
|
|
para.Items = &swagger.ParameterItems{ |
|
|
|
Type: paraType, |
|
|
|
Format: paraFormat, |
|
|
@ -901,49 +909,60 @@ func getparams(str string) []string { |
|
|
|
return r |
|
|
|
} |
|
|
|
|
|
|
|
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) { |
|
|
|
func getModel(str string) (definitionName string, m swagger.Schema, realTypes []string) { |
|
|
|
strs := strings.Split(str, ".") |
|
|
|
objectname = strs[len(strs)-1] |
|
|
|
packageName := "" |
|
|
|
m.Type = "object" |
|
|
|
// strs = [packageName].[objectName]
|
|
|
|
packageName := strs[0] |
|
|
|
objectname := strs[len(strs)-1] |
|
|
|
|
|
|
|
// Default all swagger schemas to object, if no other type is found
|
|
|
|
m.Type = astTypeObject |
|
|
|
|
|
|
|
L: |
|
|
|
for _, pkg := range astPkgs { |
|
|
|
if strs[0] == pkg.Name { |
|
|
|
for _, fl := range pkg.Files { |
|
|
|
for k, d := range fl.Scope.Objects { |
|
|
|
if d.Kind == ast.Typ { |
|
|
|
if k != objectname { |
|
|
|
// Still searching for the right object
|
|
|
|
continue |
|
|
|
} |
|
|
|
packageName = pkg.Name |
|
|
|
parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name) |
|
|
|
parseObject(d, k, &m, &realTypes, astPkgs, packageName) |
|
|
|
|
|
|
|
// When we've found the correct object, we can stop searching
|
|
|
|
break L |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if m.Title == "" { |
|
|
|
beeLogger.Log.Warnf("Cannot find the object: %s", str) |
|
|
|
// Don't log when error has already been logged
|
|
|
|
if _, found := rootapi.Definitions[str]; !found { |
|
|
|
beeLogger.Log.Warnf("Cannot find the object: %s", str) |
|
|
|
} |
|
|
|
m.Title = objectname |
|
|
|
// TODO remove when all type have been supported
|
|
|
|
//os.Exit(1)
|
|
|
|
} |
|
|
|
if len(rootapi.Definitions) == 0 { |
|
|
|
rootapi.Definitions = make(map[string]swagger.Schema) |
|
|
|
} |
|
|
|
objectname = packageName + "." + objectname |
|
|
|
rootapi.Definitions[objectname] = m |
|
|
|
return |
|
|
|
rootapi.Definitions[str] = m |
|
|
|
return str, m, realTypes |
|
|
|
} |
|
|
|
|
|
|
|
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs []*ast.Package, packageName string) { |
|
|
|
ts, ok := d.Decl.(*ast.TypeSpec) |
|
|
|
if !ok { |
|
|
|
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d) |
|
|
|
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v", d) |
|
|
|
} |
|
|
|
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
|
|
|
|
// TODO support other types, such as `MapType`, `InterfaceType` etc...
|
|
|
|
switch t := ts.Type.(type) { |
|
|
|
case *ast.ArrayType: |
|
|
|
m.Title = k |
|
|
|
m.Type = "array" |
|
|
|
m.Type = astTypeArray |
|
|
|
if isBasicType(fmt.Sprint(t.Elt)) { |
|
|
|
typeFormat := strings.Split(basicTypes[fmt.Sprint(t.Elt)], ":") |
|
|
|
m.Format = typeFormat[0] |
|
|
@ -970,8 +989,8 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa |
|
|
|
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject { |
|
|
|
basicType = object |
|
|
|
} |
|
|
|
if k, ok := basicTypes[basicType]; ok { |
|
|
|
typeFormat := strings.Split(k, ":") |
|
|
|
if t, ok := basicTypes[basicType]; ok { |
|
|
|
typeFormat := strings.Split(t, ":") |
|
|
|
m.Type = typeFormat[0] |
|
|
|
m.Format = typeFormat[1] |
|
|
|
} |
|
|
@ -983,7 +1002,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa |
|
|
|
if obj.Kind == ast.Con { |
|
|
|
vs, ok := obj.Decl.(*ast.ValueSpec) |
|
|
|
if !ok { |
|
|
|
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v\n", vs) |
|
|
|
beeLogger.Log.Fatalf("Unknown type without ValueSpec: %v", vs) |
|
|
|
} |
|
|
|
|
|
|
|
ti, ok := vs.Type.(*ast.Ident) |
|
|
@ -1000,7 +1019,7 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa |
|
|
|
for i, val := range vs.Values { |
|
|
|
v, ok := val.(*ast.BasicLit) |
|
|
|
if !ok { |
|
|
|
beeLogger.Log.Warnf("Unknown type without BasicLit: %v\n", v) |
|
|
|
beeLogger.Log.Warnf("Unknown type without BasicLit: %v", v) |
|
|
|
continue |
|
|
|
} |
|
|
|
enums[int(val.Pos())] = fmt.Sprintf("%s = %s", vs.Names[i].Name, v.Value) |
|
|
@ -1008,14 +1027,14 @@ func parseIdent(st *ast.Ident, k string, m *swagger.Schema, astPkgs []*ast.Packa |
|
|
|
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) |
|
|
|
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", 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) |
|
|
|
beeLogger.Log.Warnf("Unknown type with BasicLit to int: %v", v.Value) |
|
|
|
continue |
|
|
|
} |
|
|
|
enumValues[int(val.Pos())] = vv |
|
|
@ -1050,7 +1069,7 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s |
|
|
|
m.Properties = make(map[string]swagger.Propertie) |
|
|
|
for _, field := range st.Fields.List { |
|
|
|
isSlice, realType, sType := typeAnalyser(field) |
|
|
|
if (isSlice && isBasicType(realType)) || sType == "object" { |
|
|
|
if (isSlice && isBasicType(realType)) || sType == astTypeObject { |
|
|
|
if len(strings.Split(realType, " ")) > 1 { |
|
|
|
realType = strings.Replace(realType, " ", ".", -1) |
|
|
|
realType = strings.Replace(realType, "&", "", -1) |
|
|
@ -1064,9 +1083,9 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s |
|
|
|
mp := swagger.Propertie{} |
|
|
|
isObject := false |
|
|
|
if isSlice { |
|
|
|
mp.Type = "array" |
|
|
|
if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok { |
|
|
|
typeFormat := strings.Split(sType, ":") |
|
|
|
mp.Type = astTypeArray |
|
|
|
if t, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok { |
|
|
|
typeFormat := strings.Split(t, ":") |
|
|
|
mp.Items = &swagger.Propertie{ |
|
|
|
Type: typeFormat[0], |
|
|
|
Format: typeFormat[1], |
|
|
@ -1077,14 +1096,14 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
if sType == "object" { |
|
|
|
if sType == astTypeObject { |
|
|
|
isObject = true |
|
|
|
mp.Ref = "#/definitions/" + realType |
|
|
|
} else if isBasicType(realType) { |
|
|
|
typeFormat := strings.Split(sType, ":") |
|
|
|
mp.Type = typeFormat[0] |
|
|
|
mp.Format = typeFormat[1] |
|
|
|
} else if realType == "map" { |
|
|
|
} else if realType == astTypeMap { |
|
|
|
typeFormat := strings.Split(sType, ":") |
|
|
|
mp.AdditionalProperties = &swagger.Propertie{ |
|
|
|
Type: typeFormat[0], |
|
|
@ -1120,7 +1139,6 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s |
|
|
|
} |
|
|
|
|
|
|
|
tag := stag.Get("json") |
|
|
|
|
|
|
|
if tag != "" { |
|
|
|
tagValues = strings.Split(tag, ",") |
|
|
|
} |
|
|
@ -1202,12 +1220,12 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) { |
|
|
|
return true, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)] |
|
|
|
} |
|
|
|
if mp, ok := arr.Elt.(*ast.MapType); ok { |
|
|
|
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), "object" |
|
|
|
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), astTypeObject |
|
|
|
} |
|
|
|
if star, ok := arr.Elt.(*ast.StarExpr); ok { |
|
|
|
return true, fmt.Sprint(star.X), "object" |
|
|
|
return true, fmt.Sprint(star.X), astTypeObject |
|
|
|
} |
|
|
|
return true, fmt.Sprint(arr.Elt), "object" |
|
|
|
return true, fmt.Sprint(arr.Elt), astTypeObject |
|
|
|
} |
|
|
|
switch t := f.Type.(type) { |
|
|
|
case *ast.StarExpr: |
|
|
@ -1218,13 +1236,13 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) { |
|
|
|
if k, ok := basicTypes[basicType]; ok { |
|
|
|
return false, basicType, k |
|
|
|
} |
|
|
|
return false, basicType, "object" |
|
|
|
return false, basicType, astTypeObject |
|
|
|
case *ast.MapType: |
|
|
|
val := fmt.Sprintf("%v", t.Value) |
|
|
|
if isBasicType(val) { |
|
|
|
return false, "map", basicTypes[val] |
|
|
|
return false, astTypeMap, basicTypes[val] |
|
|
|
} |
|
|
|
return false, val, "object" |
|
|
|
return false, val, astTypeObject |
|
|
|
} |
|
|
|
basicType := fmt.Sprint(f.Type) |
|
|
|
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject { |
|
|
@ -1233,7 +1251,7 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) { |
|
|
|
if k, ok := basicTypes[basicType]; ok { |
|
|
|
return false, basicType, k |
|
|
|
} |
|
|
|
return false, basicType, "object" |
|
|
|
return false, basicType, astTypeObject |
|
|
|
} |
|
|
|
|
|
|
|
func isBasicType(Type string) bool { |
|
|
@ -1247,7 +1265,7 @@ func isBasicType(Type string) bool { |
|
|
|
func appendModels(pkgpath, controllerName string, realTypes []string) { |
|
|
|
for _, realType := range realTypes { |
|
|
|
if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) && |
|
|
|
!strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") { |
|
|
|
!strings.HasPrefix(realType, astTypeMap) && !strings.HasPrefix(realType, "&") { |
|
|
|
if _, ok := modelsList[pkgpath+controllerName][realType]; ok { |
|
|
|
continue |
|
|
|
} |
|
|
|