From 6689e25d6f7c0892abcbd0b1d2d056a4ba2df59f Mon Sep 17 00:00:00 2001 From: Franz Wilhelm von der Lippe Date: Sat, 6 Oct 2018 13:35:26 +0200 Subject: [PATCH 1/2] Add support for apps outside of gopath with go modules, fixes #549 --- cmd/commands/run/run.go | 49 +++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/cmd/commands/run/run.go b/cmd/commands/run/run.go index 17f73db..bcd6251 100644 --- a/cmd/commands/run/run.go +++ b/cmd/commands/run/run.go @@ -79,37 +79,44 @@ func init() { commands.AvailableCommands = append(commands.AvailableCommands, CmdRun) } +// RunApp locates files to watch, and starts the beego application func RunApp(cmd *commands.Command, args []string) int { - if len(args) == 0 || args[0] == "watchall" { - currpath, _ = os.Getwd() - if found, _gopath, _ := utils.SearchGOPATHs(currpath); found { - appname = path.Base(currpath) - currentGoPath = _gopath - } else { - beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", currpath) - } - } else { - // Check if passed Bee application path/name exists in the GOPATH(s) - if found, _gopath, _path := utils.SearchGOPATHs(args[0]); found { - currpath = _path - currentGoPath = _gopath - appname = path.Base(currpath) - } else { - beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", args[0]) - } + // The default app path is the current working directory + appPath, _ := os.Getwd() - if strings.HasSuffix(appname, ".go") && utils.IsExist(currpath) { + // If an argument is presented, we use it as the app path + if len(args) != 0 && args[0] != "watchall" { + if path.IsAbs(args[0]) { + appPath = args[0] + } else { + appPath = path.Join(appPath, args[0]) + } + } + + if utils.IsInGOPATH(appPath) { + if found, _gopath, _path := utils.SearchGOPATHs(appPath); found { + appPath = _path + appname = path.Base(appPath) + currentGoPath = _gopath + } else { + beeLogger.Log.Fatalf("No application '%s' found in your GOPATH", appPath) + } + if strings.HasSuffix(appname, ".go") && utils.IsExist(appPath) { beeLogger.Log.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname) beeLogger.Log.Info("Do you want to overwrite it? [yes|no] ") if !utils.AskForConfirmation() { return 0 } } + } else { + beeLogger.Log.Warn("Running application outside of GOPATH") + appname = path.Base(appPath) + currentGoPath = appPath } beeLogger.Log.Infof("Using '%s' as 'appname'", appname) - beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), currpath) + beeLogger.Log.Debugf("Current path: %s", utils.FILE(), utils.LINE(), appPath) if runmode == "prod" || runmode == "dev" { os.Setenv("BEEGO_RUNMODE", runmode) @@ -122,7 +129,7 @@ func RunApp(cmd *commands.Command, args []string) int { } var paths []string - readAppDirectories(currpath, &paths) + readAppDirectories(appPath, &paths) // Because monitor files has some issues, we watch current directory // and ignore non-go files. @@ -159,7 +166,7 @@ func RunApp(cmd *commands.Command, args []string) int { } } if downdoc == "true" { - if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil { + if _, err := os.Stat(path.Join(appPath, "swagger", "index.html")); err != nil { if os.IsNotExist(err) { downloadFromURL(swaggerlink, "swagger.zip") unzipAndDelete("swagger.zip") From d63a5eb53de87a3400f6618de4c6d65da82cc33b Mon Sep 17 00:00:00 2001 From: Franz von der Lippe Date: Sat, 6 Oct 2018 22:32:51 +0200 Subject: [PATCH 2/2] Fix badly created definition names --- generate/swaggergen/g_docs.go | 102 ++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 42 deletions(-) diff --git a/generate/swaggergen/g_docs.go b/generate/swaggergen/g_docs.go index 351a289..71c13bd 100644 --- a/generate/swaggergen/g_docs.go +++ b/generate/swaggergen/g_docs.go @@ -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 }