自动生成基于数据库的Hprose发布服务。

This commit is contained in:
Liujian 2014-10-14 21:30:32 +08:00
parent 7905d77f05
commit f75d8997ef
4 changed files with 1348 additions and 1 deletions

1
bee.go
View File

@ -77,6 +77,7 @@ var commands = []*Command{
cmdRun,
cmdPack,
cmdApiapp,
cmdHproseapp,
//cmdRouter,
//cmdTest,
cmdBale,

View File

@ -499,7 +499,13 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
// getGoDataType maps an SQL data type to Golang data type
func (*MysqlDB) GetGoDataType(sqlType string) (goType string) {
if v, ok := typeMappingMysql[sqlType]; ok {
var typeMapping = map[string]string{}
if isCreateHproseApp {
typeMapping = typeMappingMysqlOfRpc
} else {
typeMapping = typeMappingMysql
}
if v, ok := typeMapping[sqlType]; ok {
return v
} else {
ColorLog("[ERRO] data type (%s) not found!\n", sqlType)

685
g_hproseappcode.go Normal file
View File

@ -0,0 +1,685 @@
/**********************************************************\
| |
| hprose |
| |
| Official WebSite: http://www.hprose.com/ |
| http://www.hprose.org/ |
| |
\**********************************************************/
/**********************************************************\
* *
* Build rpc application use Hprose base on beego *
* *
* LastModified: Oct 13, 2014 *
* Author: Liu jian <laoliu@lanmv.com> *
* *
\**********************************************************/
package main
import (
"database/sql"
"os"
"path"
"strings"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
// typeMapping maps SQL data type to corresponding Go data type
var typeMappingMysqlOfRpc = map[string]string{
"int": "int", // int signed
"integer": "int",
"tinyint": "int8",
"smallint": "int16",
"mediumint": "int32",
"bigint": "int64",
"int unsigned": "uint", // int unsigned
"integer unsigned": "uint",
"tinyint unsigned": "uint8",
"smallint unsigned": "uint16",
"mediumint unsigned": "uint32",
"bigint unsigned": "uint64",
"bit": "uint64",
"bool": "bool", // boolean
"enum": "string", // enum
"set": "string", // set
"varchar": "string", // string & text
"char": "string",
"tinytext": "string",
"mediumtext": "string",
"text": "string",
"longtext": "string",
"blob": "byte", // blob as byte
"tinyblob": "byte",
"mediumblob": "byte",
"longblob": "byte",
"date": "time.Time", // time
"datetime": "time.Time",
"timestamp": "time.Time",
"time": "time.Time",
"float": "float32", // float & decimal
"double": "float64",
"decimal": "float64",
"binary": "string", // binary
"varbinary": "string",
}
// typeMappingPostgres maps SQL data type to corresponding Go data type
var typeMappingPostgresOfRpc = map[string]string{
"serial": "int", // serial
"big serial": "int64",
"smallint": "int16", // int
"integer": "int",
"bigint": "int64",
"boolean": "bool", // bool
"char": "string", // string
"character": "string",
"character varying": "string",
"varchar": "string",
"text": "string",
"date": "time.Time", // time
"time": "time.Time",
"timestamp": "time.Time",
"timestamp without time zone": "time.Time",
"interval": "string", // time interval, string for now
"real": "float32", // float & decimal
"double precision": "float64",
"decimal": "float64",
"numeric": "float64",
"money": "float64", // money
"bytea": "byte", // binary
"tsvector": "string", // fulltext
"ARRAY": "string", // array
"USER-DEFINED": "string", // user defined
"uuid": "string", // uuid
"json": "string", // json
}
func generateHproseAppcode(driver, connStr, level, tables, currpath string) {
var mode byte
switch level {
case "1":
mode = O_MODEL
case "2":
mode = O_MODEL | O_CONTROLLER
case "3":
mode = O_MODEL | O_CONTROLLER | O_ROUTER
default:
ColorLog("[ERRO] Invalid 'level' option: %s\n", level)
ColorLog("[HINT] Level must be either 1, 2 or 3\n")
os.Exit(2)
}
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":
ColorLog("[ERRO] Generating app code from SQLite database is not supported yet.\n")
os.Exit(2)
default:
ColorLog("[ERRO] Unknown database driver: %s\n", driver)
ColorLog("[HINT] Driver must be one of mysql, postgres or sqlite\n")
os.Exit(2)
}
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 {
ColorLog("[ERRO] Could not connect to %s database: %s, %s\n", dbms, connStr, err)
os.Exit(2)
}
defer db.Close()
if trans, ok := dbDriver[dbms]; ok {
ColorLog("[INFO] Analyzing database tables...\n")
tableNames := trans.GetTableNames(db)
// 添加 Hprose Function
for _, tb := range tableNames {
hproseAddFunctions = append(hproseAddFunctions, strings.Replace(HPROSE_ADDFUNCTION, "{{modelName}}", camelCase(tb), -1))
}
// 添加结束
tables := getTableObjects(tableNames, db, trans)
mvcPath := new(MvcPath)
mvcPath.ModelPath = path.Join(currpath, "models")
mvcPath.ControllerPath = path.Join(currpath, "controllers")
mvcPath.RouterPath = path.Join(currpath, "routers")
createPaths(mode, mvcPath)
pkgPath := getPackagePath(currpath)
writeHproseSourceFiles(pkgPath, tables, mode, mvcPath, selectedTableNames)
} else {
ColorLog("[ERRO] Generating app code from %s database is not supported yet.\n", dbms)
os.Exit(2)
}
}
// 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 (O_MODEL & mode) == O_MODEL {
ColorLog("[INFO] Creating model files...\n")
writeHproseModelFiles(tables, paths.ModelPath, selectedTables)
}
if (O_CONTROLLER & mode) == O_CONTROLLER {
ColorLog("[INFO] Creating controller files...\n")
writeHproseControllerFiles(tables, paths.ControllerPath, selectedTables, pkgPath)
}
if (O_ROUTER & mode) == O_ROUTER {
ColorLog("[INFO] Creating router files...\n")
writeHproseRouterFile(tables, paths.RouterPath, selectedTables, pkgPath)
}
}
// writeHproseModelFiles generates model files
func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[string]bool) {
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 isExist(fpath) {
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
continue
}
} else {
ColorLog("[WARN] skip create file\n")
continue
}
} else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
continue
}
}
template := ""
if tb.Pk == "" {
template = HPROSE_STRUCT_MODEL_TPL
} else {
template = HPROSE_MODEL_TPL
}
fileStr := strings.Replace(template, "{{modelStruct}}", tb.String(), 1)
fileStr = strings.Replace(fileStr, "{{modelName}}", 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 {
ColorLog("[ERRO] Could not write model file to %s\n", fpath)
os.Exit(2)
}
f.Close()
ColorLog("[INFO] model => %s\n", fpath)
formatSourceCode(fpath)
}
}
// writeHproseControllerFiles generates controller files
func writeHproseControllerFiles(tables []*Table, cPath string, selectedTables map[string]bool, pkgPath 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
}
filename := getFileName(tb.Name)
fpath := path.Join(cPath, filename+".go")
var f *os.File
var err error
if isExist(fpath) {
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
continue
}
} else {
ColorLog("[WARN] skip create file\n")
continue
}
} else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
continue
}
}
fileStr := strings.Replace(HPROSE_CTRL_TPL, "{{ctrlName}}", camelCase(tb.Name), -1)
fileStr = strings.Replace(fileStr, "{{pkgPath}}", pkgPath, -1)
if _, err := f.WriteString(fileStr); err != nil {
ColorLog("[ERRO] Could not write controller file to %s\n", fpath)
os.Exit(2)
}
f.Close()
ColorLog("[INFO] controller => %s\n", fpath)
formatSourceCode(fpath)
}
}
// writeHproseRouterFile generates router file
func writeHproseRouterFile(tables []*Table, rPath string, selectedTables map[string]bool, pkgPath string) {
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
}
// add name spaces
nameSpace := strings.Replace(HPROSE_NAMESPACE_TPL, "{{nameSpace}}", tb.Name, -1)
nameSpace = strings.Replace(nameSpace, "{{ctrlName}}", camelCase(tb.Name), -1)
nameSpaces = append(nameSpaces, nameSpace)
}
// add export controller
fpath := path.Join(rPath, "router.go")
routerStr := strings.Replace(HPROSE_ROUTER_TPL, "{{nameSpaces}}", strings.Join(nameSpaces, ""), 1)
routerStr = strings.Replace(routerStr, "{{pkgPath}}", pkgPath, 1)
var f *os.File
var err error
if isExist(fpath) {
ColorLog("[WARN] %v is exist, do you want to overwrite it? Yes or No?\n", fpath)
if askForConfirmation() {
f, err = os.OpenFile(fpath, os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
return
}
} else {
ColorLog("[WARN] skip create file\n")
return
}
} else {
f, err = os.OpenFile(fpath, os.O_CREATE|os.O_RDWR, 0666)
if err != nil {
ColorLog("[WARN] %v\n", err)
return
}
}
if _, err := f.WriteString(routerStr); err != nil {
ColorLog("[ERRO] Could not write router file to %s\n", fpath)
os.Exit(2)
}
f.Close()
ColorLog("[INFO] router => %s\n", fpath)
formatSourceCode(fpath)
}
const (
HPROSE_ADDFUNCTION = `
// 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}})
`
HPROSE_STRUCT_MODEL_TPL = `package models
{{importTimePkg}}
{{modelStruct}}
`
HPROSE_MODEL_TPL = `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
}
`
HPROSE_CTRL_TPL = `package controllers
import (
"{{pkgPath}}/models"
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/astaxie/beego"
)
// oprations for {{ctrlName}}
type {{ctrlName}}Controller struct {
beego.Controller
}
func (this *{{ctrlName}}Controller) URLMapping() {
this.Mapping("Post", this.Post)
this.Mapping("GetOne", this.GetOne)
this.Mapping("GetAll", this.GetAll)
this.Mapping("Put", this.Put)
this.Mapping("Delete", this.Delete)
}
// @Title Post
// @Description create {{ctrlName}}
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
// @Success 200 {int} models.{{ctrlName}}.Id
// @Failure 403 body is empty
// @router / [post]
func (this *{{ctrlName}}Controller) Post() {
var v models.{{ctrlName}}
json.Unmarshal(this.Ctx.Input.RequestBody, &v)
if id, err := models.Add{{ctrlName}}(&v); err == nil {
this.Data["json"] = map[string]int64{"id": id}
} else {
this.Data["json"] = err.Error()
}
this.ServeJson()
}
// @Title Get
// @Description get {{ctrlName}} by id
// @Param id path string true "The key for staticblock"
// @Success 200 {object} models.{{ctrlName}}
// @Failure 403 :id is empty
// @router /:id [get]
func (this *{{ctrlName}}Controller) GetOne() {
idStr := this.Ctx.Input.Params[":id"]
id, _ := strconv.Atoi(idStr)
v, err := models.Get{{ctrlName}}ById(id)
if err != nil {
this.Data["json"] = err.Error()
} else {
this.Data["json"] = v
}
this.ServeJson()
}
// @Title Get All
// @Description get {{ctrlName}}
// @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.{{ctrlName}}
// @Failure 403
// @router / [get]
func (this *{{ctrlName}}Controller) GetAll() {
var fields []string
var sortby []string
var order []string
var query map[string]string = make(map[string]string)
var limit int64 = 10
var offset int64 = 0
// fields: col1,col2,entity.col3
if v := this.GetString("fields"); v != "" {
fields = strings.Split(v, ",")
}
// limit: 10 (default is 10)
if v, err := this.GetInt("limit"); err == nil {
limit = v
}
// offset: 0 (default is 0)
if v, err := this.GetInt("offset"); err == nil {
offset = v
}
// sortby: col1,col2
if v := this.GetString("sortby"); v != "" {
sortby = strings.Split(v, ",")
}
// order: desc,asc
if v := this.GetString("order"); v != "" {
order = strings.Split(v, ",")
}
// query: k:v,k:v
if v := this.GetString("query"); v != "" {
for _, cond := range strings.Split(v, ",") {
kv := strings.Split(cond, ":")
if len(kv) != 2 {
this.Data["json"] = errors.New("Error: invalid query key/value pair")
this.ServeJson()
return
}
k, v := kv[0], kv[1]
query[k] = v
}
}
l, err := models.GetAll{{ctrlName}}(query, fields, sortby, order, offset, limit)
if err != nil {
this.Data["json"] = err.Error()
} else {
this.Data["json"] = l
}
this.ServeJson()
}
// @Title Update
// @Description update the {{ctrlName}}
// @Param id path string true "The id you want to update"
// @Param body body models.{{ctrlName}} true "body for {{ctrlName}} content"
// @Success 200 {object} models.{{ctrlName}}
// @Failure 403 :id is not int
// @router /:id [put]
func (this *{{ctrlName}}Controller) Put() {
idStr := this.Ctx.Input.Params[":id"]
id, _ := strconv.Atoi(idStr)
v := models.{{ctrlName}}{Id: id}
json.Unmarshal(this.Ctx.Input.RequestBody, &v)
if err := models.Update{{ctrlName}}ById(&v); err == nil {
this.Data["json"] = "OK"
} else {
this.Data["json"] = err.Error()
}
this.ServeJson()
}
// @Title Delete
// @Description delete the {{ctrlName}}
// @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 (this *{{ctrlName}}Controller) Delete() {
idStr := this.Ctx.Input.Params[":id"]
id, _ := strconv.Atoi(idStr)
if err := models.Delete{{ctrlName}}(id); err == nil {
this.Data["json"] = "OK"
} else {
this.Data["json"] = err.Error()
}
this.ServeJson()
}
`
HPROSE_ROUTER_TPL = `// @APIVersion 1.0.0
// @Title beego Test API
// @Description beego has a very cool tools to autogenerate documents for your API
// @Contact astaxie@gmail.com
// @TermsOfServiceUrl http://beego.me/
// @License Apache 2.0
// @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html
package routers
import (
"{{pkgPath}}/controllers"
"github.com/astaxie/beego"
)
func init() {
ns := beego.NewNamespace("/v1",
{{nameSpaces}}
)
beego.AddNamespace(ns)
}
`
HPROSE_NAMESPACE_TPL = `
beego.NSNamespace("/{{nameSpace}}",
beego.NSInclude(
&controllers.{{ctrlName}}Controller{},
),
),
`
)

