1
0
mirror of https://github.com/beego/bee.git synced 2025-06-11 08:20:39 +00:00

Refactor!

create sub packages
delete unused code
delete code from not use command cmdRouter,cmdTest, cmdRundocs
make command plugins
check with gosimple,staticcheck,go vet,unused,unconvert
This commit is contained in:
Sergey Lanzman
2017-03-07 01:58:53 +02:00
parent 74baba4f63
commit c538bfbc8f
47 changed files with 1838 additions and 2467 deletions

23
generate/g.go Normal file
View File

@ -0,0 +1,23 @@
// Copyright 2013 bee authors
//
// 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.
package generate
import "github.com/beego/bee/utils"
var SQLDriver utils.DocValue
var SQLConn utils.DocValue
var Level utils.DocValue
var Tables utils.DocValue
var Fields utils.DocValue

1344
generate/g_appcode.go Normal file

File diff suppressed because it is too large Load Diff

325
generate/g_controllers.go Normal file
View File

@ -0,0 +1,325 @@
// Copyright 2013 bee authors
//
// 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.
package generate
import (
"fmt"
"os"
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
)
func GenerateController(cname, currpath string) {
w := colors.NewColorWriter(os.Stdout)
p, f := path.Split(cname)
controllerName := strings.Title(f)
packageName := "controllers"
if p != "" {
i := strings.LastIndex(p[:len(p)-1], "/")
packageName = p[i+1 : len(p)-1]
}
beeLogger.Log.Infof("Using '%s' as controller name", controllerName)
beeLogger.Log.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "controllers", p)
if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the controller's directory
if err := os.MkdirAll(fp, 0777); err != nil {
beeLogger.Log.Fatalf("Could not create controllers directory: %s", err)
}
}
fpath := path.Join(fp, strings.ToLower(controllerName)+".go")
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
modelPath := path.Join(currpath, "models", strings.ToLower(controllerName)+".go")
var content string
if _, err := os.Stat(modelPath); err == nil {
beeLogger.Log.Infof("Using matching model '%s'", controllerName)
content = strings.Replace(controllerModelTpl, "{{packageName}}", packageName, -1)
pkgPath := getPackagePath(currpath)
content = strings.Replace(content, "{{pkgPath}}", pkgPath, -1)
} else {
content = strings.Replace(controllerTpl, "{{packageName}}", packageName, -1)
}
content = strings.Replace(content, "{{controllerName}}", controllerName, -1)
f.WriteString(content)
// Run 'gofmt' on the generated source code
utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create controller file: %s", err)
}
}
var controllerTpl = `package {{packageName}}
import (
"github.com/astaxie/beego"
)
// {{controllerName}}Controller operations for {{controllerName}}
type {{controllerName}}Controller struct {
beego.Controller
}
// URLMapping ...
func (c *{{controllerName}}Controller) URLMapping() {
c.Mapping("Post", c.Post)
c.Mapping("GetOne", c.GetOne)
c.Mapping("GetAll", c.GetAll)
c.Mapping("Put", c.Put)
c.Mapping("Delete", c.Delete)
}
// Post ...
// @Title Create
// @Description create {{controllerName}}
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
// @Success 201 {object} models.{{controllerName}}
// @Failure 403 body is empty
// @router / [post]
func (c *{{controllerName}}Controller) Post() {
}
// GetOne ...
// @Title GetOne
// @Description get {{controllerName}} by id
// @Param id path string true "The key for staticblock"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403 :id is empty
// @router /:id [get]
func (c *{{controllerName}}Controller) GetOne() {
}
// GetAll ...
// @Title GetAll
// @Description get {{controllerName}}
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
// @Param fields query string false "Fields returned. e.g. col1,col2 ..."
// @Param sortby query string false "Sorted-by fields. e.g. col1,col2 ..."
// @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..."
// @Param limit query string false "Limit the size of result set. Must be an integer"
// @Param offset query string false "Start position of result set. Must be an integer"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403
// @router / [get]
func (c *{{controllerName}}Controller) GetAll() {
}
// Put ...
// @Title Put
// @Description update the {{controllerName}}
// @Param id path string true "The id you want to update"
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403 :id is not int
// @router /:id [put]
func (c *{{controllerName}}Controller) Put() {
}
// Delete ...
// @Title Delete
// @Description delete the {{controllerName}}
// @Param id path string true "The id you want to delete"
// @Success 200 {string} delete success!
// @Failure 403 id is empty
// @router /:id [delete]
func (c *{{controllerName}}Controller) Delete() {
}
`
var controllerModelTpl = `package {{packageName}}
import (
"{{pkgPath}}/models"
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/astaxie/beego"
)
// {{controllerName}}Controller operations for {{controllerName}}
type {{controllerName}}Controller struct {
beego.Controller
}
// URLMapping ...
func (c *{{controllerName}}Controller) URLMapping() {
c.Mapping("Post", c.Post)
c.Mapping("GetOne", c.GetOne)
c.Mapping("GetAll", c.GetAll)
c.Mapping("Put", c.Put)
c.Mapping("Delete", c.Delete)
}
// Post ...
// @Title Post
// @Description create {{controllerName}}
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
// @Success 201 {int} models.{{controllerName}}
// @Failure 403 body is empty
// @router / [post]
func (c *{{controllerName}}Controller) Post() {
var v models.{{controllerName}}
json.Unmarshal(c.Ctx.Input.RequestBody, &v)
if _, err := models.Add{{controllerName}}(&v); err == nil {
c.Ctx.Output.SetStatus(201)
c.Data["json"] = v
} else {
c.Data["json"] = err.Error()
}
c.ServeJSON()
}
// GetOne ...
// @Title Get One
// @Description get {{controllerName}} by id
// @Param id path string true "The key for staticblock"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403 :id is empty
// @router /:id [get]
func (c *{{controllerName}}Controller) GetOne() {
idStr := c.Ctx.Input.Param(":id")
id, _ := strconv.ParseInt(idStr, 0, 64)
v, err := models.Get{{controllerName}}ById(id)
if err != nil {
c.Data["json"] = err.Error()
} else {
c.Data["json"] = v
}
c.ServeJSON()
}
// GetAll ...
// @Title Get All
// @Description get {{controllerName}}
// @Param query query string false "Filter. e.g. col1:v1,col2:v2 ..."
// @Param fields query string false "Fields returned. e.g. col1,col2 ..."
// @Param sortby query string false "Sorted-by fields. e.g. col1,col2 ..."
// @Param order query string false "Order corresponding to each sortby field, if single value, apply to all sortby fields. e.g. desc,asc ..."
// @Param limit query string false "Limit the size of result set. Must be an integer"
// @Param offset query string false "Start position of result set. Must be an integer"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403
// @router / [get]
func (c *{{controllerName}}Controller) GetAll() {
var fields []string
var sortby []string
var order []string
var query = make(map[string]string)
var limit int64 = 10
var offset int64
// fields: col1,col2,entity.col3
if v := c.GetString("fields"); v != "" {
fields = strings.Split(v, ",")
}
// limit: 10 (default is 10)
if v, err := c.GetInt64("limit"); err == nil {
limit = v
}
// offset: 0 (default is 0)
if v, err := c.GetInt64("offset"); err == nil {
offset = v
}
// sortby: col1,col2
if v := c.GetString("sortby"); v != "" {
sortby = strings.Split(v, ",")
}
// order: desc,asc
if v := c.GetString("order"); v != "" {
order = strings.Split(v, ",")
}
// query: k:v,k:v
if v := c.GetString("query"); v != "" {
for _, cond := range strings.Split(v, ",") {
kv := strings.SplitN(cond, ":", 2)
if len(kv) != 2 {
c.Data["json"] = errors.New("Error: invalid query key/value pair")
c.ServeJSON()
return
}
k, v := kv[0], kv[1]
query[k] = v
}
}
l, err := models.GetAll{{controllerName}}(query, fields, sortby, order, offset, limit)
if err != nil {
c.Data["json"] = err.Error()
} else {
c.Data["json"] = l
}
c.ServeJSON()
}
// Put ...
// @Title Put
// @Description update the {{controllerName}}
// @Param id path string true "The id you want to update"
// @Param body body models.{{controllerName}} true "body for {{controllerName}} content"
// @Success 200 {object} models.{{controllerName}}
// @Failure 403 :id is not int
// @router /:id [put]
func (c *{{controllerName}}Controller) Put() {
idStr := c.Ctx.Input.Param(":id")
id, _ := strconv.ParseInt(idStr, 0, 64)
v := models.{{controllerName}}{Id: id}
json.Unmarshal(c.Ctx.Input.RequestBody, &v)
if err := models.Update{{controllerName}}ById(&v); err == nil {
c.Data["json"] = "OK"
} else {
c.Data["json"] = err.Error()
}
c.ServeJSON()
}
// Delete ...
// @Title Delete
// @Description delete the {{controllerName}}
// @Param id path string true "The id you want to delete"
// @Success 200 {string} delete success!
// @Failure 403 id is empty
// @router /:id [delete]
func (c *{{controllerName}}Controller) Delete() {
idStr := c.Ctx.Input.Param(":id")
id, _ := strconv.ParseInt(idStr, 0, 64)
if err := models.Delete{{controllerName}}(id); err == nil {
c.Data["json"] = "OK"
} else {
c.Data["json"] = err.Error()
}
c.ServeJSON()
}
`

