Merge pull request #491 from beego/develop

bee 1.10.0
This commit is contained in:
astaxie 2018-07-22 12:13:01 +08:00 committed by GitHub
commit f728b23527
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 282 additions and 99 deletions

View File

@ -1,8 +1,6 @@
language: go language: go
go: go:
- 1.6.3 - 1.10.3
- 1.7.3
- 1.8
install: install:
- export PATH=$PATH:$HOME/gopath/bin - export PATH=$PATH:$HOME/gopath/bin
- go get -u github.com/opennota/check/cmd/structcheck - go get -u github.com/opennota/check/cmd/structcheck

View File

@ -1,6 +1,7 @@
version: 0 version: 0
go_install: false go_install: false
watch_ext: [] watch_ext: [".go"]
watch_ext_static: [".html", ".tpl", ".js", ".css"]
dir_structure: dir_structure:
watch_all: false watch_all: false
controllers: "" controllers: ""

View File

@ -4,7 +4,8 @@ bee
Bee is a command-line tool facilitating development of Beego-based application. Bee is a command-line tool facilitating development of Beego-based application.
[![Build Status](https://drone.io/github.com/beego/bee/status.png)](https://drone.io/github.com/beego/bee/latest) [![Build Status](https://img.shields.io/travis/beego/bee.svg?branch=master&label=master)](https://travis-ci.org/beego/bee)
[![Build Status](https://img.shields.io/travis/beego/bee.svg?branch=develop&label=develop)](https://travis-ci.org/beego/bee)
## Requirements ## Requirements

View File

@ -1,7 +1,8 @@
{ {
"version": 0, "version": 0,
"go_install": false, "go_install": false,
"watch_ext": [], "watch_ext": [".go"],
"watch_ext_static": [".html", ".tpl", ".js", ".css"],
"dir_structure": { "dir_structure": {
"watch_all": false, "watch_all": false,
"controllers": "", "controllers": "",

View File

@ -166,6 +166,7 @@ func appCode(cmd *commands.Command, args []string, currpath string) {
beeLogger.Log.Infof("Using '%s' as 'Level'", generate.Level) beeLogger.Log.Infof("Using '%s' as 'Level'", generate.Level)
generate.GenerateAppcode(generate.SQLDriver.String(), generate.SQLConn.String(), generate.Level.String(), generate.Tables.String(), currpath) generate.GenerateAppcode(generate.SQLDriver.String(), generate.SQLConn.String(), generate.Level.String(), generate.Tables.String(), currpath)
} }
func migration(cmd *commands.Command, args []string, currpath string) { func migration(cmd *commands.Command, args []string, currpath string) {
if len(args) < 2 { if len(args) < 2 {
beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate") beeLogger.Log.Fatal("Wrong number of arguments. Run: bee help generate")

View File

@ -196,6 +196,17 @@ func checkForSchemaUpdateTable(db *sql.DB, driver string) {
} }
} }
func driverImportStatement(driver string) string {
switch driver {
case "mysql":
return "github.com/go-sql-driver/mysql"
case "postgres":
return "github.com/lib/pq"
default:
return "github.com/go-sql-driver/mysql"
}
}
func showMigrationsTableSQL(driver string) string { func showMigrationsTableSQL(driver string) string {
switch driver { switch driver {
case "mysql": case "mysql":
@ -263,6 +274,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
beeLogger.Log.Fatalf("Could not create file: %s", err) beeLogger.Log.Fatalf("Could not create file: %s", err)
} else { } else {
content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1) content := strings.Replace(MigrationMainTPL, "{{DBDriver}}", driver, -1)
content = strings.Replace(content, "{{DriverRepo}}", driverImportStatement(driver), -1)
content = strings.Replace(content, "{{ConnStr}}", connStr, -1) content = strings.Replace(content, "{{ConnStr}}", connStr, -1)
content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1) content = strings.Replace(content, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
content = strings.Replace(content, "{{LatestName}}", latestName, -1) content = strings.Replace(content, "{{LatestName}}", latestName, -1)
@ -346,8 +358,7 @@ import(
"github.com/astaxie/beego/orm" "github.com/astaxie/beego/orm"
"github.com/astaxie/beego/migration" "github.com/astaxie/beego/migration"
_ "github.com/go-sql-driver/mysql" _ "{{DriverRepo}}"
_ "github.com/lib/pq"
) )
func init(){ func init(){

View File

@ -58,6 +58,8 @@ var (
currentGoPath string currentGoPath string
// Current runmode // Current runmode
runmode string runmode string
// Extra args to run application
runargs string
// Extra directories // Extra directories
extraPackages utils.StrFlags extraPackages utils.StrFlags
) )
@ -71,6 +73,7 @@ func init() {
CmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.") CmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.")
CmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/") CmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/")
CmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.") CmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.")
CmdRun.Flag.StringVar(&runargs, "runargs", "", "Extra args to run application")
CmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.") CmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.")
exit = make(chan bool) exit = make(chan bool)
commands.AvailableCommands = append(commands.AvailableCommands, CmdRun) commands.AvailableCommands = append(commands.AvailableCommands, CmdRun)

View File

@ -36,13 +36,14 @@ var (
state sync.Mutex state sync.Mutex
eventTime = make(map[string]int64) eventTime = make(map[string]int64)
scheduleTime time.Time scheduleTime time.Time
watchExts = []string{".go"} watchExts = config.Conf.WatchExts
watchExtsStatic = []string{".html", ".tpl", ".js", ".css"} watchExtsStatic = config.Conf.WatchExtsStatic
ignoredFilesRegExps = []string{ ignoredFilesRegExps = []string{
`.#(\w+).go`, `.#(\w+).go`,
`.(\w+).go.swp`, `.(\w+).go.swp`,
`(\w+).go~`, `(\w+).go~`,
`(\w+).tmp`, `(\w+).tmp`,
`commentsRouter_controllers.go`,
} }
) )
@ -86,6 +87,12 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
scheduleTime = time.Now().Add(1 * time.Second) scheduleTime = time.Now().Add(1 * time.Second)
time.Sleep(scheduleTime.Sub(time.Now())) time.Sleep(scheduleTime.Sub(time.Now()))
AutoBuild(files, isgenerate) AutoBuild(files, isgenerate)
if config.Conf.EnableReload {
// Wait 100ms more before refreshing the browser
time.Sleep(100 * time.Millisecond)
sendReload(e.String())
}
}() }()
} }
case err := <-watcher.Errors: case err := <-watcher.Errors:
@ -139,9 +146,9 @@ func AutoBuild(files []string, isgenerate bool) {
} }
beeLogger.Log.Success("Docs generated!") beeLogger.Log.Success("Docs generated!")
} }
appName := appname
if err == nil { if err == nil {
appName := appname
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
appName += ".exe" appName += ".exe"
} }
@ -165,7 +172,7 @@ func AutoBuild(files []string, isgenerate bool) {
} }
beeLogger.Log.Success("Built Successfully!") beeLogger.Log.Success("Built Successfully!")
Restart(appname) Restart(appName)
} }
// Kill kills the running command process // Kill kills the running command process
@ -200,7 +207,13 @@ func Start(appname string) {
cmd = exec.Command(appname) cmd = exec.Command(appname)
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Args = append([]string{appname}, config.Conf.CmdArgs...) if runargs != "" {
r := regexp.MustCompile("'.+'|\".+\"|\\S+")
m := r.FindAllString(runargs, -1)
cmd.Args = append([]string{appname}, m...)
} else {
cmd.Args = append([]string{appname}, config.Conf.CmdArgs...)
}
cmd.Env = append(os.Environ(), config.Conf.Envs...) cmd.Env = append(os.Environ(), config.Conf.Envs...)
go cmd.Run() go cmd.Run()

View File

@ -57,7 +57,7 @@ Prints the current Bee, Beego and Go version alongside the platform information.
} }
var outputFormat string var outputFormat string
const version = "1.9.1" const version = "1.10.0"
func init() { func init() {
fs := flag.NewFlagSet("version", flag.ContinueOnError) fs := flag.NewFlagSet("version", flag.ContinueOnError)

View File

@ -27,6 +27,8 @@ const confVer = 0
var Conf = struct { var Conf = struct {
Version int Version int
WatchExts []string `json:"watch_ext" yaml:"watch_ext"`
WatchExtsStatic []string `json:"watch_ext_static" yaml:"watch_ext_static"`
GoInstall bool `json:"go_install" yaml:"go_install"` // Indicates whether execute "go install" before "go build". GoInstall bool `json:"go_install" yaml:"go_install"` // Indicates whether execute "go install" before "go build".
DirStruct dirStruct `json:"dir_structure" yaml:"dir_structure"` DirStruct dirStruct `json:"dir_structure" yaml:"dir_structure"`
CmdArgs []string `json:"cmd_args" yaml:"cmd_args"` CmdArgs []string `json:"cmd_args" yaml:"cmd_args"`
@ -37,7 +39,9 @@ var Conf = struct {
EnableNotification bool `json:"enable_notification" yaml:"enable_notification"` EnableNotification bool `json:"enable_notification" yaml:"enable_notification"`
Scripts map[string]string `json:"scripts" yaml:"scripts"` Scripts map[string]string `json:"scripts" yaml:"scripts"`
}{ }{
GoInstall: true, WatchExts: []string{".go"},
WatchExtsStatic: []string{".html", ".tpl", ".js", ".css"},
GoInstall: true,
DirStruct: dirStruct{ DirStruct: dirStruct{
Others: []string{}, Others: []string{},
}, },

View File

@ -318,7 +318,7 @@ func gen(dbms, connStr string, mode byte, selectedTableNames map[string]bool, ap
mvcPath.RouterPath = path.Join(apppath, "routers") mvcPath.RouterPath = path.Join(apppath, "routers")
createPaths(mode, mvcPath) createPaths(mode, mvcPath)
pkgPath := getPackagePath(apppath) pkgPath := getPackagePath(apppath)
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames) writeSourceFiles(pkgPath, tables, mode, mvcPath)
} else { } else {
beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet.", dbms) beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet.", dbms)
} }
@ -728,32 +728,26 @@ func createPaths(mode byte, paths *MvcPath) {
// writeSourceFiles generates source files for model/controller/router // writeSourceFiles generates source files for model/controller/router
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers // It will wipe the following directories and recreate them:./models, ./controllers, ./routers
// Newly geneated files will be inside these folders. // Newly geneated files will be inside these folders.
func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) { func writeSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath) {
if (OModel & mode) == OModel { if (OModel & mode) == OModel {
beeLogger.Log.Info("Creating model files...") beeLogger.Log.Info("Creating model files...")
writeModelFiles(tables, paths.ModelPath, selectedTables) writeModelFiles(tables, paths.ModelPath)
} }
if (OController & mode) == OController { if (OController & mode) == OController {
beeLogger.Log.Info("Creating controller files...") beeLogger.Log.Info("Creating controller files...")
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath) writeControllerFiles(tables, paths.ControllerPath, pkgPath)
} }
if (ORouter & mode) == ORouter { if (ORouter & mode) == ORouter {
beeLogger.Log.Info("Creating router files...") beeLogger.Log.Info("Creating router files...")
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath) writeRouterFile(tables, paths.RouterPath, pkgPath)
} }
} }
// writeModelFiles generates model files // writeModelFiles generates model files
func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) { func writeModelFiles(tables []*Table, mPath string) {
w := colors.NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
for _, tb := range tables { for _, tb := range tables {
// if selectedTables map is not nil and this table is not selected, ignore it
if selectedTables != nil {
if _, selected := selectedTables[tb.Name]; !selected {
continue
}
}
filename := getFileName(tb.Name) filename := getFileName(tb.Name)
fpath := path.Join(mPath, filename+".go") fpath := path.Join(mPath, filename+".go")
var f *os.File var f *os.File
@ -806,16 +800,10 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
} }
// writeControllerFiles generates controller files // writeControllerFiles generates controller files
func writeControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath string) { func writeControllerFiles(tables []*Table, cPath string, pkgPath string) {
w := colors.NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
for _, tb := range tables { for _, tb := range tables {
// If selectedTables map is not nil and this table is not selected, ignore it
if selectedTables != nil {
if _, selected := selectedTables[tb.Name]; !selected {
continue
}
}
if tb.Pk == "" { if tb.Pk == "" {
continue continue
} }
@ -854,17 +842,11 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
} }
// writeRouterFile generates router file // writeRouterFile generates router file
func writeRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) { func writeRouterFile(tables []*Table, rPath string, pkgPath string) {
w := colors.NewColorWriter(os.Stdout) w := colors.NewColorWriter(os.Stdout)
var nameSpaces []string var nameSpaces []string
for _, tb := range tables { for _, tb := range tables {
// If selectedTables map is not nil and this table is not selected, ignore it
if selectedTables != nil {
if _, selected := selectedTables[tb.Name]; !selected {
continue
}
}
if tb.Pk == "" { if tb.Pk == "" {
continue continue
} }

View File

@ -27,6 +27,7 @@ import (
"reflect" "reflect"
"regexp" "regexp"
"runtime" "runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
@ -77,7 +78,7 @@ var basicTypes = map[string]string{
"byte": "string:byte", "byte": "string:byte",
"rune": "string:byte", "rune": "string:byte",
// builtin golang objects // builtin golang objects
"time.Time": "string:string", "time.Time": "string:datetime",
} }
var stdlibObject = map[string]string{ var stdlibObject = map[string]string{
@ -93,6 +94,7 @@ func init() {
astPkgs = make([]*ast.Package, 0) astPkgs = make([]*ast.Package, 0)
} }
// ParsePackagesFromDir parses packages from a given directory
func ParsePackagesFromDir(dirpath string) { func ParsePackagesFromDir(dirpath string) {
c := make(chan error) c := make(chan error)
@ -105,11 +107,12 @@ func ParsePackagesFromDir(dirpath string) {
return nil return nil
} }
// 7 is length of 'vendor' (6) + length of file path separator (1) // skip folder if it's a 'vendor' folder within dirpath or its child,
// so we skip dir 'vendor' which is directly under dirpath // all 'tests' folders and dot folders wihin dirpath
if !(len(fpath) == len(dirpath)+7 && strings.HasSuffix(fpath, "vendor")) && d, _ := filepath.Rel(dirpath, fpath)
if !(d == "vendor" || strings.HasPrefix(d, "vendor"+string(os.PathSeparator))) &&
!strings.Contains(fpath, "tests") && !strings.Contains(fpath, "tests") &&
!(len(fpath) > len(dirpath) && fpath[len(dirpath)+1] == '.') { !(d[0] == '.') {
err = parsePackageFromDir(fpath) err = parsePackageFromDir(fpath)
if err != nil { if err != nil {
// Send the error to through the channel and continue walking // Send the error to through the channel and continue walking
@ -144,6 +147,7 @@ func parsePackageFromDir(path string) error {
return nil return nil
} }
// GenerateDocs generates documentations for a given path.
func GenerateDocs(curpath string) { func GenerateDocs(curpath string) {
fset := token.NewFileSet() fset := token.NewFileSet()
@ -261,8 +265,9 @@ func GenerateDocs(curpath string) {
case *ast.AssignStmt: case *ast.AssignStmt:
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 // Analyze NewNamespace, it will return version and the subfunction
if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" { selExpr, selOK := v.Fun.(*ast.SelectorExpr)
if !selOK || selExpr.Sel.Name != "NewNamespace" {
continue continue
} }
version, params := analyseNewNamespace(v) version, params := analyseNewNamespace(v)
@ -348,21 +353,21 @@ func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
func analyseNSInclude(baseurl string, ce *ast.CallExpr) string { func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
cname := "" cname := ""
for _, p := range ce.Args { for _, p := range ce.Args {
var x *ast.SelectorExpr var x *ast.SelectorExpr
var p1 interface{} = p var p1 interface{} = p
if ident, ok := p1.(*ast.Ident); ok { if ident, ok := p1.(*ast.Ident); ok {
if assign, ok := ident.Obj.Decl.(*ast.AssignStmt); ok { if assign, ok := ident.Obj.Decl.(*ast.AssignStmt); ok {
if len(assign.Rhs) > 0 { if len(assign.Rhs) > 0 {
p1 = assign.Rhs[0].(*ast.UnaryExpr) p1 = assign.Rhs[0].(*ast.UnaryExpr)
} }
} }
} }
if _, ok := p1.(*ast.UnaryExpr); ok { if _, ok := p1.(*ast.UnaryExpr); ok {
x = p1.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr) x = p1.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
} else { } else {
beeLogger.Log.Warnf("Couldn't determine type\n") beeLogger.Log.Warnf("Couldn't determine type\n")
continue continue
} }
if v, ok := importlist[fmt.Sprint(x.X)]; ok { if v, ok := importlist[fmt.Sprint(x.X)]; ok {
cname = v + x.Sel.Name cname = v + x.Sel.Name
} }
@ -627,10 +632,25 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
pp := strings.Split(p[2], ".") pp := strings.Split(p[2], ".")
typ := pp[len(pp)-1] typ := pp[len(pp)-1]
if len(pp) >= 2 { if len(pp) >= 2 {
m, mod, realTypes := getModel(p[2]) isArray := false
para.Schema = &swagger.Schema{ if p[1] == "body" && strings.HasPrefix(p[2], "[]") {
Ref: "#/definitions/" + m, p[2] = p[2][2:]
isArray = true
} }
m, mod, realTypes := getModel(p[2])
if isArray {
para.Schema = &swagger.Schema{
Type: "array",
Items: &swagger.Schema{
Ref: "#/definitions/" + m,
},
}
} else {
para.Schema = &swagger.Schema{
Ref: "#/definitions/" + m,
}
}
if _, ok := modelsList[pkgpath+controllerName]; !ok { if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema) modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
} }
@ -779,10 +799,20 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
appendModels(pkgpath, controllerName, realTypes) appendModels(pkgpath, controllerName, realTypes)
} }
if isArray { if isArray {
para.Type = "array" if para.In == "body" {
para.Items = &swagger.ParameterItems{ para.Schema = &swagger.Schema{
Type: paraType, Type: "array",
Format: paraFormat, Items: &swagger.Schema{
Type: paraType,
Format: paraFormat,
},
}
} else {
para.Type = "array"
para.Items = &swagger.ParameterItems{
Type: paraType,
Format: paraFormat,
}
} }
} else { } else {
para.Type = paraType para.Type = paraType
@ -910,10 +940,111 @@ 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...
st, ok := ts.Type.(*ast.StructType) switch t := ts.Type.(type) {
if !ok { case *ast.ArrayType:
return 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)
@ -931,9 +1062,10 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
} }
*realTypes = append(*realTypes, realType) *realTypes = append(*realTypes, realType)
mp := swagger.Propertie{} mp := swagger.Propertie{}
isObject := false
if isSlice { if isSlice {
mp.Type = "array" mp.Type = "array"
if isBasicType(strings.Replace(realType, "[]", "", -1)) { if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(sType, ":")
mp.Items = &swagger.Propertie{ mp.Items = &swagger.Propertie{
Type: typeFormat[0], Type: typeFormat[0],
@ -946,6 +1078,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
} }
} else { } else {
if sType == "object" { if sType == "object" {
isObject = true
mp.Ref = "#/definitions/" + realType mp.Ref = "#/definitions/" + realType
} else if isBasicType(realType) { } else if isBasicType(realType) {
typeFormat := strings.Split(sType, ":") typeFormat := strings.Split(sType, ":")
@ -1013,20 +1146,50 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
mp.Description = desc mp.Description = desc
} }
if example := stag.Get("example"); example != "" && !isObject && !isSlice {
mp.Example = str2RealType(example, realType)
}
m.Properties[name] = mp m.Properties[name] = mp
} }
if ignore := stag.Get("ignore"); ignore != "" { if ignore := stag.Get("ignore"); ignore != "" {
continue continue
} }
} else { } else {
for _, pkg := range astPkgs { // only parse case of when embedded field is TypeName
for _, fl := range pkg.Files { // cases of *TypeName and Interface are not handled, maybe useless for swagger spec
for nameOfObj, obj := range fl.Scope.Objects { tag := ""
if obj.Name == fmt.Sprint(field.Type) { if field.Tag != nil {
parseObject(obj, nameOfObj, m, realTypes, astPkgs, pkg.Name) stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
tag = stag.Get("json")
}
if tag != "" {
tagValues := strings.Split(tag, ",")
if tagValues[0] == "-" {
//if json tag is "-", omit
continue
} else {
//if json tag is "something", output: something #definition/pkgname.Type
m.Properties[tagValues[0]] = mp
continue
}
} else {
//if no json tag, expand all fields of the type here
nm := &swagger.Schema{}
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for nameOfObj, obj := range fl.Scope.Objects {
if obj.Name == fmt.Sprint(field.Type) {
parseObject(obj, nameOfObj, nm, realTypes, astPkgs, pkg.Name)
}
} }
} }
} }
for name, p := range nm.Properties {
m.Properties[name] = p
}
continue
} }
} }
} }
@ -1049,6 +1212,9 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
switch t := f.Type.(type) { switch t := f.Type.(type) {
case *ast.StarExpr: case *ast.StarExpr:
basicType := fmt.Sprint(t.X) basicType := fmt.Sprint(t.X)
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
basicType = object
}
if k, ok := basicTypes[basicType]; ok { if k, ok := basicTypes[basicType]; ok {
return false, basicType, k return false, basicType, k
} }
@ -1114,6 +1280,12 @@ func urlReplace(src string) string {
} else if p[0] == '?' && p[1] == ':' { } else if p[0] == '?' && p[1] == ':' {
pt[i] = "{" + p[2:] + "}" pt[i] = "{" + p[2:] + "}"
} }
if pt[i][0] == '{' && strings.Contains(pt[i], ":") {
pt[i] = pt[i][:strings.Index(pt[i], ":")] + "}"
} else if pt[i][0] == '{' && strings.Contains(pt[i], "(") {
pt[i] = pt[i][:strings.Index(pt[i], "(")] + "}"
}
} }
} }
return strings.Join(pt, "/") return strings.Join(pt, "/")
@ -1126,6 +1298,8 @@ func str2RealType(s string, typ string) interface{} {
switch typ { switch typ {
case "int", "int64", "int32", "int16", "int8": case "int", "int64", "int32", "int16", "int8":
ret, err = strconv.Atoi(s) ret, err = strconv.Atoi(s)
case "uint", "uint64", "uint32", "uint16", "uint8":
ret, err = strconv.ParseUint(s, 10, 0)
case "bool": case "bool":
ret, err = strconv.ParseBool(s) ret, err = strconv.ParseBool(s)
case "float64": case "float64":

View File

@ -25,9 +25,15 @@ import (
"github.com/beego/bee/utils" "github.com/beego/bee/utils"
) )
var (
workspace = os.Getenv("BeeWorkspace")
)
func main() { func main() {
currentpath, _ := os.Getwd() currentpath, _ := os.Getwd()
if workspace != "" {
currentpath = workspace
}
flag.Usage = cmd.Usage flag.Usage = cmd.Usage
flag.Parse() flag.Parse()
log.SetFlags(0) log.SetFlags(0)
@ -66,7 +72,6 @@ func main() {
if utils.IsInGOPATH(currentpath) && cmd.IfGenerateDocs(c.Name(), args) { if utils.IsInGOPATH(currentpath) && cmd.IfGenerateDocs(c.Name(), args) {
swaggergen.ParsePackagesFromDir(currentpath) swaggergen.ParsePackagesFromDir(currentpath)
} }
os.Exit(c.Run(c, args)) os.Exit(c.Run(c, args))
return return
} }

View File

@ -1,13 +0,0 @@
Copyright 2014 astaxie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -121,6 +121,8 @@ type Schema struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"`
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"` Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"` Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
} }
// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification // Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
@ -130,7 +132,7 @@ type Propertie struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"` Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"`
Example string `json:"example,omitempty" yaml:"example,omitempty"` Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"` Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`

6
vendor/vendor.json vendored
View File

@ -3,10 +3,10 @@
"ignore": "test", "ignore": "test",
"package": [ "package": [
{ {
"checksumSHA1": "OeOvZ+3A1tRBdMD8GuS6R9zcnqA=", "checksumSHA1": "/ffb74fNK251iTEebGjybfFBAbs=",
"path": "github.com/astaxie/beego/swagger", "path": "github.com/astaxie/beego/swagger",
"revision": "323a1c4214101331a4b71922c23d19b7409ac71f", "revision": "a09bafbf2ab483742b17bb352f95dbc0a597e720",
"revisionTime": "2017-03-06T13:59:04Z" "revisionTime": "2018-07-21T07:55:28Z"
}, },
{ {
"checksumSHA1": "epd3Y7nD7QVzTW0ppwK+q4pKo/4=", "checksumSHA1": "epd3Y7nD7QVzTW0ppwK+q4pKo/4=",