1
0
mirror of https://github.com/beego/bee.git synced 2024-12-25 04:30:50 +00:00
# Conflicts:
#	generate/swaggergen/g_docs.go
This commit is contained in:
王迎宾 2018-07-25 18:25:45 +08:00
commit e56f6a4fe7
16 changed files with 213 additions and 112 deletions

View File

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

View File

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

View File

@ -4,7 +4,8 @@ bee
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

View File

@ -1,7 +1,8 @@
{
"version": 0,
"go_install": false,
"watch_ext": [],
"watch_ext": [".go"],
"watch_ext_static": [".html", ".tpl", ".js", ".css"],
"dir_structure": {
"watch_all": false,
"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)
generate.GenerateAppcode(generate.SQLDriver.String(), generate.SQLConn.String(), generate.Level.String(), generate.Tables.String(), currpath)
}
func migration(cmd *commands.Command, args []string, currpath string) {
if len(args) < 2 {
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 {
switch driver {
case "mysql":
@ -263,6 +274,7 @@ func writeMigrationSourceFile(dir, source, driver, connStr string, latestTime in
beeLogger.Log.Fatalf("Could not create file: %s", err)
} else {
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, "{{LatestTime}}", strconv.FormatInt(latestTime, 10), -1)
content = strings.Replace(content, "{{LatestName}}", latestName, -1)
@ -346,8 +358,7 @@ import(
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/migration"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
_ "{{DriverRepo}}"
)
func init(){

View File

@ -58,6 +58,8 @@ var (
currentGoPath string
// Current runmode
runmode string
// Extra args to run application
runargs string
// Extra directories
extraPackages utils.StrFlags
)
@ -71,6 +73,7 @@ func init() {
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(&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.")
exit = make(chan bool)
commands.AvailableCommands = append(commands.AvailableCommands, CmdRun)

View File

@ -36,13 +36,14 @@ var (
state sync.Mutex
eventTime = make(map[string]int64)
scheduleTime time.Time
watchExts = []string{".go"}
watchExtsStatic = []string{".html", ".tpl", ".js", ".css"}
watchExts = config.Conf.WatchExts
watchExtsStatic = config.Conf.WatchExtsStatic
ignoredFilesRegExps = []string{
`.#(\w+).go`,
`.(\w+).go.swp`,
`(\w+).go~`,
`(\w+).tmp`,
`commentsRouter_controllers.go`,
}
)
@ -86,6 +87,12 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
scheduleTime = time.Now().Add(1 * time.Second)
time.Sleep(scheduleTime.Sub(time.Now()))
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:
@ -141,9 +148,9 @@ func AutoBuild(files []string, isgenerate bool) {
}
beeLogger.Log.Success("Docs generated!")
}
if err == nil {
appName := appname
if err == nil {
if runtime.GOOS == "windows" {
appName += ".exe"
}
@ -167,7 +174,7 @@ func AutoBuild(files []string, isgenerate bool) {
}
beeLogger.Log.Success("Built Successfully!")
Restart(appname)
Restart(appName)
}
// Kill kills the running command process
@ -202,7 +209,13 @@ func Start(appname string) {
cmd = exec.Command(appname)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
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...)
go cmd.Run()

View File

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

View File

@ -27,6 +27,8 @@ const confVer = 0
var Conf = struct {
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".
DirStruct dirStruct `json:"dir_structure" yaml:"dir_structure"`
CmdArgs []string `json:"cmd_args" yaml:"cmd_args"`
@ -37,6 +39,8 @@ var Conf = struct {
EnableNotification bool `json:"enable_notification" yaml:"enable_notification"`
Scripts map[string]string `json:"scripts" yaml:"scripts"`
}{
WatchExts: []string{".go"},
WatchExtsStatic: []string{".html", ".tpl", ".js", ".css"},
GoInstall: true,
DirStruct: dirStruct{
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")
createPaths(mode, mvcPath)
pkgPath := getPackagePath(apppath)
writeSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
writeSourceFiles(pkgPath, tables, mode, mvcPath)
} else {
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
// It will wipe the following directories and recreate them:./models, ./controllers, ./routers
// 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 {
beeLogger.Log.Info("Creating model files...")
writeModelFiles(tables, paths.ModelPath, selectedTables)
writeModelFiles(tables, paths.ModelPath)
}
if (OController & mode) == OController {
beeLogger.Log.Info("Creating controller files...")
writeControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
writeControllerFiles(tables, paths.ControllerPath, pkgPath)
}
if (ORouter & mode) == ORouter {
beeLogger.Log.Info("Creating router files...")
writeRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
writeRouterFile(tables, paths.RouterPath, pkgPath)
}
}
// 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)
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)
fpath := path.Join(mPath, filename+".go")
var f *os.File
@ -806,16 +800,10 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
}
// 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)
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 == "" {
continue
}
@ -854,17 +842,11 @@ func writeControllerFiles(tables []*Table, cPath string, selectedTables map[stri
}
// 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)
var nameSpaces []string
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 == "" {
continue
}

View File

@ -37,6 +37,7 @@ import (
"github.com/astaxie/beego/utils"
"github.com/beego/bee/logger"
bu "github.com/beego/bee/utils"
"sort"
)
const (
@ -262,18 +263,9 @@ func GenerateDocs(curpath string) {
for _, l := range stmt.Rhs {
if v, ok := l.(*ast.CallExpr); ok {
// Analyse NewNamespace, it will return version and the subfunction
//if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
// continue
//}
if sel, ok := v.Fun.(*ast.SelectorExpr); ok {
if sel.Sel.String() != "NewNamespace" {
if selName := v.Fun.(*ast.SelectorExpr).Sel.String(); selName != "NewNamespace" {
continue
}
} else if ident, ok := v.Fun.(*ast.Ident); ok {
if ident.String() != "NewNamespace" {
continue
}
}
version, params := analyseNewNamespace(v)
if rootapi.BasePath == "" && version != "" {
rootapi.BasePath = version
@ -443,20 +435,15 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) {
}
fileSet := token.NewFileSet()
astPkg, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
astPkgs, err := parser.ParseDir(fileSet, pkgRealpath, func(info os.FileInfo) bool {
name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments)
if err != nil {
beeLogger.Log.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err)
}
for _, pkg := range astPkg {
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for _, v := range fl.Imports {
for _, v1 := range gopaths {
parsePackageFromDir(filepath.Join(v1+"/src/", strings.Trim(v.Path.Value, "\"")))
}
}
for _, d := range fl.Decls {
switch specDecl := d.(type) {
case *ast.FuncDecl:
@ -572,13 +559,6 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
schema.Format = typeFormat[1]
} else {
m, mod, realTypes := getModel(schemaName)
if mod.Type != "object" {
if sType, ok := basicTypes[mod.Type]; ok {
typeFormat := strings.Split(sType, ":")
schema.Type = typeFormat[0]
schema.Format = typeFormat[1]
}
} else {
schema.Ref = "#/definitions/" + m
if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
@ -586,7 +566,6 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
modelsList[pkgpath+controllerName][schemaName] = mod
appendModels(pkgpath, controllerName, realTypes)
}
}
if isArray {
rs.Schema = &swagger.Schema{
Type: "array",
@ -636,7 +615,6 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
typ := pp[len(pp)-1]
if len(pp) >= 2 {
m, mod, realTypes := getModel(p[2])
//TODO:这里可能还要对解析的参数进行处理
para.Schema = &swagger.Schema{
Ref: "#/definitions/" + m,
}
@ -797,11 +775,21 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
}
}
if isArray {
if para.In == "body" {
para.Schema = &swagger.Schema{
Type: "array",
Items: &swagger.Schema{
Type: paraType,
Format: paraFormat,
},
}
} else {
para.Type = "array"
para.Items = &swagger.ParameterItems{
Type: paraType,
Format: paraFormat,
}
}
} else {
para.Type = paraType
para.Format = paraFormat
@ -931,12 +919,117 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
beeLogger.Log.Fatalf("Unknown type without TypeSec: %v\n", d)
}
// TODO support other types, such as `ArrayType`, `MapType`, `InterfaceType` etc...
st, ok := ts.Type.(*ast.StructType)
_, ok = ts.Type.(*ast.StructType)
if !ok {
m.Title = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
m.Type = fmt.Sprintf("%v", reflect.ValueOf(ts.Type))
return
switch t := ts.Type.(type) {
case *ast.ArrayType:
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
if st.Fields.List != nil {
m.Properties = make(map[string]swagger.Propertie)
@ -956,7 +1049,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
mp := swagger.Propertie{}
if isSlice {
mp.Type = "array"
if isBasicType(strings.Replace(realType, "[]", "", -1)) {
if sType, ok := basicTypes[(strings.Replace(realType, "[]", "", -1))]; ok {
typeFormat := strings.Split(sType, ":")
mp.Items = &swagger.Propertie{
Type: typeFormat[0],
@ -1046,19 +1139,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
for _, fl := range pkg.Files {
for nameOfObj, obj := range fl.Scope.Objects {
if obj.Name == fmt.Sprint(field.Type) {
nameOfObj = fmt.Sprintf("%s.%s",pkg.Name,nameOfObj)
//m.Properties[obj.Name] = swagger.Propertie{
// Ref: "#/definitions/" + nameOfObj,
//}
m1,ok := rootapi.Definitions[nameOfObj]
if !ok {
_,m1,_ = getModel(nameOfObj)
}
if len(m1.Properties) > 0 {
for k,v := range m1.Properties {
m.Properties[k]=v
}
}
parseObject(obj, nameOfObj, m, realTypes, astPkgs, pkg.Name)
}
}
}
@ -1084,6 +1165,9 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
switch t := f.Type.(type) {
case *ast.StarExpr:
basicType := fmt.Sprint(t.X)
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
basicType = object
}
if k, ok := basicTypes[basicType]; ok {
return false, basicType, k
}
@ -1160,6 +1244,12 @@ func urlReplace(src string) string {
} else if p[0] == '?' && p[1] == ':' {
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, "/")
@ -1172,6 +1262,8 @@ func str2RealType(s string, typ string) interface{} {
switch typ {
case "int", "int64", "int32", "int16", "int8":
ret, err = strconv.Atoi(s)
case "uint", "uint64", "uint32", "uint16", "uint8":
ret, err = strconv.ParseUint(s, 10, 0)
case "bool":
ret, err = strconv.ParseBool(s)
case "float64":

View File

@ -25,9 +25,15 @@ import (
"github.com/beego/bee/utils"
)
var (
workspace = os.Getenv("BeeWorkspace")
)
func main() {
currentpath, _ := os.Getwd()
if workspace != "" {
currentpath = workspace
}
flag.Usage = cmd.Usage
flag.Parse()
log.SetFlags(0)
@ -66,7 +72,6 @@ func main() {
if utils.IsInGOPATH(currentpath) && cmd.IfGenerateDocs(c.Name(), args) {
swaggergen.ParsePackagesFromDir(currentpath)
}
os.Exit(c.Run(c, args))
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"`
Items *Schema `json:"items,omitempty" yaml:"items,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
@ -130,7 +132,7 @@ type Propertie struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,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"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`

6
vendor/vendor.json vendored
View File

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