554
generate/g_hproseappcode.go Normal file
View File

@ -0,0 +1,554 @@
/**********************************************************\
| |
| hprose |
| |
| Official WebSite: http://www.hprose.com/ |
| http://www.hprose.org/ |
| |
\**********************************************************/
/**********************************************************\
* *
* Build rpc application use Hprose base on beego *
* *
* LastModified: Oct 23, 2014 *
* Author: Liu jian <laoliu@lanmv.com> *
* *
\**********************************************************/
package generate
import (
"database/sql"
"fmt"
"os"
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
var Hproseconf = `appname = {{.Appname}}
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
`
var HproseMaingo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
)
func logInvokeHandler(
name string,
args []reflect.Value,
context rpc.Context,
next rpc.NextInvokeHandler) (results []reflect.Value, err error) {
fmt.Printf("%s(%v) = ", name, args)
results, err = next(name, args, context)
fmt.Printf("%v %v\r\n", results, err)
return
}
func main() {
// Create WebSocketServer
// service := rpc.NewWebSocketService()
// Create Http Server
service := rpc.NewHTTPService()
// Use Logger Middleware
service.AddInvokeHandler(logInvokeHandler)
// Publish Functions
service.AddFunction("AddOne", models.AddOne)
service.AddFunction("GetOne", models.GetOne)
// Start Service
beego.Handler("/", service)
beego.Run()
}
`
var HproseMainconngo = `package main
import (
"fmt"
"reflect"
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
{{.DriverPkg}}
)
func init() {
orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}")
}
func logInvokeHandler(
name string,
args []reflect.Value,
context rpc.Context,
next rpc.NextInvokeHandler) (results []reflect.Value, err error) {
fmt.Printf("%s(%v) = ", name, args)
results, err = next(name, args, context)
fmt.Printf("%v %v\r\n", results, err)
return
}
func main() {
// Create WebSocketServer
// service := rpc.NewWebSocketService()
// Create Http Server
service := rpc.NewHTTPService()
// Use Logger Middleware
service.AddInvokeHandler(logInvokeHandler)
{{HproseFunctionList}}
// Start Service
beego.Handler("/", service)
beego.Run()
}
`
var HproseModels = `package models
import (
"errors"
"strconv"
"time"
)
var (
Objects map[string]*Object
)
type Object struct {
ObjectId string
Score int64
PlayerName string
}
func init() {
Objects = make(map[string]*Object)
Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"}
Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"}
}
func AddOne(object Object) (ObjectId string) {
object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10)
Objects[object.ObjectId] = &object
return object.ObjectId
}
func GetOne(ObjectId string) (object *Object, err error) {
if v, ok := Objects[ObjectId]; ok {
return v, nil
}
return nil, errors.New("ObjectId Not Exist")
}
func GetAll() map[string]*Object {
return Objects
}
func Update(ObjectId string, Score int64) (err error) {
if v, ok := Objects[ObjectId]; ok {
v.Score = Score
return nil
}
return errors.New("ObjectId Not Exist")
}
func Delete(ObjectId string) {
delete(Objects, ObjectId)
}
`
var HproseModels2 = `package models
import (
"errors"
"strconv"
"time"
)
var (
UserList map[string]*User
)
func init() {
UserList = make(map[string]*User)
u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}}
UserList["user_11111"] = &u
}
type User struct {
Id string
Username string
Password string
Profile Profile
}
type Profile struct {
Gender string
Age int
Address string
Email string
}
func AddUser(u User) string {
u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10)
UserList[u.Id] = &u
return u.Id
}
func GetUser(uid string) (u *User, err error) {
if u, ok := UserList[uid]; ok {
return u, nil
}
return nil, errors.New("User not exists")
}
func GetAllUsers() map[string]*User {
return UserList
}
func UpdateUser(uid string, uu *User) (a *User, err error) {
if u, ok := UserList[uid]; ok {
if uu.Username != "" {
u.Username = uu.Username
}
if uu.Password != "" {
u.Password = uu.Password
}
if uu.Profile.Age != 0 {
u.Profile.Age = uu.Profile.Age
}
if uu.Profile.Address != "" {
u.Profile.Address = uu.Profile.Address
}
if uu.Profile.Gender != "" {
u.Profile.Gender = uu.Profile.Gender
}
if uu.Profile.Email != "" {
u.Profile.Email = uu.Profile.Email
}
return u, nil
}
return nil, errors.New("User Not Exist")
}
func Login(username, password string) bool {
for _, u := range UserList {
if u.Username == username && u.Password == password {
return true
}
}
return false
}
func DeleteUser(uid string) {
delete(UserList, uid)
}
`
var HproseAddFunctions = []string{}
func GenerateHproseAppcode(driver, connStr, level, tables, currpath string) {
var mode byte
switch level {
case "1":
mode = OModel
case "2":
mode = OModel | OController
case "3":
mode = OModel | OController | ORouter
default:
beeLogger.Log.Fatal("Invalid 'level' option. Level must be either \"1\", \"2\" or \"3\"")
}
var selectedTables map[string]bool
if tables != "" {
selectedTables = make(map[string]bool)
for _, v := range strings.Split(tables, ",") {
selectedTables[v] = true
}
}
switch driver {
case "mysql":
case "postgres":
case "sqlite":
beeLogger.Log.Fatal("Generating app code from SQLite database is not supported yet")
default:
beeLogger.Log.Fatalf("Unknown database driver '%s'. Driver must be one of mysql, postgres or sqlite", driver)
}
genHprose(driver, connStr, mode, selectedTables, currpath)
}
// Generate takes table, column and foreign key information from database connection
// and generate corresponding golang source files
func genHprose(dbms, connStr string, mode byte, selectedTableNames map[string]bool, currpath string) {
db, err := sql.Open(dbms, connStr)
if err != nil {
beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", dbms, connStr, err)
}
defer db.Close()
if trans, ok := dbDriver[dbms]; ok {
beeLogger.Log.Info("Analyzing database tables...")
tableNames := trans.GetTableNames(db)
tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath)
mvcPath.ModelPath = path.Join(currpath, "models")
createPaths(mode, mvcPath)
pkgPath := getPackagePath(currpath)
writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else {
beeLogger.Log.Fatalf("Generating app code from '%s' database is not supported yet", dbms)
}
}
// writeHproseSourceFiles 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 writeHproseSourceFiles(pkgPath string, tables []*Table, mode byte, paths *MvcPath, selectedTables map[string]bool) {
if (OModel & mode) == OModel {
beeLogger.Log.Info("Creating model files...")
writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
}
}
// writeHproseModelFiles generates model files
func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
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
var err error
if utils.IsExist(fpath) {
beeLogger.Log.Warnf("'%s' already exists. Do you want to overwrite it? [Yes|No] ", fpath)
if utils.AskForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
beeLogger.Log.Warnf("%s", err)
continue
}
} else {
beeLogger.Log.Warnf("Skipped create file '%s'", fpath)
continue
}
} else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
beeLogger.Log.Warnf("%s", err)
continue
}
}
template := ""
if tb.Pk == "" {
template = HproseStructModelTPL
} else {
template = HproseModelTPL
HproseAddFunctions = append(HproseAddFunctions, strings.Replace(HproseAddFunction, "{{modelName}}", utils.CamelCase(tb.Name), -1))
}
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
fileStr = strings.Replace(fileStr, "{{modelName}}", utils.CamelCase(tb.Name), -1)
// if table contains time field, import time.Time package
timePkg := ""
importTimePkg := ""
if tb.ImportTimePkg {
timePkg = "\"time\"\n"
importTimePkg = "import \"time\"\n"
}
fileStr = strings.Replace(fileStr, "{{timePkg}}", timePkg, -1)
fileStr = strings.Replace(fileStr, "{{importTimePkg}}", importTimePkg, -1)
if _, err := f.WriteString(fileStr); err != nil {
beeLogger.Log.Fatalf("Could not write model file to '%s'", fpath)
}
utils.CloseFile(f)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
utils.FormatSourceCode(fpath)
}
}
const (
HproseAddFunction = `
// publish about {{modelName}} function
service.AddFunction("Add{{modelName}}", models.Add{{modelName}})
service.AddFunction("Get{{modelName}}ById", models.Get{{modelName}}ById)
service.AddFunction("GetAll{{modelName}}", models.GetAll{{modelName}})
service.AddFunction("Update{{modelName}}ById", models.Update{{modelName}}ById)
service.AddFunction("Delete{{modelName}}", models.Delete{{modelName}})
`
HproseStructModelTPL = `package models
{{importTimePkg}}
{{modelStruct}}
`
HproseModelTPL = `package models
import (
"errors"
"fmt"
"reflect"
"strings"
{{timePkg}}
"github.com/astaxie/beego/orm"
)
{{modelStruct}}
func init() {
orm.RegisterModel(new({{modelName}}))
}
// Add{{modelName}} insert a new {{modelName}} into database and returns
// last inserted Id on success.
func Add{{modelName}}(m *{{modelName}}) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
return
}
// Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if
// Id doesn't exist
func Get{{modelName}}ById(id int) (v *{{modelName}}, err error) {
o := orm.NewOrm()
v = &{{modelName}}{Id: id}
if err = o.Read(v); err == nil {
return v, nil
}
return nil, err
}
// GetAll{{modelName}} retrieves all {{modelName}} matches certain condition. Returns empty list if
// no records exist
func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string,
offset int64, limit int64) (ml []interface{}, err error) {
o := orm.NewOrm()
qs := o.QueryTable(new({{modelName}}))
// query k=v
for k, v := range query {
// rewrite dot-notation to Object__Attribute
k = strings.Replace(k, ".", "__", -1)
qs = qs.Filter(k, v)
}
// order by:
var sortFields []string
if len(sortby) != 0 {
if len(sortby) == len(order) {
// 1) for each sort field, there is an associated order
for i, v := range sortby {
orderby := ""
if order[i] == "desc" {
orderby = "-" + v
} else if order[i] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
qs = qs.OrderBy(sortFields...)
} else if len(sortby) != len(order) && len(order) == 1 {
// 2) there is exactly one order, all the sorted fields will be sorted by this order
for _, v := range sortby {
orderby := ""
if order[0] == "desc" {
orderby = "-" + v
} else if order[0] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
} else if len(sortby) != len(order) && len(order) != 1 {
return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
}
} else {
if len(order) != 0 {
return nil, errors.New("Error: unused 'order' fields")
}
}
var l []{{modelName}}
qs = qs.OrderBy(sortFields...)
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
if len(fields) == 0 {
for _, v := range l {
ml = append(ml, v)
}
} else {
// trim unused fields
for _, v := range l {
m := make(map[string]interface{})
val := reflect.ValueOf(v)
for _, fname := range fields {
m[fname] = val.FieldByName(fname).Interface()
}
ml = append(ml, m)
}
}
return ml, nil
}
return nil, err
}
// Update{{modelName}} updates {{modelName}} by Id and returns error if
// the record to be updated doesn't exist
func Update{{modelName}}ById(m *{{modelName}}) (err error) {
o := orm.NewOrm()
v := {{modelName}}{Id: m.Id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Update(m); err == nil {
fmt.Println("Number of records updated in database:", num)
}
}
return
}
// Delete{{modelName}} deletes {{modelName}} by Id and returns error if
// the record to be deleted doesn't exist
func Delete{{modelName}}(id int) (err error) {
o := orm.NewOrm()
v := {{modelName}}{Id: id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Delete(&{{modelName}}{Id: id}); err == nil {
fmt.Println("Number of records deleted in database:", num)
}
}
return
}
`
)

248
generate/g_migration.go Normal file
View File

@ -0,0 +1,248 @@
// Copyright 2013 bee authors
//
// 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.
package generate
import (
"fmt"
"os"
"path"
"strings"
"time"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
)
const (
MPath = "migrations"
MDateFormat = "20060102_150405"
DBPath = "database"
)
type DBDriver interface {
GenerateCreateUp(tableName string) string
GenerateCreateDown(tableName string) string
}
type mysqlDriver struct{}
func (m mysqlDriver) GenerateCreateUp(tableName string) string {
upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");`
return upsql
}
func (m mysqlDriver) GenerateCreateDown(tableName string) string {
downsql := `m.SQL("DROP TABLE ` + "`" + tableName + "`" + `")`
return downsql
}
func (m mysqlDriver) generateSQLFromFields(fields string) string {
sql, tags := "", ""
fds := strings.Split(fields, ",")
for i, v := range fds {
kv := strings.SplitN(v, ":", 2)
if len(kv) != 2 {
beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return ""
}
typ, tag := m.getSQLType(kv[1])
if typ == "" {
beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return ""
}
if i == 0 && strings.ToLower(kv[0]) != "id" {
sql += "`id` int(11) NOT NULL AUTO_INCREMENT,"
tags = tags + "PRIMARY KEY (`id`),"
}
sql += "`" + utils.SnakeString(kv[0]) + "` " + typ + ","
if tag != "" {
tags = tags + fmt.Sprintf(tag, "`"+utils.SnakeString(kv[0])+"`") + ","
}
}
sql = strings.TrimRight(sql+tags, ",")
return sql
}
func (m mysqlDriver) getSQLType(ktype string) (tp, tag string) {
kv := strings.SplitN(ktype, ":", 2)
switch kv[0] {
case "string":
if len(kv) == 2 {
return "varchar(" + kv[1] + ") NOT NULL", ""
}
return "varchar(128) NOT NULL", ""
case "text":
return "longtext NOT NULL", ""
case "auto":
return "int(11) NOT NULL AUTO_INCREMENT", ""
case "pk":
return "int(11) NOT NULL", "PRIMARY KEY (%s)"
case "datetime":
return "datetime NOT NULL", ""
case "int", "int8", "int16", "int32", "int64":
fallthrough
case "uint", "uint8", "uint16", "uint32", "uint64":
return "int(11) DEFAULT NULL", ""
case "bool":
return "tinyint(1) NOT NULL", ""
case "float32", "float64":
return "float NOT NULL", ""
case "float":
return "float NOT NULL", ""
}
return "", ""
}
type postgresqlDriver struct{}
func (m postgresqlDriver) GenerateCreateUp(tableName string) string {
upsql := `m.SQL("CREATE TABLE ` + tableName + "(" + m.generateSQLFromFields(Fields.String()) + `)");`
return upsql
}
func (m postgresqlDriver) GenerateCreateDown(tableName string) string {
downsql := `m.SQL("DROP TABLE ` + tableName + `")`
return downsql
}
func (m postgresqlDriver) generateSQLFromFields(fields string) string {
sql, tags := "", ""
fds := strings.Split(fields, ",")
for i, v := range fds {
kv := strings.SplitN(v, ":", 2)
if len(kv) != 2 {
beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return ""
}
typ, tag := m.getSQLType(kv[1])
if typ == "" {
beeLogger.Log.Error("Fields format is wrong. Should be: key:type,key:type " + v)
return ""
}
if i == 0 && strings.ToLower(kv[0]) != "id" {
sql += "id serial primary key,"
}
sql += utils.SnakeString(kv[0]) + " " + typ + ","
if tag != "" {
tags = tags + fmt.Sprintf(tag, utils.SnakeString(kv[0])) + ","
}
}
if tags != "" {
sql = strings.TrimRight(sql+" "+tags, ",")
} else {
sql = strings.TrimRight(sql, ",")
}
return sql
}
func (m postgresqlDriver) getSQLType(ktype string) (tp, tag string) {
kv := strings.SplitN(ktype, ":", 2)
switch kv[0] {
case "string":
if len(kv) == 2 {
return "char(" + kv[1] + ") NOT NULL", ""
}
return "TEXT NOT NULL", ""
case "text":
return "TEXT NOT NULL", ""
case "auto", "pk":
return "serial primary key", ""
case "datetime":
return "TIMESTAMP WITHOUT TIME ZONE NOT NULL", ""
case "int", "int8", "int16", "int32", "int64":
fallthrough
case "uint", "uint8", "uint16", "uint32", "uint64":
return "integer DEFAULT NULL", ""
case "bool":
return "boolean NOT NULL", ""
case "float32", "float64", "float":
return "numeric NOT NULL", ""
}
return "", ""
}
func NewDBDriver() DBDriver {
switch SQLDriver {
case "mysql":
return mysqlDriver{}
case "postgres":
return postgresqlDriver{}
default:
beeLogger.Log.Fatal("Driver not supported")
return nil
}
}
// generateMigration generates migration file template for database schema update.
// The generated file template consists of an up() method for updating schema and
// a down() method for reverting the update.
func GenerateMigration(mname, upsql, downsql, curpath string) {
w := colors.NewColorWriter(os.Stdout)
migrationFilePath := path.Join(curpath, DBPath, MPath)
if _, err := os.Stat(migrationFilePath); os.IsNotExist(err) {
// create migrations directory
if err := os.MkdirAll(migrationFilePath, 0777); err != nil {
beeLogger.Log.Fatalf("Could not create migration directory: %s", err)
}
}
// create file
today := time.Now().Format(MDateFormat)
fpath := path.Join(migrationFilePath, fmt.Sprintf("%s_%s.go", today, mname))
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
content := strings.Replace(MigrationTPL, "{{StructName}}", utils.CamelCase(mname)+"_"+today, -1)
content = strings.Replace(content, "{{CurrTime}}", today, -1)
content = strings.Replace(content, "{{UpSQL}}", upsql, -1)
content = strings.Replace(content, "{{DownSQL}}", downsql, -1)
f.WriteString(content)
// Run 'gofmt' on the generated source code
utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create migration file: %s", err)
}
}
const MigrationTPL = `package main
import (
"github.com/astaxie/beego/migration"
)
// DO NOT MODIFY
type {{StructName}} struct {
migration.Migration
}
// DO NOT MODIFY
func init() {
m := &{{StructName}}{}
m.Created = "{{CurrTime}}"
migration.Register("{{StructName}}", m)
}
// Run the migrations
func (m *{{StructName}}) Up() {
// use m.SQL("CREATE TABLE ...") to make schema update
{{UpSQL}}
}
// Reverse the migrations
func (m *{{StructName}}) Down() {
// use m.SQL("DROP TABLE ...") to reverse schema update
{{DownSQL}}
}
`

