From a117a48d7ba61eac3da4eca440e8cf08c65c43ab Mon Sep 17 00:00:00 2001 From: eyalpost Date: Wed, 26 Apr 2017 00:10:03 +0300 Subject: [PATCH] add support for controller methods with paramaters - allow automatic mapping of type from method params - generate swagger for parameters not covered by comment annotations --- generate/swaggergen/g_docs.go | 158 ++++++++++++++++++++++++++-------- 1 file changed, 124 insertions(+), 34 deletions(-) diff --git a/generate/swaggergen/g_docs.go b/generate/swaggergen/g_docs.go index a9f1c0a..3e8cca0 100644 --- a/generate/swaggergen/g_docs.go +++ b/generate/swaggergen/g_docs.go @@ -362,11 +362,11 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) { pkgRealpath = wg } else { wgopath := gopaths - for _, wg := range wgopath { - wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", pkgpath)) - if utils.FileExists(wg) { - pkgRealpath = wg - break + for _, wg := range wgopath { + wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", pkgpath)) + if utils.FileExists(wg) { + pkgRealpath = wg + break } } } @@ -395,7 +395,7 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) { if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 { if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok { // Parse controller method - parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath) + parserComments(specDecl, fmt.Sprint(t.X), pkgpath) } } case *ast.GenDecl: @@ -448,12 +448,16 @@ func peekNextSplitString(ss string) (s string, spacePos int) { } // parse the func comments -func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error { +func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error { var routerPath string var HTTPMethod string opts := swagger.Operation{ Responses: make(map[string]swagger.Response), } + funcName := f.Name.String() + comments := f.Doc + funcParamMap := buildParamMap(f.Type.Params) + //TODO: resultMap := buildParamMap(f.Type.Results) if comments != nil && comments.List != nil { for _, c := range comments.List { t := strings.TrimSpace(strings.TrimLeft(c.Text, "//")) @@ -526,7 +530,17 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat if len(p) < 4 { beeLogger.Log.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params") } - para.Name = p[0] + paramNames := strings.SplitN(p[0], "=>", 2) + para.Name = paramNames[0] + funcParamName := para.Name + if len(paramNames) > 1 { + funcParamName = paramNames[1] + } + paramType, ok := funcParamMap[funcParamName] + if ok { + delete(funcParamMap, funcParamName) + } + switch p[1] { case "query": fallthrough @@ -555,33 +569,10 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat modelsList[pkgpath+controllerName][typ] = mod appendModels(pkgpath, controllerName, realTypes) } else { - isArray := false - paraType := "" - paraFormat := "" - if strings.HasPrefix(typ, "[]") { - typ = typ[2:] - isArray = true - } - if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" || - typ == "array" || typ == "file" { - paraType = typ - } else if sType, ok := basicTypes[typ]; ok { - typeFormat := strings.Split(sType, ":") - paraType = typeFormat[0] - paraFormat = typeFormat[1] - } else { - beeLogger.Log.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ) - } - if isArray { - para.Type = "array" - para.Items = &swagger.ParameterItems{ - Type: paraType, - Format: paraFormat, - } - } else { - para.Type = paraType - para.Format = paraFormat + if typ == "auto" { + typ = paramType } + setParamType(¶, typ, pkgpath, controllerName) } switch len(p) { case 5: @@ -636,7 +627,21 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat } } } + if routerPath != "" { + //Go over function parameters which were not mapped and create swagger params for them + for name, typ := range funcParamMap { + para := swagger.Parameter{} + para.Name = name + setParamType(¶, typ, pkgpath, controllerName) + if paramInPath(name, routerPath) { + para.In = "path" + } else { + para.In = "query" + } + opts.Parameters = append(opts.Parameters, para) + } + var item *swagger.Item if itemList, ok := controllerList[pkgpath+controllerName]; ok { if it, ok := itemList[routerPath]; !ok { @@ -669,6 +674,91 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat return nil } +func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName string) { + isArray := false + paraType := "" + paraFormat := "" + + if strings.HasPrefix(typ, "[]") { + typ = typ[2:] + isArray = true + } + if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" || + typ == "array" || typ == "file" { + paraType = typ + } else if sType, ok := basicTypes[typ]; ok { + typeFormat := strings.Split(sType, ":") + paraType = typeFormat[0] + paraFormat = typeFormat[1] + } else { + m, mod, realTypes := getModel(typ) + para.Schema = &swagger.Schema{ + Ref: "#/definitions/" + m, + } + if _, ok := modelsList[pkgpath+controllerName]; !ok { + modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema) + } + modelsList[pkgpath+controllerName][typ] = mod + appendModels(pkgpath, controllerName, realTypes) + } + if isArray { + para.Type = "array" + para.Items = &swagger.ParameterItems{ + Type: paraType, + Format: paraFormat, + } + } else { + para.Type = paraType + para.Format = paraFormat + } + +} + +func paramInPath(name, route string) bool { + return strings.HasSuffix(route, ":"+name) || + strings.Contains(route, ":"+name+"/") +} + +func getFunctionParamType(t ast.Expr) string { + switch paramType := t.(type) { + case *ast.Ident: + return paramType.Name + // case *ast.Ellipsis: + // result := getFunctionParamType(paramType.Elt) + // result.array = true + // return result + case *ast.ArrayType: + return "[]" + getFunctionParamType(paramType.Elt) + case *ast.StarExpr: + return getFunctionParamType(paramType.X) + case *ast.SelectorExpr: + return getFunctionParamType(paramType.X) + "." + paramType.Sel.Name + default: + return "" + + } +} + +func buildParamMap(list *ast.FieldList) map[string]string { + i := 0 + result := map[string]string{} + if list != nil { + funcParams := list.List + for _, fparam := range funcParams { + param := getFunctionParamType(fparam.Type) + var paramName string + if len(fparam.Names) > 0 { + paramName = fparam.Names[0].Name + } else { + paramName = fmt.Sprint(i) + i++ + } + result[paramName] = param + } + } + return result +} + // analisys params return []string // @Param query form string true "The email for login" // [query form string true "The email for login"]