2020-10-17 22:31:19 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"go/ast"
|
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var api API
|
|
|
|
|
2020-11-04 16:14:18 +00:00
|
|
|
//var httptestdir string
|
2020-10-17 22:31:19 +00:00
|
|
|
var tstypemapper map[string]string = make(map[string]string)
|
|
|
|
var knownMethods map[string]bool = make(map[string]bool)
|
|
|
|
var httpMapper map[string]map[string]string = make(map[string]map[string]string)
|
|
|
|
var packageName string = "main"
|
|
|
|
|
|
|
|
func addStruct(a *ast.GenDecl) {
|
|
|
|
md := manageComments(a.Doc)
|
|
|
|
if md["API"] == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
tp := APIType{
|
|
|
|
Name: "",
|
|
|
|
Desc: "",
|
|
|
|
Fields: make(map[string]*APIField),
|
|
|
|
Col: md["COL"],
|
|
|
|
}
|
|
|
|
tp.Name = a.Specs[0].(*ast.TypeSpec).Name.Name
|
|
|
|
log.Printf("Type:" + tp.Name)
|
|
|
|
for _, v := range a.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List {
|
|
|
|
tp.Fields[v.Names[0].Name] = &APIField{}
|
|
|
|
switch x := v.Type.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = x.Name
|
|
|
|
case *ast.ArrayType:
|
|
|
|
switch z := x.Elt.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = z.Name
|
|
|
|
tp.Fields[v.Names[0].Name].Array = true
|
|
|
|
case *ast.InterfaceType:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
|
|
tp.Fields[v.Names[0].Name].Array = true
|
|
|
|
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
|
|
|
tp.Fields[v.Names[0].Name].Array = true
|
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.StarExpr:
|
|
|
|
switch y := x.X.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = y.Name
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
switch z := y.X.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = z.Name + "." + y.Sel.Name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case *ast.InterfaceType:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
|
|
|
|
|
|
case *ast.SelectorExpr:
|
|
|
|
|
|
|
|
switch z := x.X.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = z.Name + "." + x.Sel.Name
|
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.MapType:
|
|
|
|
|
|
|
|
switch z := x.Value.(type) {
|
|
|
|
case *ast.Ident:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = ""
|
|
|
|
tp.Fields[v.Names[0].Name].Mapkey = x.Key.(*ast.Ident).Name
|
|
|
|
tp.Fields[v.Names[0].Name].Mapval = z.Name
|
|
|
|
tp.Fields[v.Names[0].Name].Map = true
|
|
|
|
case *ast.InterfaceType:
|
|
|
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
|
|
tp.Fields[v.Names[0].Name].Array = true
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
log.Printf("%#v", x)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
api.Types[tp.Name] = &tp
|
|
|
|
}
|
|
|
|
|
|
|
|
func addFunction(a *ast.FuncDecl) {
|
|
|
|
md := manageComments(a.Doc)
|
|
|
|
|
|
|
|
if md["API"] == "" {
|
|
|
|
return
|
|
|
|
}
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType := &APIParamType{}
|
|
|
|
resType := &APIParamType{}
|
2020-10-17 22:31:19 +00:00
|
|
|
|
|
|
|
if len(a.Type.Params.List) > 1 {
|
|
|
|
switch x := a.Type.Params.List[1].Type.(type) {
|
|
|
|
case *ast.StarExpr:
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType.Ispointer = true
|
2020-10-17 22:31:19 +00:00
|
|
|
switch y := x.X.(type) {
|
|
|
|
case *ast.Ident:
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType.Typename = y.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
case *ast.SelectorExpr:
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.Ident:
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType.Typename = x.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Type.Results != nil && len(a.Type.Results.List) > 0 {
|
|
|
|
|
|
|
|
switch x := a.Type.Results.List[0].Type.(type) {
|
|
|
|
case *ast.StarExpr:
|
2020-11-04 16:14:18 +00:00
|
|
|
resType.Ispointer = true
|
2020-10-17 22:31:19 +00:00
|
|
|
switch y := x.X.(type) {
|
|
|
|
case *ast.Ident:
|
2020-11-04 16:14:18 +00:00
|
|
|
resType.Typename = y.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
case *ast.SelectorExpr:
|
2020-11-04 16:14:18 +00:00
|
|
|
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.Ident:
|
2020-11-04 16:14:18 +00:00
|
|
|
resType.Typename = x.Name
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if md["RAW"] == "true" {
|
2020-11-04 16:14:18 +00:00
|
|
|
reqType.Typename = md["REQ"]
|
|
|
|
resType.Typename = md["RES"]
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
verb := md["VERB"]
|
|
|
|
if verb == "" {
|
|
|
|
verb = http.MethodPost
|
|
|
|
}
|
|
|
|
fn := APIMethod{
|
|
|
|
Desc: a.Name.Name,
|
|
|
|
Verb: verb,
|
|
|
|
Path: md["PATH"],
|
|
|
|
Perm: md["PERM"],
|
|
|
|
ReqType: reqType,
|
|
|
|
ResType: resType,
|
|
|
|
Raw: md["RAW"] == "true",
|
|
|
|
}
|
|
|
|
api.Methods[a.Name.Name] = &fn
|
|
|
|
}
|
|
|
|
|
2020-11-04 16:14:18 +00:00
|
|
|
//func processHTTPTestOutput(f string, api *API) {
|
|
|
|
//
|
|
|
|
// for k, m := range api.Methods {
|
|
|
|
// fname := path.Join(httptestdir, k+".http")
|
|
|
|
// if dc.FileExists(fname) {
|
|
|
|
// continue
|
|
|
|
// }
|
|
|
|
// b := bytes.Buffer{}
|
|
|
|
// b.WriteString(fmt.Sprintf(`%s{{BASEURL}}%s
|
|
|
|
//Content-Type: application/json
|
|
|
|
//Cookie: dc={{COOKIE}}
|
|
|
|
//
|
|
|
|
//{}
|
|
|
|
//
|
|
|
|
//###
|
|
|
|
//`, m.Verb, api.BasePath+m.Path))
|
|
|
|
//
|
|
|
|
// err := ioutil.WriteFile(fname, b.Bytes(), 0600)
|
|
|
|
// dc.Err(err)
|
|
|
|
// }
|
|
|
|
//}
|
2020-10-17 22:31:19 +00:00
|
|
|
|
|
|
|
func mapHttp(api *API) {
|
|
|
|
for k, v := range api.Methods {
|
|
|
|
pathmap, ok := httpMapper[v.Path]
|
|
|
|
if !ok {
|
|
|
|
httpMapper[v.Path] = make(map[string]string)
|
|
|
|
pathmap = httpMapper[v.Path]
|
|
|
|
}
|
|
|
|
pathmap[v.Verb] = k
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func process(api *API) {
|
|
|
|
mapHttp(api)
|
2020-11-04 16:14:18 +00:00
|
|
|
processGoServerOutput(api)
|
2020-10-17 22:31:19 +00:00
|
|
|
processTSClientOutput("", api)
|
2020-11-06 23:16:52 +00:00
|
|
|
processGoClientOutput(api)
|
2020-10-17 22:31:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func load() {
|
|
|
|
|
|
|
|
api.Types = (make(map[string]*APIType))
|
|
|
|
api.Methods = (make(map[string]*APIMethod))
|
|
|
|
|
|
|
|
fset := token.NewFileSet() // positions are relative to fset
|
|
|
|
|
2020-11-04 16:14:18 +00:00
|
|
|
f, err := parser.ParseDir(fset, config.Goimpldir, nil, parser.ParseComments)
|
2020-10-17 22:31:19 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range f {
|
|
|
|
// Print the AST.
|
|
|
|
ast.Inspect(v, func(n ast.Node) bool {
|
|
|
|
|
|
|
|
switch x := n.(type) {
|
|
|
|
|
|
|
|
case *ast.GenDecl:
|
|
|
|
if x.Tok == token.TYPE {
|
|
|
|
addStruct(x)
|
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
case *ast.File:
|
|
|
|
c := manageCommentsGroups(x.Comments)
|
|
|
|
log.Printf("%+v", c)
|
|
|
|
case *ast.FuncDecl:
|
|
|
|
addFunction(x)
|
|
|
|
case *ast.ValueSpec:
|
|
|
|
if x.Names[0].Name == "BASEPATH" {
|
|
|
|
api.BasePath = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
|
|
|
}
|
|
|
|
if x.Names[0].Name == "NAMESPACE" {
|
|
|
|
api.Namespace = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
|
|
|
}
|
|
|
|
log.Printf("%#v", x)
|
|
|
|
case *ast.Package:
|
|
|
|
packageName = x.Name
|
|
|
|
default:
|
|
|
|
//log.Printf("%#v", x)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
|
2020-11-04 16:14:18 +00:00
|
|
|
loadConfig()
|
|
|
|
|
2020-10-17 22:31:19 +00:00
|
|
|
tstypemapper["time.Time"] = "Date"
|
|
|
|
tstypemapper["primitive.ObjectID"] = "string"
|
|
|
|
tstypemapper["time.Duration"] = "Date"
|
|
|
|
tstypemapper["int"] = "number"
|
|
|
|
tstypemapper["int32"] = "number"
|
|
|
|
tstypemapper["int64"] = "number"
|
|
|
|
tstypemapper["float"] = "number"
|
|
|
|
tstypemapper["float64"] = "number"
|
|
|
|
tstypemapper["uint8"] = "number"
|
|
|
|
tstypemapper["uint16"] = "number"
|
|
|
|
tstypemapper["uint32"] = "number"
|
|
|
|
tstypemapper["error"] = "Error"
|
|
|
|
tstypemapper["bool"] = "boolean"
|
|
|
|
tstypemapper["interface{}"] = "any"
|
|
|
|
tstypemapper["bson.M"] = "any"
|
|
|
|
|
2020-11-04 16:14:18 +00:00
|
|
|
os.Remove(config.Gofname)
|
2020-10-17 22:31:19 +00:00
|
|
|
load()
|
|
|
|
process(&api)
|
|
|
|
}
|