279
generate/g_model.go Normal file
View File

@ -0,0 +1,279 @@
// Copyright 2013 bee authors
//
// 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.
package generate
import (
"errors"
"fmt"
"os"
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
)
func GenerateModel(mname, fields, currpath string) {
w := colors.NewColorWriter(os.Stdout)
p, f := path.Split(mname)
modelName := strings.Title(f)
packageName := "models"
if p != "" {
i := strings.LastIndex(p[:len(p)-1], "/")
packageName = p[i+1 : len(p)-1]
}
modelStruct, hastime, err := getStruct(modelName, fields)
if err != nil {
beeLogger.Log.Fatalf("Could not generate the model struct: %s", err)
}
beeLogger.Log.Infof("Using '%s' as model name", modelName)
beeLogger.Log.Infof("Using '%s' as package name", packageName)
fp := path.Join(currpath, "models", p)
if _, err := os.Stat(fp); os.IsNotExist(err) {
// Create the model's directory
if err := os.MkdirAll(fp, 0777); err != nil {
beeLogger.Log.Fatalf("Could not create the model directory: %s", err)
}
}
fpath := path.Join(fp, strings.ToLower(modelName)+".go")
if f, err := os.OpenFile(fpath, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
content := strings.Replace(modelTpl, "{{packageName}}", packageName, -1)
content = strings.Replace(content, "{{modelName}}", modelName, -1)
content = strings.Replace(content, "{{modelStruct}}", modelStruct, -1)
if hastime {
content = strings.Replace(content, "{{timePkg}}", `"time"`, -1)
} else {
content = strings.Replace(content, "{{timePkg}}", "", -1)
}
f.WriteString(content)
// Run 'gofmt' on the generated source code
utils.FormatSourceCode(fpath)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", fpath, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create model file: %s", err)
}
}
func getStruct(structname, fields string) (string, bool, error) {
if fields == "" {
return "", false, errors.New("fields cannot be empty")
}
hastime := false
structStr := "type " + structname + " struct{\n"
fds := strings.Split(fields, ",")
for i, v := range fds {
kv := strings.SplitN(v, ":", 2)
if len(kv) != 2 {
return "", false, errors.New("the fields format is wrong. Should be key:type,key:type " + v)
}
typ, tag, hastimeinner := getType(kv[1])
if typ == "" {
return "", false, errors.New("the fields format is wrong. Should be key:type,key:type " + v)
}
if i == 0 && strings.ToLower(kv[0]) != "id" {
structStr = structStr + "Id int64 `orm:\"auto\"`\n"
}
if hastimeinner {
hastime = true
}
structStr = structStr + utils.CamelString(kv[0]) + " " + typ + " " + tag + "\n"
}
structStr += "}\n"
return structStr, hastime, nil
}
// fields support type
// http://beego.me/docs/mvc/model/models.md#mysql
func getType(ktype string) (kt, tag string, hasTime bool) {
kv := strings.SplitN(ktype, ":", 2)
switch kv[0] {
case "string":
if len(kv) == 2 {
return "string", "`orm:\"size(" + kv[1] + ")\"`", false
}
return "string", "`orm:\"size(128)\"`", false
case "text":
return "string", "`orm:\"type(longtext)\"`", false
case "auto":
return "int64", "`orm:\"auto\"`", false
case "pk":
return "int64", "`orm:\"pk\"`", false
case "datetime":
return "time.Time", "`orm:\"type(datetime)\"`", true
case "int", "int8", "int16", "int32", "int64":
fallthrough
case "uint", "uint8", "uint16", "uint32", "uint64":
fallthrough
case "bool":
fallthrough
case "float32", "float64":
return kv[0], "", false
case "float":
return "float64", "", false
}
return "", "", false
}
var modelTpl = `package {{packageName}}
import (
"errors"
"fmt"
"reflect"
"strings"
{{timePkg}}
"github.com/astaxie/beego/orm"
)
{{modelStruct}}
func init() {
orm.RegisterModel(new({{modelName}}))
}
// Add{{modelName}} insert a new {{modelName}} into database and returns
// last inserted Id on success.
func Add{{modelName}}(m *{{modelName}}) (id int64, err error) {
o := orm.NewOrm()
id, err = o.Insert(m)
return
}
// Get{{modelName}}ById retrieves {{modelName}} by Id. Returns error if
// Id doesn't exist
func Get{{modelName}}ById(id int64) (v *{{modelName}}, err error) {
o := orm.NewOrm()
v = &{{modelName}}{Id: id}
if err = o.Read(v); err == nil {
return v, nil
}
return nil, err
}
// GetAll{{modelName}} retrieves all {{modelName}} matches certain condition. Returns empty list if
// no records exist
func GetAll{{modelName}}(query map[string]string, fields []string, sortby []string, order []string,
offset int64, limit int64) (ml []interface{}, err error) {
o := orm.NewOrm()
qs := o.QueryTable(new({{modelName}}))
// query k=v
for k, v := range query {
// rewrite dot-notation to Object__Attribute
k = strings.Replace(k, ".", "__", -1)
qs = qs.Filter(k, v)
}
// order by:
var sortFields []string
if len(sortby) != 0 {
if len(sortby) == len(order) {
// 1) for each sort field, there is an associated order
for i, v := range sortby {
orderby := ""
if order[i] == "desc" {
orderby = "-" + v
} else if order[i] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
qs = qs.OrderBy(sortFields...)
} else if len(sortby) != len(order) && len(order) == 1 {
// 2) there is exactly one order, all the sorted fields will be sorted by this order
for _, v := range sortby {
orderby := ""
if order[0] == "desc" {
orderby = "-" + v
} else if order[0] == "asc" {
orderby = v
} else {
return nil, errors.New("Error: Invalid order. Must be either [asc|desc]")
}
sortFields = append(sortFields, orderby)
}
} else if len(sortby) != len(order) && len(order) != 1 {
return nil, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
}
} else {
if len(order) != 0 {
return nil, errors.New("Error: unused 'order' fields")
}
}
var l []{{modelName}}
qs = qs.OrderBy(sortFields...)
if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
if len(fields) == 0 {
for _, v := range l {
ml = append(ml, v)
}
} else {
// trim unused fields
for _, v := range l {
m := make(map[string]interface{})
val := reflect.ValueOf(v)
for _, fname := range fields {
m[fname] = val.FieldByName(fname).Interface()
}
ml = append(ml, m)
}
}
return ml, nil
}
return nil, err
}
// Update{{modelName}} updates {{modelName}} by Id and returns error if
// the record to be updated doesn't exist
func Update{{modelName}}ById(m *{{modelName}}) (err error) {
o := orm.NewOrm()
v := {{modelName}}{Id: m.Id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Update(m); err == nil {
fmt.Println("Number of records updated in database:", num)
}
}
return
}
// Delete{{modelName}} deletes {{modelName}} by Id and returns error if
// the record to be deleted doesn't exist
func Delete{{modelName}}(id int64) (err error) {
o := orm.NewOrm()
v := {{modelName}}{Id: id}
// ascertain id exists in the database
if err = o.Read(&v); err == nil {
var num int64
if num, err = o.Delete(&{{modelName}}{Id: id}); err == nil {
fmt.Println("Number of records deleted in database:", num)
}
}
return
}
`

