// 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 main import ( "fmt" "os" path "path/filepath" "strings" ) var cmdApiapp = &Command{ // CustomFlags: true, UsageLine: "api [appname]", Short: "create an API beego application", Long: ` Create an API beego application. bee api [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] (default: 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' argument is empty, bee api creates an example API application, when 'conn' argument is provided, bee api generates an API application based on the existing database. The command 'api' creates a folder named [appname] and inside the folder deploy the following files/directories structure: ├── conf │ └── app.conf ├── controllers │ └── object.go │ └── user.go ├── routers │ └── router.go ├── tests │ └── default_test.go ├── main.go └── models └── object.go └── user.go `, } var apiconf = `appname = {{.Appname}} httpport = 8080 runmode = dev autorender = false copyrequestbody = true EnableDocs = true ` var apiMaingo = `package main import ( _ "{{.Appname}}/routers" "github.com/astaxie/beego" ) func main() { if beego.BConfig.RunMode == "dev" { beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" } beego.Run() } ` var apiMainconngo = `package main import ( _ "{{.Appname}}/routers" "github.com/astaxie/beego" "github.com/astaxie/beego/orm" {{.DriverPkg}} ) func init() { orm.RegisterDataBase("default", "{{.DriverName}}", "{{.conn}}") } func main() { if beego.BConfig.RunMode == "dev" { beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" } beego.Run() } ` var apirouter = `// @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 apiModels = `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 apiModels2 = `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 apiControllers = `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 (o *ObjectController) Post() { var ob models.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) objectid := models.AddOne(ob) o.Data["json"] = map[string]string{"ObjectId": objectid} o.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 (o *ObjectController) Get() { objectId := o.Ctx.Input.Param(":objectId") if objectId != "" { ob, err := models.GetOne(objectId) if err != nil { o.Data["json"] = err.Error() } else { o.Data["json"] = ob } } o.ServeJSON() } // @Title GetAll // @Description get all objects // @Success 200 {object} models.Object // @Failure 403 :objectId is empty // @router / [get] func (o *ObjectController) GetAll() { obs := models.GetAll() o.Data["json"] = obs o.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 (o *ObjectController) Put() { objectId := o.Ctx.Input.Param(":objectId") var ob models.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) err := models.Update(objectId, ob.Score) if err != nil { o.Data["json"] = err.Error() } else { o.Data["json"] = "update success!" } o.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 (o *ObjectController) Delete() { objectId := o.Ctx.Input.Param(":objectId") models.Delete(objectId) o.Data["json"] = "delete success!" o.ServeJSON() } ` var apiControllers2 = `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 GetAll // @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.Error() } 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.Error() } 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} login 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 apiTests = `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) }) }) } ` func init() { cmdApiapp.Run = createapi cmdApiapp.Flag.Var(&tables, "tables", "specify tables to generate model") cmdApiapp.Flag.Var(&driver, "driver", "database driver: mysql, postgresql, etc.") cmdApiapp.Flag.Var(&conn, "conn", "connection string used by the driver to connect to a database instance") } func createapi(cmd *Command, args []string) int { ShowShortVersionBanner() w := NewColorWriter(os.Stdout) if len(args) < 1 { ColorLog("[ERRO] Argument [appname] is missing\n") os.Exit(2) } 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 == "" { } ColorLog("[INFO] Creating API...\n") os.MkdirAll(apppath, 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m") os.Mkdir(path.Join(apppath, "conf"), 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m") os.Mkdir(path.Join(apppath, "controllers"), 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers"), "\x1b[0m") os.Mkdir(path.Join(apppath, "tests"), 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests"), "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf", "app.conf"), "\x1b[0m") WriteToFile(path.Join(apppath, "conf", "app.conf"), strings.Replace(apiconf, "{{.Appname}}", path.Base(args[0]), -1)) if conn != "" { fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") maingoContent := strings.Replace(apiMainconngo, "{{.Appname}}", packpath, -1) maingoContent = strings.Replace(maingoContent, "{{.DriverName}}", string(driver), -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, ), ) ColorLog("[INFO] Using '%s' as 'driver'\n", driver) ColorLog("[INFO] Using '%s' as 'conn'\n", conn) ColorLog("[INFO] Using '%s' as 'tables'\n", tables) generateAppcode(string(driver), string(conn), "3", string(tables), apppath) } else { os.Mkdir(path.Join(apppath, "models"), 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models"), "\x1b[0m") os.Mkdir(path.Join(apppath, "routers"), 0755) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers")+string(path.Separator), "\x1b[0m") fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "object.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "controllers", "object.go"), strings.Replace(apiControllers, "{{.Appname}}", packpath, -1)) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "controllers", "user.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "controllers", "user.go"), strings.Replace(apiControllers2, "{{.Appname}}", packpath, -1)) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "tests", "default_test.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "tests", "default_test.go"), strings.Replace(apiTests, "{{.Appname}}", packpath, -1)) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "routers", "router.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "routers", "router.go"), strings.Replace(apirouter, "{{.Appname}}", packpath, -1)) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "object.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "object.go"), apiModels) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "models", "user.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "models", "user.go"), apiModels2) fmt.Fprintf(w, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "main.go"), "\x1b[0m") WriteToFile(path.Join(apppath, "main.go"), strings.Replace(apiMaingo, "{{.Appname}}", packpath, -1)) } ColorLog("[SUCC] New API successfully created!\n") return 0 } func checkEnv(appname string) (apppath, packpath string, err error) { gps := GetGOPATHs() if len(gps) == 0 { ColorLog("[ERRO] Fail to start[ %s ]\n", "GOPATH environment variable is not set or empty") os.Exit(2) } currpath, _ := os.Getwd() currpath = path.Join(currpath, appname) for _, gpath := range gps { gsrcpath := path.Join(gpath, "src") if strings.HasPrefix(currpath, gsrcpath) { return currpath, currpath[len(gsrcpath)+1:], nil } } // In case of multiple paths in the GOPATH, by default // we use the first path gopath := gps[0] ColorLog("[%s]You current workdir is not a $GOPATH/src, bee will create the application in GOPATH: %s\n", WARN, gopath) Debugf("GOPATH: %s", gopath) gosrcpath := path.Join(gopath, "src") apppath = path.Join(gosrcpath, appname) if _, e := os.Stat(apppath); os.IsNotExist(e) == false { err = fmt.Errorf("Cannot create application without removing '%s' first.", apppath) ColorLog("[ERRO] Path '%s' already exists\n", apppath) return } packpath = strings.Join(strings.Split(apppath[len(gosrcpath)+1:], string(path.Separator)), "/") return }