655
hproseapp.go Normal file
View File

@ -0,0 +1,655 @@
/**********************************************************\
| |
| hprose |
| |
| Official WebSite: http://www.hprose.com/ |
| http://www.hprose.org/ |
| |
\**********************************************************/
/**********************************************************\
* *
* Build rpc application use Hprose base on beego *
* *
* LastModified: Oct 13, 2014 *
* Author: Liu jian <laoliu@lanmv.com> *
* *
\**********************************************************/
package main
import (
"fmt"
"os"
path "path/filepath"
"strings"
)
var isCreateHproseApp = false
var cmdHproseapp = &Command{
// CustomFlags: true,
UsageLine: "hprose [appname]",
Short: "create an rpc application use hprose base on beego framework",
Long: `
create an rpc application use hprose base on beego framework
bee hprose [appname] [-tables=""] [-driver=mysql] [-conn=root:@tcp(127.0.0.1:3306)/test]
-tables: a list of table names separated by ',', default is empty, indicating all tables
-driver: [mysql | postgres | sqlite], the default is mysql
-conn: the connection string used by the driver, the default is ''
e.g. for mysql: root:@tcp(127.0.0.1:3306)/test
e.g. for postgres: postgres://postgres:postgres@127.0.0.1:5432/postgres
if conn is empty will create a example rpc application. otherwise generate rpc application use hprose based on an existing database.
In the current path, will create a folder named [appname]
In the appname folder has the follow struct:
conf
app.conf
controllers
object.go
user.go
routers
router.go
tests
default_test.go
main.go
models
object.go
user.go
## For hprose client URL : http://127.0.0.1:8080/hprose
`,
}
var hproseconf = `appname = {{.Appname}}
httpport = 8080
runmode = dev
autorender = false
copyrequestbody = true
EnableDocs = true
`
var hproseMaingo = `package main
import (
_ "{{.Appname}}/docs"
"{{.Appname}}/models"
_ "{{.Appname}}/routers"
"github.com/hprose/hprose-go/hprose"
"github.com/astaxie/beego"
)
func main() {
if beego.RunMode == "dev" {
beego.DirectoryIndex = true
beego.StaticDir["/swagger"] = "swagger"
}
service := hprose.NewHttpService()
service.AddFunction("AddOne", models.AddOne)
service.AddFunction("GetOne", models.GetOne)
beego.Handler("/hprose", service)
beego.Run()
}
`
var hproseMainconngo = `package main
import (
_ "{{.Appname}}/docs"
"{{.Appname}}/models"
_ "{{.Appname}}/routers"
"github.com/hprose/hprose-go/hprose"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
{{.DriverPkg}}
)
func init() {
orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}")
}
func main() {
if beego.RunMode == "dev" {
beego.DirectoryIndex = true
beego.StaticDir["/swagger"] = "swagger"
}
service := hprose.NewHttpService()
{{HproseFunctionList}}
beego.Handler("/hprose", service)
beego.Run()
}
`
var hproserouter = `// @APIVersion 1.0.0
// @Title beego Test API
// @Description beego has a very cool tools to autogenerate documents for your API
// @Contact astaxie@gmail.com
// @TermsOfServiceUrl http://beego.me/
// @License Apache 2.0
// @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html
package routers
import (
"{{.Appname}}/controllers"
"github.com/astaxie/beego"
)
func init() {
ns := beego.NewNamespace("/v1",
beego.NSNamespace("/object",
beego.NSInclude(
&controllers.ObjectController{},
),
),
beego.NSNamespace("/user",
beego.NSInclude(
&controllers.UserController{},
),
),
)
beego.AddNamespace(ns)
}
`
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 hproseControllers = `package controllers
import (
"{{.Appname}}/models"
"encoding/json"
"github.com/astaxie/beego"
)
// Operations about object
type ObjectController struct {
beego.Controller
}
// @Title create
// @Description create object
// @Param body body models.Object true "The object content"
// @Success 200 {string} models.Object.Id
// @Failure 403 body is empty
// @router / [post]
func (this *ObjectController) Post() {
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
objectid := models.AddOne(ob)
this.Data["json"] = map[string]string{"ObjectId": objectid}
this.ServeJson()
}
// @Title Get
// @Description find object by objectid
// @Param objectId path string true "the objectid you want to get"
// @Success 200 {object} models.Object
// @Failure 403 :objectId is empty
// @router /:objectId [get]
func (this *ObjectController) Get() {
objectId := this.Ctx.Input.Params[":objectId"]
if objectId != "" {
ob, err := models.GetOne(objectId)
if err != nil {
this.Data["json"] = err
} else {
this.Data["json"] = ob
}
}
this.ServeJson()
}
// @Title GetAll
// @Description get all objects
// @Success 200 {object} models.Object
// @Failure 403 :objectId is empty
// @router / [get]
func (this *ObjectController) GetAll() {
obs := models.GetAll()
this.Data["json"] = obs
this.ServeJson()
}
// @Title update
// @Description update the object
// @Param objectId path string true "The objectid you want to update"
// @Param body body models.Object true "The body"
// @Success 200 {object} models.Object
// @Failure 403 :objectId is empty
// @router /:objectId [put]
func (this *ObjectController) Put() {
objectId := this.Ctx.Input.Params[":objectId"]
var ob models.Object
json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
err := models.Update(objectId, ob.Score)
if err != nil {
this.Data["json"] = err
} else {
this.Data["json"] = "update success!"
}
this.ServeJson()
}
// @Title delete
// @Description delete the object
// @Param objectId path string true "The objectId you want to delete"
// @Success 200 {string} delete success!
// @Failure 403 objectId is empty
// @router /:objectId [delete]
func (this *ObjectController) Delete() {
objectId := this.Ctx.Input.Params[":objectId"]
models.Delete(objectId)
this.Data["json"] = "delete success!"
this.ServeJson()
}
`
var hproseControllers2 = `package controllers
import (
"{{.Appname}}/models"
"encoding/json"
"github.com/astaxie/beego"
)
// Operations about Users
type UserController struct {
beego.Controller
}
// @Title createUser
// @Description create users
// @Param body body models.User true "body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router / [post]
func (u *UserController) Post() {
var user models.User
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
uid := models.AddUser(user)
u.Data["json"] = map[string]string{"uid": uid}
u.ServeJson()
}
// @Title Get
// @Description get all Users
// @Success 200 {object} models.User
// @router / [get]
func (u *UserController) GetAll() {
users := models.GetAllUsers()
u.Data["json"] = users
u.ServeJson()
}
// @Title Get
// @Description get user by uid
// @Param uid path string true "The key for staticblock"
// @Success 200 {object} models.User
// @Failure 403 :uid is empty
// @router /:uid [get]
func (u *UserController) Get() {
uid := u.GetString(":uid")
if uid != "" {
user, err := models.GetUser(uid)
if err != nil {
u.Data["json"] = err
} else {
u.Data["json"] = user
}
}
u.ServeJson()
}
// @Title update
// @Description update the user
// @Param uid path string true "The uid you want to update"
// @Param body body models.User true "body for user content"
// @Success 200 {object} models.User
// @Failure 403 :uid is not int
// @router /:uid [put]
func (u *UserController) Put() {
uid := u.GetString(":uid")
if uid != "" {
var user models.User
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
uu, err := models.UpdateUser(uid, &user)
if err != nil {
u.Data["json"] = err
} else {
u.Data["json"] = uu
}
}
u.ServeJson()
}
// @Title delete
// @Description delete the user
// @Param uid path string true "The uid you want to delete"
// @Success 200 {string} delete success!
// @Failure 403 uid is empty
// @router /:uid [delete]
func (u *UserController) Delete() {
uid := u.GetString(":uid")
models.DeleteUser(uid)
u.Data["json"] = "delete success!"
u.ServeJson()
}
// @Title login
// @Description Logs user into the system
// @Param username query string true "The username for login"
// @Param password query string true "The password for login"
// @Success 200 {string} lonin success
// @Failure 403 user not exist
// @router /login [get]
func (u *UserController) Login() {
username := u.GetString("username")
password := u.GetString("password")
if models.Login(username, password) {
u.Data["json"] = "login success"
} else {
u.Data["json"] = "user not exist"
}
u.ServeJson()
}
// @Title logout
// @Description Logs out current logged in user session
// @Success 200 {string} logout success
// @router /logout [get]
func (u *UserController) Logout() {
u.Data["json"] = "logout success"
u.ServeJson()
}
`
var hproseTests = `package test
import (
"net/http"
"net/http/httptest"
"testing"
"runtime"
"path/filepath"
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
. "github.com/smartystreets/goconvey/convey"
)
func init() {
_, file, _, _ := runtime.Caller(1)
apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator))))
beego.TestBeegoInit(apppath)
}
// TestGet is a sample to run an endpoint test
func TestGet(t *testing.T) {
r, _ := http.NewRequest("GET", "/v1/object", nil)
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
beego.Trace("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String())
Convey("Subject: Test Station Endpoint\n", t, func() {
Convey("Status Code Should Be 200", func() {
So(w.Code, ShouldEqual, 200)
})
Convey("The Result Should Not Be Empty", func() {
So(w.Body.Len(), ShouldBeGreaterThan, 0)
})
})
}
`
var hproseAddFunctions = []string{}
func init() {
cmdHproseapp.Run = createhprose
cmdHproseapp.Flag.Var(&tables, "tables", "specify tables to generate model")
cmdHproseapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.")
cmdHproseapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance")
}
func createhprose(cmd *Command, args []string) int {
isCreateHproseApp = true
curpath, _ := os.Getwd()
if len(args) > 1 {
cmd.Flag.Parse(args[1:])
}
apppath, packpath, err := checkEnv(args[0])
if err != nil {
fmt.Println(err)
os.Exit(2)
}
if driver == "" {
driver = "mysql"
}
if conn == "" {
}
os.MkdirAll(apppath, 0755)
fmt.Println("create app folder:", apppath)
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Println("create conf:", path.Join(apppath, "conf"))
os.Mkdir(path.Join(apppath, "controllers"), 0755)
fmt.Println("create controllers:", path.Join(apppath, "controllers"))
os.Mkdir(path.Join(apppath, "docs"), 0755)
fmt.Println("create docs:", path.Join(apppath, "docs"))
os.Mkdir(path.Join(apppath, "tests"), 0755)
fmt.Println("create tests:", path.Join(apppath, "tests"))
fmt.Println("create conf app.conf:", path.Join(apppath, "conf", "app.conf"))
writetofile(path.Join(apppath, "conf", "app.conf"),
strings.Replace(hproseconf, "{{.Appname}}", args[0], -1))
if conn != "" {
ColorLog("[INFO] Using '%s' as 'driver'\n", driver)
ColorLog("[INFO] Using '%s' as 'conn'\n", conn)
ColorLog("[INFO] Using '%s' as 'tables'\n", tables)
generateHproseAppcode(string(driver), string(conn), "3", string(tables), path.Join(curpath, args[0]))
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
maingoContent := strings.Replace(hproseMainconngo, "{{.Appname}}", packpath, -1)
maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -1)
maingoContent = strings.Replace(maingoContent, "{{HproseFunctionList}}", strings.Join(hproseAddFunctions, ""), -1)
if driver == "mysql" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/go-sql-driver/mysql"`, -1)
} else if driver == "postgres" {
maingoContent = strings.Replace(maingoContent, "{{.DriverPkg}}", `_ "github.com/lib/pq"`, -1)
}
writetofile(path.Join(apppath, "main.go"),
strings.Replace(
maingoContent,
"{{.conn}}",
conn.String(),
-1,
),
)
} else {
os.Mkdir(path.Join(apppath, "models"), 0755)
fmt.Println("create models:", path.Join(apppath, "models"))
os.Mkdir(path.Join(apppath, "routers"), 0755)
fmt.Println(path.Join(apppath, "routers") + string(path.Separator))
fmt.Println("create controllers object.go:", path.Join(apppath, "controllers", "object.go"))
writetofile(path.Join(apppath, "controllers", "object.go"),
strings.Replace(hproseControllers, "{{.Appname}}", packpath, -1))
fmt.Println("create controllers user.go:", path.Join(apppath, "controllers", "user.go"))
writetofile(path.Join(apppath, "controllers", "user.go"),
strings.Replace(hproseControllers2, "{{.Appname}}", packpath, -1))
fmt.Println("create tests default.go:", path.Join(apppath, "tests", "default_test.go"))
writetofile(path.Join(apppath, "tests", "default_test.go"),
strings.Replace(hproseTests, "{{.Appname}}", packpath, -1))
fmt.Println("create routers router.go:", path.Join(apppath, "routers", "router.go"))
writetofile(path.Join(apppath, "routers", "router.go"),
strings.Replace(hproserouter, "{{.Appname}}", packpath, -1))
fmt.Println("create models object.go:", path.Join(apppath, "models", "object.go"))
writetofile(path.Join(apppath, "models", "object.go"), apiModels)
fmt.Println("create models user.go:", path.Join(apppath, "models", "user.go"))
writetofile(path.Join(apppath, "models", "user.go"), apiModels2)
fmt.Println("create docs doc.go:", path.Join(apppath, "docs", "doc.go"))
writetofile(path.Join(apppath, "docs", "doc.go"), "package docs")
fmt.Println("create main.go:", path.Join(apppath, "main.go"))
writetofile(path.Join(apppath, "main.go"),
strings.Replace(hproseMaingo, "{{.Appname}}", packpath, -1))
}
return 0
}