50
generate/g_scaffold.go Normal file
View File

@ -0,0 +1,50 @@
package generate
import (
"strings"
"github.com/beego/bee/cmd/commands/migrate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
)
func GenerateScaffold(sname, fields, currpath, driver, conn string) {
beeLogger.Log.Infof("Do you want to create a '%s' model? [Yes|No] ", sname)
// Generate the model
if utils.AskForConfirmation() {
GenerateModel(sname, fields, currpath)
}
// Generate the controller
beeLogger.Log.Infof("Do you want to create a '%s' controller? [Yes|No] ", sname)
if utils.AskForConfirmation() {
GenerateController(sname, currpath)
}
// Generate the views
beeLogger.Log.Infof("Do you want to create views for this '%s' resource? [Yes|No] ", sname)
if utils.AskForConfirmation() {
GenerateView(sname, currpath)
}
// Generate a migration
beeLogger.Log.Infof("Do you want to create a '%s' migration and schema for this resource? [Yes|No] ", sname)
if utils.AskForConfirmation() {
upsql := ""
downsql := ""
if fields != "" {
dbMigrator := NewDBDriver()
upsql = dbMigrator.GenerateCreateUp(sname)
downsql = dbMigrator.GenerateCreateDown(sname)
}
GenerateMigration(sname, upsql, downsql, currpath)
}
// Run the migration
beeLogger.Log.Infof("Do you want to migrate the database? [Yes|No] ")
if utils.AskForConfirmation() {
migrate.MigrateUpdate(currpath, driver, conn)
}
beeLogger.Log.Successf("All done! Don't forget to add beego.Router(\"/%s\" ,&controllers.%sController{}) to routers/route.go\n", sname, strings.Title(sname))
}

75
generate/g_views.go Normal file
View File

@ -0,0 +1,75 @@
// Copyright 2013 bee authors
//
// 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.
package generate
import (
"fmt"
"os"
"path"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
)
// recipe
// admin/recipe
func GenerateView(viewpath, currpath string) {
w := colors.NewColorWriter(os.Stdout)
beeLogger.Log.Info("Generating view...")
absViewPath := path.Join(currpath, "views", viewpath)
err := os.MkdirAll(absViewPath, os.ModePerm)
if err != nil {
beeLogger.Log.Fatalf("Could not create '%s' view: %s", viewpath, err)
}
cfile := path.Join(absViewPath, "index.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create view file: %s", err)
}
cfile = path.Join(absViewPath, "show.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create view file: %s", err)
}
cfile = path.Join(absViewPath, "create.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create view file: %s", err)
}
cfile = path.Join(absViewPath, "edit.tpl")
if f, err := os.OpenFile(cfile, os.O_CREATE|os.O_EXCL|os.O_RDWR, 0666); err == nil {
defer utils.CloseFile(f)
f.WriteString(cfile)
fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", cfile, "\x1b[0m")
} else {
beeLogger.Log.Fatalf("Could not create view file: %s", err)
}
}

View File

@ -0,0 +1,954 @@
// Copyright 2013 bee authors
//
// 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.
package swaggergen
import (
"encoding/json"
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"unicode"
"gopkg.in/yaml.v2"
"github.com/astaxie/beego/swagger"
"github.com/astaxie/beego/utils"
beeLogger "github.com/beego/bee/logger"
)
const (
ajson = "application/json"
axml = "application/xml"
aplain = "text/plain"
ahtml = "text/html"
)
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
var controllerComments map[string]string
var importlist map[string]string
var controllerList map[string]map[string]*swagger.Item //controllername Paths items
var modelsList map[string]map[string]swagger.Schema
var rootapi swagger.Swagger
var astPkgs map[string]*ast.Package
// refer to builtin.go
var basicTypes = map[string]string{
"bool": "boolean:",
"uint": "integer:int32",
"uint8": "integer:int32",
"uint16": "integer:int32",
"uint32": "integer:int32",
"uint64": "integer:int64",
"int": "integer:int64",
"int8": "integer:int32",
"int16": "integer:int32",
"int32": "integer:int32",
"int64": "integer:int64",
"uintptr": "integer:int64",
"float32": "number:float",
"float64": "number:double",
"string": "string:",
"complex64": "number:float",
"complex128": "number:double",
"byte": "string:byte",
"rune": "string:byte",
// builtin golang objects
"time.Time": "string:string",
}
var stdlibObject = map[string]string{
"&{time Time}": "time.Time",
}
func init() {
pkgCache = make(map[string]struct{})
controllerComments = make(map[string]string)
importlist = make(map[string]string)
controllerList = make(map[string]map[string]*swagger.Item)
modelsList = make(map[string]map[string]swagger.Schema)
astPkgs = map[string]*ast.Package{}
}
func ParsePackagesFromDir(dirpath string) {
c := make(chan error)
go func() {
filepath.Walk(dirpath, func(fpath string, fileInfo os.FileInfo, err error) error {
if err != nil {
return nil
}
if !fileInfo.IsDir() {
return nil
}
if !strings.Contains(fpath, "vendor") && !strings.Contains(fpath, "tests") {
err = parsePackageFromDir(fpath)
if err != nil {
// Send the error to through the channel and continue walking
c <- fmt.Errorf("Error while parsing directory: %s", err.Error())
return nil
}
}
return nil
})
close(c)
}()
for err := range c {
beeLogger.Log.Warnf("%s", err)
}
}
func parsePackageFromDir(path string) error {
fileSet := token.NewFileSet()
folderPkgs, err := parser.ParseDir(fileSet, path, func(info os.FileInfo) bool {
name := info.Name()
return !info.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}, parser.ParseComments)
if err != nil {
return err
}
for k, v := range folderPkgs {
astPkgs[k] = v
}
return nil
}
func GenerateDocs(curpath string) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, path.Join(curpath, "routers", "router.go"), nil, parser.ParseComments)
if err != nil {
// beeLogger.Log.Fatalf("Error while parsing router.go: %s", err)
}
rootapi.Infos = swagger.Information{}
rootapi.SwaggerVersion = "2.0"
// Analyse API comments
if f.Comments != nil {
for _, c := range f.Comments {
for _, s := range strings.Split(c.Text(), "\n") {
if strings.HasPrefix(s, "@APIVersion") {
rootapi.Infos.Version = strings.TrimSpace(s[len("@APIVersion"):])
} else if strings.HasPrefix(s, "@Title") {
rootapi.Infos.Title = strings.TrimSpace(s[len("@Title"):])
} else if strings.HasPrefix(s, "@Description") {
rootapi.Infos.Description = strings.TrimSpace(s[len("@Description"):])
} else if strings.HasPrefix(s, "@TermsOfServiceUrl") {
rootapi.Infos.TermsOfService = strings.TrimSpace(s[len("@TermsOfServiceUrl"):])
} else if strings.HasPrefix(s, "@Contact") {
rootapi.Infos.Contact.EMail = strings.TrimSpace(s[len("@Contact"):])
} else if strings.HasPrefix(s, "@Name") {
rootapi.Infos.Contact.Name = strings.TrimSpace(s[len("@Name"):])
} else if strings.HasPrefix(s, "@URL") {
rootapi.Infos.Contact.URL = strings.TrimSpace(s[len("@URL"):])
} else if strings.HasPrefix(s, "@LicenseUrl") {
if rootapi.Infos.License == nil {
rootapi.Infos.License = &swagger.License{URL: strings.TrimSpace(s[len("@LicenseUrl"):])}
} else {
rootapi.Infos.License.URL = strings.TrimSpace(s[len("@LicenseUrl"):])
}
} else if strings.HasPrefix(s, "@License") {
if rootapi.Infos.License == nil {
rootapi.Infos.License = &swagger.License{Name: strings.TrimSpace(s[len("@License"):])}
} else {
rootapi.Infos.License.Name = strings.TrimSpace(s[len("@License"):])
}
} else if strings.HasPrefix(s, "@Schemes") {
rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",")
} else if strings.HasPrefix(s, "@Host") {
rootapi.Host = strings.TrimSpace(s[len("@Host"):])
}
}
}
}
// Analyse controller package
for _, im := range f.Imports {
localName := ""
if im.Name != nil {
localName = im.Name.Name
}
analyseControllerPkg(localName, im.Path.Value)
}
for _, d := range f.Decls {
switch specDecl := d.(type) {
case *ast.FuncDecl:
for _, l := range specDecl.Body.List {
switch stmt := l.(type) {
case *ast.AssignStmt:
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
}
version, params := analyseNewNamespace(v)
if rootapi.BasePath == "" && version != "" {
rootapi.BasePath = version
}
for _, p := range params {
switch pp := p.(type) {
case *ast.CallExpr:
controllerName := ""
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
s, params := analyseNewNamespace(pp)
for _, sp := range params {
switch pp := sp.(type) {
case *ast.CallExpr:
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
controllerName = analyseNSInclude(s, pp)
if v, ok := controllerComments[controllerName]; ok {
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
Name: strings.Trim(s, "/"),
Description: v,
})
}
}
}
}
} else if selname == "NSInclude" {
controllerName = analyseNSInclude("", pp)
if v, ok := controllerComments[controllerName]; ok {
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag
Description: v,
})
}
}
}
}
}
}
}
}
}
}
os.Mkdir(path.Join(curpath, "swagger"), 0755)
fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json"))
if err != nil {
panic(err)
}
fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml"))
if err != nil {
panic(err)
}
defer fdyml.Close()
defer fd.Close()
dt, err := json.MarshalIndent(rootapi, "", " ")
dtyml, erryml := yaml.Marshal(rootapi)
if err != nil || erryml != nil {
panic(err)
}
_, err = fd.Write(dt)
_, erryml = fdyml.Write(dtyml)
if err != nil || erryml != nil {
panic(err)
}
}
// analyseNewNamespace returns version and the others params
func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
for i, p := range ce.Args {
if i == 0 {
switch pp := p.(type) {
case *ast.BasicLit:
first = strings.Trim(pp.Value, `"`)
}
continue
}
others = append(others, p)
}
return
}
func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
cname := ""
for _, p := range ce.Args {
x := p.(*ast.UnaryExpr).X.(*ast.CompositeLit).Type.(*ast.SelectorExpr)
if v, ok := importlist[fmt.Sprint(x.X)]; ok {
cname = v + x.Sel.Name
}
if apis, ok := controllerList[cname]; ok {
for rt, item := range apis {
tag := ""
if baseurl != "" {
rt = baseurl + rt
tag = strings.Trim(baseurl, "/")
} else {
tag = cname
}
if item.Get != nil {
item.Get.Tags = []string{tag}
}
if item.Post != nil {
item.Post.Tags = []string{tag}
}
if item.Put != nil {
item.Put.Tags = []string{tag}
}
if item.Patch != nil {
item.Patch.Tags = []string{tag}
}
if item.Head != nil {
item.Head.Tags = []string{tag}
}
if item.Delete != nil {
item.Delete.Tags = []string{tag}
}
if item.Options != nil {
item.Options.Tags = []string{tag}
}
if len(rootapi.Paths) == 0 {
rootapi.Paths = make(map[string]*swagger.Item)
}
rt = urlReplace(rt)
rootapi.Paths[rt] = item
}
}
}
return cname
}
func analyseControllerPkg(localName, pkgpath string) {
pkgpath = strings.Trim(pkgpath, "\"")
if isSystemPackage(pkgpath) {
return
}
if pkgpath == "github.com/astaxie/beego" {
return
}
if localName != "" {
importlist[localName] = pkgpath
} else {
pps := strings.Split(pkgpath, "/")
importlist[pps[len(pps)-1]] = pkgpath
}
gopath := os.Getenv("GOPATH")
if gopath == "" {
// beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
pkgRealpath := ""
wgopath := filepath.SplitList(gopath)
for _, wg := range wgopath {
wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", pkgpath))
if utils.FileExists(wg) {
pkgRealpath = wg
break
}
}
if pkgRealpath != "" {
if _, ok := pkgCache[pkgpath]; ok {
return
}
pkgCache[pkgpath] = struct{}{}
} else {
// beeLogger.Log.Fatalf("Package '%s' does not exist in the GOPATH", pkgpath)
}
fileSet := token.NewFileSet()
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 astPkgs {
for _, fl := range pkg.Files {
for _, d := range fl.Decls {
switch specDecl := d.(type) {
case *ast.FuncDecl:
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)
}
}
case *ast.GenDecl:
if specDecl.Tok == token.TYPE {
for _, s := range specDecl.Specs {
switch tp := s.(*ast.TypeSpec).Type.(type) {
case *ast.StructType:
_ = tp.Struct
// Parse controller definition comments
if strings.TrimSpace(specDecl.Doc.Text()) != "" {
controllerComments[pkgpath+s.(*ast.TypeSpec).Name.String()] = specDecl.Doc.Text()
}
}
}
}
}
}
}
}
}
func isSystemPackage(pkgpath string) bool {
goroot := os.Getenv("GOROOT")
if goroot == "" {
goroot = runtime.GOROOT()
}
if goroot == "" {
// beeLogger.Log.Fatalf("GOROOT environment variable is not set or empty")
}
wg, _ := filepath.EvalSymlinks(filepath.Join(goroot, "src", "pkg", pkgpath))
if utils.FileExists(wg) {
return true
}
//TODO(zh):support go1.4
wg, _ = filepath.EvalSymlinks(filepath.Join(goroot, "src", pkgpath))
return utils.FileExists(wg)
}
func peekNextSplitString(ss string) (s string, spacePos int) {
spacePos = strings.IndexFunc(ss, unicode.IsSpace)
if spacePos < 0 {
s = ss
spacePos = len(ss)
} else {
s = strings.TrimSpace(ss[:spacePos])
}
return
}
// parse the func comments
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
var routerPath string
var HTTPMethod string
opts := swagger.Operation{
Responses: make(map[string]swagger.Response),
}
if comments != nil && comments.List != nil {
for _, c := range comments.List {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
if strings.HasPrefix(t, "@router") {
elements := strings.TrimSpace(t[len("@router"):])
e1 := strings.SplitN(elements, " ", 2)
if len(e1) < 1 {
return errors.New("you should has router infomation")
}
routerPath = e1[0]
if len(e1) == 2 && e1[1] != "" {
e1 = strings.SplitN(e1[1], " ", 2)
HTTPMethod = strings.ToUpper(strings.Trim(e1[0], "[]"))
} else {
HTTPMethod = "GET"
}
} else if strings.HasPrefix(t, "@Title") {
opts.OperationID = controllerName + "." + strings.TrimSpace(t[len("@Title"):])
} else if strings.HasPrefix(t, "@Description") {
opts.Description = strings.TrimSpace(t[len("@Description"):])
} else if strings.HasPrefix(t, "@Summary") {
opts.Summary = strings.TrimSpace(t[len("@Summary"):])
} else if strings.HasPrefix(t, "@Success") {
ss := strings.TrimSpace(t[len("@Success"):])
rs := swagger.Response{}
respCode, pos := peekNextSplitString(ss)
ss = strings.TrimSpace(ss[pos:])
respType, pos := peekNextSplitString(ss)
if respType == "{object}" || respType == "{array}" {
isArray := respType == "{array}"
ss = strings.TrimSpace(ss[pos:])
schemaName, pos := peekNextSplitString(ss)
if schemaName == "" {
// beeLogger.Log.Fatalf("[%s.%s] Schema must follow {object} or {array}", controllerName, funcName)
}
if strings.HasPrefix(schemaName, "[]") {
schemaName = schemaName[2:]
isArray = true
}
schema := swagger.Schema{}
if sType, ok := basicTypes[schemaName]; ok {
typeFormat := strings.Split(sType, ":")
schema.Type = typeFormat[0]
schema.Format = typeFormat[1]
} else {
m, mod, realTypes := getModel(schemaName)
schema.Ref = "#/definitions/" + m
if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
}
modelsList[pkgpath+controllerName][schemaName] = mod
appendModels(pkgpath, controllerName, realTypes)
}
if isArray {
rs.Schema = &swagger.Schema{
Type: "array",
Items: &schema,
}
} else {
rs.Schema = &schema
}
rs.Description = strings.TrimSpace(ss[pos:])
} else {
rs.Description = strings.TrimSpace(ss)
}
opts.Responses[respCode] = rs
} else if strings.HasPrefix(t, "@Param") {
para := swagger.Parameter{}
p := getparams(strings.TrimSpace(t[len("@Param "):]))
if len(p) < 4 {
// beeLogger.Log.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params")
}
para.Name = p[0]
switch p[1] {
case "query":
fallthrough
case "header":
fallthrough
case "path":
fallthrough
case "formData":
fallthrough
case "body":
break
default:
// beeLogger.Log.Warnf("[%s.%s] Unknown param location: %s. Possible values are `query`, `header`, `path`, `formData` or `body`.\n", controllerName, funcName, p[1])
}
para.In = p[1]
pp := strings.Split(p[2], ".")
typ := pp[len(pp)-1]
if len(pp) >= 2 {
m, mod, realTypes := getModel(p[2])
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)
} 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
}
}
switch len(p) {
case 5:
para.Required, _ = strconv.ParseBool(p[3])
para.Description = strings.Trim(p[4], `" `)
case 6:
para.Default = str2RealType(p[3], para.Type)
para.Required, _ = strconv.ParseBool(p[4])
para.Description = strings.Trim(p[5], `" `)
default:
para.Description = strings.Trim(p[3], `" `)
}
opts.Parameters = append(opts.Parameters, para)
} else if strings.HasPrefix(t, "@Failure") {
rs := swagger.Response{}
st := strings.TrimSpace(t[len("@Failure"):])
var cd []rune
var start bool
for i, s := range st {
if unicode.IsSpace(s) {
if start {
rs.Description = strings.TrimSpace(st[i+1:])
break
} else {
continue
}
}
start = true
cd = append(cd, s)
}
opts.Responses[string(cd)] = rs
} else if strings.HasPrefix(t, "@Deprecated") {
opts.Deprecated, _ = strconv.ParseBool(strings.TrimSpace(t[len("@Deprecated"):]))
} else if strings.HasPrefix(t, "@Accept") {
accepts := strings.Split(strings.TrimSpace(strings.TrimSpace(t[len("@Accept"):])), ",")
for _, a := range accepts {
switch a {
case "json":
opts.Consumes = append(opts.Consumes, ajson)
opts.Produces = append(opts.Produces, ajson)
case "xml":
opts.Consumes = append(opts.Consumes, axml)
opts.Produces = append(opts.Produces, axml)
case "plain":
opts.Consumes = append(opts.Consumes, aplain)
opts.Produces = append(opts.Produces, aplain)
case "html":
opts.Consumes = append(opts.Consumes, ahtml)
opts.Produces = append(opts.Produces, ahtml)
}
}
}
}
}
if routerPath != "" {
var item *swagger.Item
if itemList, ok := controllerList[pkgpath+controllerName]; ok {
if it, ok := itemList[routerPath]; !ok {
item = &swagger.Item{}
} else {
item = it
}
} else {
controllerList[pkgpath+controllerName] = make(map[string]*swagger.Item)
item = &swagger.Item{}
}
switch HTTPMethod {
case "GET":
item.Get = &opts
case "POST":
item.Post = &opts
case "PUT":
item.Put = &opts
case "PATCH":
item.Patch = &opts
case "DELETE":
item.Delete = &opts
case "HEAD":
item.Head = &opts
case "OPTIONS":
item.Options = &opts
}
controllerList[pkgpath+controllerName][routerPath] = item
}
return nil
}
// analisys params return []string
// @Param query form string true "The email for login"
// [query form string true "The email for login"]
func getparams(str string) []string {
var s []rune
var j int
var start bool
var r []string
var quoted int8
for _, c := range []rune(str) {
if unicode.IsSpace(c) && quoted == 0 {
if !start {
continue
} else {
start = false
j++
r = append(r, string(s))
s = make([]rune, 0)
continue
}
}
start = true
if c == '"' {
quoted ^= 1
continue
}
s = append(s, c)
}
if len(s) > 0 {
r = append(r, string(s))
}
return r
}
func getModel(str string) (objectname string, m swagger.Schema, realTypes []string) {
strs := strings.Split(str, ".")
objectname = strs[len(strs)-1]
packageName := ""
m.Type = "object"
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for k, d := range fl.Scope.Objects {
if d.Kind == ast.Typ {
if k != objectname {
continue
}
packageName = pkg.Name
parseObject(d, k, &m, &realTypes, astPkgs, pkg.Name)
}
}
}
}
if m.Title == "" {
// beeLogger.Log.Warnf("Cannot find the object: %s", str)
// 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
}
func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string, astPkgs map[string]*ast.Package, packageName string) {
ts, ok := d.Decl.(*ast.TypeSpec)
if !ok {
// 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)
if !ok {
return
}
m.Title = k
if st.Fields.List != nil {
m.Properties = make(map[string]swagger.Propertie)
for _, field := range st.Fields.List {
realType := ""
isSlice, realType, sType := typeAnalyser(field)
if (isSlice && isBasicType(realType)) || sType == "object" {
if len(strings.Split(realType, " ")) > 1 {
realType = strings.Replace(realType, " ", ".", -1)
realType = strings.Replace(realType, "&", "", -1)
realType = strings.Replace(realType, "{", "", -1)
realType = strings.Replace(realType, "}", "", -1)
} else {
realType = packageName + "." + realType
}
}
*realTypes = append(*realTypes, realType)
mp := swagger.Propertie{}
if isSlice {
mp.Type = "array"
if isBasicType(realType) {
typeFormat := strings.Split(sType, ":")
mp.Items = &swagger.Propertie{
Type: typeFormat[0],
Format: typeFormat[1],
}
} else {
mp.Items = &swagger.Propertie{
Ref: "#/definitions/" + realType,
}
}
} else {
if sType == "object" {
mp.Ref = "#/definitions/" + realType
} else if isBasicType(realType) {
typeFormat := strings.Split(sType, ":")
mp.Type = typeFormat[0]
mp.Format = typeFormat[1]
} else if realType == "map" {
typeFormat := strings.Split(sType, ":")
mp.AdditionalProperties = &swagger.Propertie{
Type: typeFormat[0],
Format: typeFormat[1],
}
}
}
if field.Names != nil {
// set property name as field name
var name = field.Names[0].Name
// if no tag skip tag processing
if field.Tag == nil {
m.Properties[name] = mp
continue
}
var tagValues []string
stag := reflect.StructTag(strings.Trim(field.Tag.Value, "`"))
defaultValue := stag.Get("doc")
if defaultValue != "" {
r, _ := regexp.Compile(`default\((.*)\)`)
if r.MatchString(defaultValue) {
res := r.FindStringSubmatch(defaultValue)
mp.Default = str2RealType(res[1], realType)
} else {
// beeLogger.Log.Warnf("Invalid default value: %s", defaultValue)
}
}
tag := stag.Get("json")
if tag != "" {
tagValues = strings.Split(tag, ",")
}
// dont add property if json tag first value is "-"
if len(tagValues) == 0 || tagValues[0] != "-" {
// set property name to the left most json tag value only if is not omitempty
if len(tagValues) > 0 && tagValues[0] != "omitempty" {
name = tagValues[0]
}
if thrifttag := stag.Get("thrift"); thrifttag != "" {
ts := strings.Split(thrifttag, ",")
if ts[0] != "" {
name = ts[0]
}
}
if required := stag.Get("required"); required != "" {
m.Required = append(m.Required, name)
}
if desc := stag.Get("description"); desc != "" {
mp.Description = desc
}
m.Properties[name] = mp
}
if ignore := stag.Get("ignore"); ignore != "" {
continue
}
} else {
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, m, realTypes, astPkgs, pkg.Name)
}
}
}
}
}
}
}
}
func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
if arr, ok := f.Type.(*ast.ArrayType); ok {
if isBasicType(fmt.Sprint(arr.Elt)) {
return false, 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"
}
if star, ok := arr.Elt.(*ast.StarExpr); ok {
return true, fmt.Sprint(star.X), "object"
}
return true, fmt.Sprint(arr.Elt), "object"
}
switch t := f.Type.(type) {
case *ast.StarExpr:
return false, fmt.Sprint(t.X), "object"
case *ast.MapType:
val := fmt.Sprintf("%v", t.Value)
if isBasicType(val) {
return false, "map", basicTypes[val]
}
return false, val, "object"
}
basicType := fmt.Sprint(f.Type)
if object, isStdLibObject := stdlibObject[basicType]; isStdLibObject {
basicType = object
}
if k, ok := basicTypes[basicType]; ok {
return false, basicType, k
}
return false, basicType, "object"
}
func isBasicType(Type string) bool {
if _, ok := basicTypes[Type]; ok {
return true
}
return false
}
// append models
func appendModels(pkgpath, controllerName string, realTypes []string) {
for _, realType := range realTypes {
if realType != "" && !isBasicType(strings.TrimLeft(realType, "[]")) &&
!strings.HasPrefix(realType, "map") && !strings.HasPrefix(realType, "&") {
if _, ok := modelsList[pkgpath+controllerName][realType]; ok {
continue
}
_, mod, newRealTypes := getModel(realType)
modelsList[pkgpath+controllerName][realType] = mod
appendModels(pkgpath, controllerName, newRealTypes)
}
}
}
func urlReplace(src string) string {
pt := strings.Split(src, "/")
for i, p := range pt {
if len(p) > 0 {
if p[0] == ':' {
pt[i] = "{" + p[1:] + "}"
} else if p[0] == '?' && p[1] == ':' {
pt[i] = "{" + p[2:] + "}"
}
}
}
return strings.Join(pt, "/")
}
func str2RealType(s string, typ string) interface{} {
var err error
var ret interface{}
switch typ {
case "int", "int64", "int32", "int16", "int8":
ret, err = strconv.Atoi(s)
case "bool":
ret, err = strconv.ParseBool(s)
case "float64":
ret, err = strconv.ParseFloat(s, 64)
case "float32":
ret, err = strconv.ParseFloat(s, 32)
default:
return s
}
if err != nil {
// beeLogger.Log.Warnf("Invalid default value type '%s': %s", typ, s)
return s
}
return ret
}