├── .dockerignore ├── .gitignore ├── Dockerfile ├── Makefile ├── README.md ├── controllers ├── .gitkeep ├── account_name.go ├── email.go └── user.go ├── db ├── database.db ├── db.go └── pagination.go ├── docs ├── .gitkeep ├── account_name.apib ├── email.apib ├── index.apib └── user.apib ├── glide.lock ├── glide.yaml ├── helper ├── field.go └── field_test.go ├── main.go ├── middleware └── set_db.go ├── models ├── .gitkeep ├── accountname.go ├── email.go └── user.go ├── router └── router.go ├── server └── server.go └── version ├── version.go └── version_test.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .git 3 | README.md 4 | 5 | go-api-sokushukai 6 | vendor 7 | 8 | glide 9 | bin 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin/ 3 | 4 | /go-api-sokushukai 5 | /vendor 6 | 7 | /glide 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.4 2 | 3 | ENV GOPATH /go 4 | COPY . /go/src/github.com/shimastripe/go-api-sokushukai 5 | RUN apk add --no-cache --update --virtual=build-deps g++ curl git go make mercurial \ 6 | && cd /go/src/github.com/shimastripe/go-api-sokushukai \ 7 | && make \ 8 | && apk del build-deps 9 | 10 | WORKDIR /go/src/github.com/shimastripe/go-api-sokushukai 11 | EXPOSE 8080 12 | CMD ["bin/server"] 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BINARY := server 2 | LDFLAGS := -ldflags="-s -w" 3 | 4 | GLIDE_VERSION := 0.11.1 5 | 6 | .DEFAULT_GOAL := bin/server 7 | 8 | bin/server: deps 9 | go build $(LDFLAGS) -o bin/server 10 | 11 | .PHONY: clean 12 | clean: 13 | rm -rf bin/* 14 | rm -rf vendor/* 15 | 16 | .PHONY: deps 17 | deps: glide 18 | ./glide install 19 | 20 | glide: 21 | ifeq ($(shell uname),Darwin) 22 | curl -fL https://github.com/Masterminds/glide/releases/download/v$(GLIDE_VERSION)/glide-v$(GLIDE_VERSION)-darwin-amd64.zip -o glide.zip 23 | unzip glide.zip 24 | mv ./darwin-amd64/glide glide 25 | rm -fr ./darwin-amd64 26 | rm ./glide.zip 27 | else 28 | curl -fL https://github.com/Masterminds/glide/releases/download/v$(GLIDE_VERSION)/glide-v$(GLIDE_VERSION)-linux-amd64.zip -o glide.zip 29 | unzip glide.zip 30 | mv ./linux-amd64/glide glide 31 | rm -fr ./linux-amd64 32 | rm ./glide.zip 33 | endif 34 | 35 | .PHONY: update-deps 36 | update-deps: glide 37 | ./glide update 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # API Server 2 | 3 | Simple Rest API using gin(framework) & gorm(orm) 4 | 5 | Used in [wantedly-house event](http://qiita.com/shimastripe/items/e9b0e1f8f8d77b89373f). 6 | 7 | ## How to make 8 | 9 | this was automatically generated by [apig](https://github.com/wantedly/apig). 10 | 11 | ```bash 12 | $ apig new go-api-sokushukai -u shimastripe 13 | $ cd $GOPATH/src/github.com/shimastripe/go-api-sokushukai 14 | Add models/XX.go 15 | $ apig gen 16 | ``` 17 | 18 | ## Endpoint list 19 | 20 | ### Emails Resource 21 | 22 | ``` 23 | GET /api/emails 24 | GET /api/emails/:id 25 | POST /api/emails 26 | PUT /api/emails/:id 27 | DELETE /api/emails/:id 28 | ``` 29 | 30 | ### AccountNames Resource 31 | 32 | ``` 33 | GET /api/accountnames 34 | GET /api/accountnames/:id 35 | POST /api/accountnames 36 | PUT /api/accountnames/:id 37 | DELETE /api/accountnames/:id 38 | ``` 39 | 40 | ### Users Resource 41 | 42 | ``` 43 | GET /api/users 44 | GET /api/users/:id 45 | POST /api/users 46 | PUT /api/users/:id 47 | DELETE /api/users/:id 48 | ``` 49 | 50 | server runs at http://localhost:8080 51 | -------------------------------------------------------------------------------- /controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shimastripe/go-api-sokushukai/c9a3bf2ecb283112a16a846005c414067b23184a/controllers/.gitkeep -------------------------------------------------------------------------------- /controllers/account_name.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | dbpkg "github.com/shimastripe/go-api-sokushukai/db" 7 | "github.com/shimastripe/go-api-sokushukai/helper" 8 | "github.com/shimastripe/go-api-sokushukai/models" 9 | "github.com/shimastripe/go-api-sokushukai/version" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func GetAccountNames(c *gin.Context) { 15 | ver, err := version.New(c) 16 | if err != nil { 17 | c.JSON(400, gin.H{"error": err.Error()}) 18 | return 19 | } 20 | 21 | preloads := c.DefaultQuery("preloads", "") 22 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 23 | 24 | pagination := dbpkg.Pagination{} 25 | db, err := pagination.Paginate(c) 26 | 27 | if err != nil { 28 | c.JSON(400, gin.H{"error": err.Error()}) 29 | return 30 | } 31 | 32 | db = dbpkg.SetPreloads(preloads, db) 33 | 34 | var accountnames []models.AccountName 35 | if err := db.Select("*").Find(&accountnames).Error; err != nil { 36 | c.JSON(500, gin.H{"error": err.Error()}) 37 | return 38 | } 39 | 40 | // paging 41 | var index int 42 | if len(accountnames) < 1 { 43 | index = 0 44 | } else { 45 | index = int(accountnames[len(accountnames)-1].ID) 46 | } 47 | pagination.SetHeaderLink(c, index) 48 | 49 | if version.Range(ver, "<", "1.0.0") { 50 | // conditional branch by version. 51 | // this version < 1.0.0 !! 52 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 53 | return 54 | } 55 | 56 | fieldMap := []map[string]interface{}{} 57 | for key, _ := range accountnames { 58 | fieldMap = append(fieldMap, helper.FieldToMap(accountnames[key], fields)) 59 | } 60 | c.JSON(200, fieldMap) 61 | } 62 | 63 | func GetAccountName(c *gin.Context) { 64 | ver, err := version.New(c) 65 | if err != nil { 66 | c.JSON(400, gin.H{"error": err.Error()}) 67 | return 68 | } 69 | 70 | id := c.Params.ByName("id") 71 | preloads := c.DefaultQuery("preloads", "") 72 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 73 | 74 | db := dbpkg.DBInstance(c) 75 | db = dbpkg.SetPreloads(preloads, db) 76 | 77 | var accountname models.AccountName 78 | if err := db.Select("*").First(&accountname, id).Error; err != nil { 79 | content := gin.H{"error": "accountname with id#" + id + " not found"} 80 | c.JSON(404, content) 81 | return 82 | } 83 | 84 | if version.Range(ver, "<", "1.0.0") { 85 | // conditional branch by version. 86 | // this version < 1.0.0 !! 87 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 88 | return 89 | } 90 | 91 | fieldMap := helper.FieldToMap(accountname, fields) 92 | c.JSON(200, fieldMap) 93 | } 94 | 95 | func CreateAccountName(c *gin.Context) { 96 | ver, err := version.New(c) 97 | if err != nil { 98 | c.JSON(400, gin.H{"error": err.Error()}) 99 | return 100 | } 101 | 102 | db := dbpkg.DBInstance(c) 103 | var accountname models.AccountName 104 | c.Bind(&accountname) 105 | if db.Create(&accountname).Error != nil { 106 | content := gin.H{"error": err.Error()} 107 | c.JSON(500, content) 108 | return 109 | } 110 | 111 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 112 | // conditional branch by version. 113 | // 1.0.0 <= this version < 2.0.0 !! 114 | } 115 | 116 | c.JSON(201, accountname) 117 | } 118 | 119 | func UpdateAccountName(c *gin.Context) { 120 | ver, err := version.New(c) 121 | if err != nil { 122 | c.JSON(400, gin.H{"error": err.Error()}) 123 | return 124 | } 125 | 126 | db := dbpkg.DBInstance(c) 127 | id := c.Params.ByName("id") 128 | var accountname models.AccountName 129 | if db.First(&accountname, id).Error != nil { 130 | content := gin.H{"error": "accountname with id#" + id + " not found"} 131 | c.JSON(404, content) 132 | return 133 | } 134 | c.Bind(&accountname) 135 | db.Save(&accountname) 136 | 137 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 138 | // conditional branch by version. 139 | // 1.0.0 <= this version < 2.0.0 !! 140 | } 141 | 142 | c.JSON(200, accountname) 143 | } 144 | 145 | func DeleteAccountName(c *gin.Context) { 146 | ver, err := version.New(c) 147 | if err != nil { 148 | c.JSON(400, gin.H{"error": err.Error()}) 149 | return 150 | } 151 | 152 | db := dbpkg.DBInstance(c) 153 | id := c.Params.ByName("id") 154 | var accountname models.AccountName 155 | if db.First(&accountname, id).Error != nil { 156 | content := gin.H{"error": "accountname with id#" + id + " not found"} 157 | c.JSON(404, content) 158 | return 159 | } 160 | db.Delete(&accountname) 161 | 162 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 163 | // conditional branch by version. 164 | // 1.0.0 <= this version < 2.0.0 !! 165 | } 166 | 167 | c.Writer.WriteHeader(http.StatusNoContent) 168 | } 169 | -------------------------------------------------------------------------------- /controllers/email.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | dbpkg "github.com/shimastripe/go-api-sokushukai/db" 7 | "github.com/shimastripe/go-api-sokushukai/helper" 8 | "github.com/shimastripe/go-api-sokushukai/models" 9 | "github.com/shimastripe/go-api-sokushukai/version" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func GetEmails(c *gin.Context) { 15 | ver, err := version.New(c) 16 | if err != nil { 17 | c.JSON(400, gin.H{"error": err.Error()}) 18 | return 19 | } 20 | 21 | preloads := c.DefaultQuery("preloads", "") 22 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 23 | 24 | pagination := dbpkg.Pagination{} 25 | db, err := pagination.Paginate(c) 26 | 27 | if err != nil { 28 | c.JSON(400, gin.H{"error": err.Error()}) 29 | return 30 | } 31 | 32 | db = dbpkg.SetPreloads(preloads, db) 33 | 34 | var emails []models.Email 35 | if err := db.Select("*").Find(&emails).Error; err != nil { 36 | c.JSON(500, gin.H{"error": err.Error()}) 37 | return 38 | } 39 | 40 | // paging 41 | var index int 42 | if len(emails) < 1 { 43 | index = 0 44 | } else { 45 | index = int(emails[len(emails)-1].ID) 46 | } 47 | pagination.SetHeaderLink(c, index) 48 | 49 | if version.Range(ver, "<", "1.0.0") { 50 | // conditional branch by version. 51 | // this version < 1.0.0 !! 52 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 53 | return 54 | } 55 | 56 | fieldMap := []map[string]interface{}{} 57 | for key, _ := range emails { 58 | fieldMap = append(fieldMap, helper.FieldToMap(emails[key], fields)) 59 | } 60 | c.JSON(200, fieldMap) 61 | } 62 | 63 | func GetEmail(c *gin.Context) { 64 | ver, err := version.New(c) 65 | if err != nil { 66 | c.JSON(400, gin.H{"error": err.Error()}) 67 | return 68 | } 69 | 70 | id := c.Params.ByName("id") 71 | preloads := c.DefaultQuery("preloads", "") 72 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 73 | 74 | db := dbpkg.DBInstance(c) 75 | db = dbpkg.SetPreloads(preloads, db) 76 | 77 | var email models.Email 78 | if err := db.Select("*").First(&email, id).Error; err != nil { 79 | content := gin.H{"error": "email with id#" + id + " not found"} 80 | c.JSON(404, content) 81 | return 82 | } 83 | 84 | if version.Range(ver, "<", "1.0.0") { 85 | // conditional branch by version. 86 | // this version < 1.0.0 !! 87 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 88 | return 89 | } 90 | 91 | fieldMap := helper.FieldToMap(email, fields) 92 | c.JSON(200, fieldMap) 93 | } 94 | 95 | func CreateEmail(c *gin.Context) { 96 | ver, err := version.New(c) 97 | if err != nil { 98 | c.JSON(400, gin.H{"error": err.Error()}) 99 | return 100 | } 101 | 102 | db := dbpkg.DBInstance(c) 103 | var email models.Email 104 | c.Bind(&email) 105 | if db.Create(&email).Error != nil { 106 | content := gin.H{"error": err.Error()} 107 | c.JSON(500, content) 108 | return 109 | } 110 | 111 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 112 | // conditional branch by version. 113 | // 1.0.0 <= this version < 2.0.0 !! 114 | } 115 | 116 | c.JSON(201, email) 117 | } 118 | 119 | func UpdateEmail(c *gin.Context) { 120 | ver, err := version.New(c) 121 | if err != nil { 122 | c.JSON(400, gin.H{"error": err.Error()}) 123 | return 124 | } 125 | 126 | db := dbpkg.DBInstance(c) 127 | id := c.Params.ByName("id") 128 | var email models.Email 129 | if db.First(&email, id).Error != nil { 130 | content := gin.H{"error": "email with id#" + id + " not found"} 131 | c.JSON(404, content) 132 | return 133 | } 134 | c.Bind(&email) 135 | db.Save(&email) 136 | 137 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 138 | // conditional branch by version. 139 | // 1.0.0 <= this version < 2.0.0 !! 140 | } 141 | 142 | c.JSON(200, email) 143 | } 144 | 145 | func DeleteEmail(c *gin.Context) { 146 | ver, err := version.New(c) 147 | if err != nil { 148 | c.JSON(400, gin.H{"error": err.Error()}) 149 | return 150 | } 151 | 152 | db := dbpkg.DBInstance(c) 153 | id := c.Params.ByName("id") 154 | var email models.Email 155 | if db.First(&email, id).Error != nil { 156 | content := gin.H{"error": "email with id#" + id + " not found"} 157 | c.JSON(404, content) 158 | return 159 | } 160 | db.Delete(&email) 161 | 162 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 163 | // conditional branch by version. 164 | // 1.0.0 <= this version < 2.0.0 !! 165 | } 166 | 167 | c.Writer.WriteHeader(http.StatusNoContent) 168 | } 169 | -------------------------------------------------------------------------------- /controllers/user.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "net/http" 5 | 6 | dbpkg "github.com/shimastripe/go-api-sokushukai/db" 7 | "github.com/shimastripe/go-api-sokushukai/helper" 8 | "github.com/shimastripe/go-api-sokushukai/models" 9 | "github.com/shimastripe/go-api-sokushukai/version" 10 | 11 | "github.com/gin-gonic/gin" 12 | ) 13 | 14 | func GetUsers(c *gin.Context) { 15 | ver, err := version.New(c) 16 | if err != nil { 17 | c.JSON(400, gin.H{"error": err.Error()}) 18 | return 19 | } 20 | 21 | preloads := c.DefaultQuery("preloads", "") 22 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 23 | 24 | pagination := dbpkg.Pagination{} 25 | db, err := pagination.Paginate(c) 26 | 27 | if err != nil { 28 | c.JSON(400, gin.H{"error": err.Error()}) 29 | return 30 | } 31 | 32 | db = dbpkg.SetPreloads(preloads, db) 33 | 34 | var users []models.User 35 | if err := db.Select("*").Find(&users).Error; err != nil { 36 | c.JSON(500, gin.H{"error": err.Error()}) 37 | return 38 | } 39 | 40 | // paging 41 | var index int 42 | if len(users) < 1 { 43 | index = 0 44 | } else { 45 | index = int(users[len(users)-1].ID) 46 | } 47 | pagination.SetHeaderLink(c, index) 48 | 49 | if version.Range(ver, "<", "1.0.0") { 50 | // conditional branch by version. 51 | // this version < 1.0.0 !! 52 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 53 | return 54 | } 55 | 56 | fieldMap := []map[string]interface{}{} 57 | for key, _ := range users { 58 | fieldMap = append(fieldMap, helper.FieldToMap(users[key], fields)) 59 | } 60 | _, ok := c.GetQuery("pretty") 61 | if ok { 62 | c.IndentedJSON(200, fieldMap) 63 | } else { 64 | c.JSON(200, fieldMap) 65 | } 66 | } 67 | 68 | func GetUser(c *gin.Context) { 69 | ver, err := version.New(c) 70 | if err != nil { 71 | c.JSON(400, gin.H{"error": err.Error()}) 72 | return 73 | } 74 | 75 | id := c.Params.ByName("id") 76 | preloads := c.DefaultQuery("preloads", "") 77 | fields := helper.ParseFields(c.DefaultQuery("fields", "*")) 78 | 79 | db := dbpkg.DBInstance(c) 80 | db = dbpkg.SetPreloads(preloads, db) 81 | 82 | var user models.User 83 | if err := db.Select("*").First(&user, id).Error; err != nil { 84 | content := gin.H{"error": "user with id#" + id + " not found"} 85 | c.JSON(404, content) 86 | return 87 | } 88 | 89 | if version.Range(ver, "<", "1.0.0") { 90 | // conditional branch by version. 91 | // this version < 1.0.0 !! 92 | c.JSON(400, gin.H{"error": "this version (< 1.0.0) is not supported!"}) 93 | return 94 | } 95 | 96 | fieldMap := helper.FieldToMap(user, fields) 97 | _, ok := c.GetQuery("pretty") 98 | if ok { 99 | c.IndentedJSON(200, fieldMap) 100 | } else { 101 | c.JSON(200, fieldMap) 102 | } 103 | } 104 | 105 | func CreateUser(c *gin.Context) { 106 | ver, err := version.New(c) 107 | if err != nil { 108 | c.JSON(400, gin.H{"error": err.Error()}) 109 | return 110 | } 111 | 112 | db := dbpkg.DBInstance(c) 113 | var user models.User 114 | c.Bind(&user) 115 | if db.Create(&user).Error != nil { 116 | content := gin.H{"error": err.Error()} 117 | c.JSON(500, content) 118 | return 119 | } 120 | 121 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 122 | // conditional branch by version. 123 | // 1.0.0 <= this version < 2.0.0 !! 124 | } 125 | 126 | c.JSON(201, user) 127 | } 128 | 129 | func UpdateUser(c *gin.Context) { 130 | ver, err := version.New(c) 131 | if err != nil { 132 | c.JSON(400, gin.H{"error": err.Error()}) 133 | return 134 | } 135 | 136 | db := dbpkg.DBInstance(c) 137 | id := c.Params.ByName("id") 138 | var user models.User 139 | if db.First(&user, id).Error != nil { 140 | content := gin.H{"error": "user with id#" + id + " not found"} 141 | c.JSON(404, content) 142 | return 143 | } 144 | c.Bind(&user) 145 | db.Save(&user) 146 | 147 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 148 | // conditional branch by version. 149 | // 1.0.0 <= this version < 2.0.0 !! 150 | } 151 | 152 | c.JSON(200, user) 153 | } 154 | 155 | func DeleteUser(c *gin.Context) { 156 | ver, err := version.New(c) 157 | if err != nil { 158 | c.JSON(400, gin.H{"error": err.Error()}) 159 | return 160 | } 161 | 162 | db := dbpkg.DBInstance(c) 163 | id := c.Params.ByName("id") 164 | var user models.User 165 | if db.First(&user, id).Error != nil { 166 | content := gin.H{"error": "user with id#" + id + " not found"} 167 | c.JSON(404, content) 168 | return 169 | } 170 | db.Delete(&user) 171 | 172 | if version.Range("1.0.0", "<=", ver) && version.Range(ver, "<", "2.0.0") { 173 | // conditional branch by version. 174 | // 1.0.0 <= this version < 2.0.0 !! 175 | } 176 | 177 | c.Writer.WriteHeader(http.StatusNoContent) 178 | } 179 | -------------------------------------------------------------------------------- /db/database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shimastripe/go-api-sokushukai/c9a3bf2ecb283112a16a846005c414067b23184a/db/database.db -------------------------------------------------------------------------------- /db/db.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/shimastripe/go-api-sokushukai/models" 10 | 11 | "github.com/gin-gonic/gin" 12 | "github.com/jinzhu/gorm" 13 | _ "github.com/jinzhu/gorm/dialects/sqlite" 14 | "github.com/serenize/snaker" 15 | ) 16 | 17 | func Connect() *gorm.DB { 18 | dir := filepath.Dir("db/database.db") 19 | db, err := gorm.Open("sqlite3", dir+"/database.db") 20 | if err != nil { 21 | log.Fatalf("Got error when connect database, the error is '%v'", err) 22 | } 23 | db.LogMode(false) 24 | if gin.IsDebugging(){ 25 | db.LogMode(true) 26 | } 27 | 28 | if os.Getenv("AUTOMIGRATE") == "true" { 29 | db.AutoMigrate( 30 | &models.Email{}, 31 | &models.AccountName{}, 32 | &models.User{}, 33 | ) 34 | } 35 | return db 36 | } 37 | 38 | func DBInstance(c *gin.Context) *gorm.DB { 39 | return c.MustGet("DB").(*gorm.DB) 40 | } 41 | 42 | func SetPreloads(preloads string, db *gorm.DB) *gorm.DB { 43 | if preloads == "" { 44 | return db 45 | } 46 | 47 | for _, preload := range strings.Split(preloads, ",") { 48 | var a []string 49 | 50 | for _, s := range strings.Split(preload, ".") { 51 | a = append(a, snaker.SnakeToCamel(s)) 52 | } 53 | 54 | db = db.Preload(strings.Join(a, ".")) 55 | } 56 | 57 | return db 58 | } 59 | -------------------------------------------------------------------------------- /db/pagination.go: -------------------------------------------------------------------------------- 1 | package db 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "math" 7 | "strconv" 8 | 9 | "github.com/gin-gonic/gin" 10 | "github.com/jinzhu/gorm" 11 | _ "github.com/jinzhu/gorm/dialects/sqlite" 12 | ) 13 | 14 | type Pagination struct { 15 | Limit int 16 | Page int 17 | Last_ID int 18 | Order string 19 | } 20 | 21 | func (p *Pagination) Paginate(c *gin.Context) (*gorm.DB, error) { 22 | db := DBInstance(c) 23 | limit_query := c.DefaultQuery("limit", "25") 24 | page_query := c.DefaultQuery("page", "1") 25 | last_id_query := c.Query("last_id") 26 | p.Order = c.DefaultQuery("order", "desc") 27 | 28 | limit, err := strconv.Atoi(limit_query) 29 | if err != nil { 30 | return db, errors.New("invalid parameter.") 31 | } 32 | p.Limit = int(math.Max(1, math.Min(10000, float64(limit)))) 33 | 34 | if last_id_query != "" { 35 | // pagination 1 36 | last_id, err := strconv.Atoi(last_id_query) 37 | if err != nil { 38 | return db, errors.New("invalid parameter.") 39 | } 40 | p.Last_ID = int(math.Max(0, float64(last_id))) 41 | if p.Order == "asc" { 42 | return db.Where("id > ?", p.Last_ID).Limit(p.Limit).Order("id asc"), nil 43 | } else { 44 | return db.Where("id < ?", p.Last_ID).Limit(p.Limit).Order("id desc"), nil 45 | } 46 | } 47 | 48 | // pagination 2 49 | page, err := strconv.Atoi(page_query) 50 | if err != nil { 51 | return db, errors.New("invalid parameter.") 52 | } 53 | p.Page = int(math.Max(1, float64(page))) 54 | return db.Offset(limit * (p.Page - 1)).Limit(p.Limit), nil 55 | } 56 | 57 | func (p *Pagination) SetHeaderLink(c *gin.Context, index int) { 58 | var link string 59 | if p.Last_ID != 0 { 60 | link = fmt.Sprintf("; rel=\"next\"", c.Request.Host, c.Request.URL.Path, p.Limit, index, p.Order) 61 | } else { 62 | if p.Page == 1 { 63 | link = fmt.Sprintf("; rel=\"next\"", c.Request.Host, c.Request.URL.Path, p.Limit, p.Page+1) 64 | } else { 65 | link = fmt.Sprintf("; rel=\"next\",; rel=\"prev\"", c.Request.Host, c.Request.URL.Path, p.Limit, p.Page+1, c.Request.Host, c.Request.URL.Path, p.Limit, p.Page-1) 66 | } 67 | } 68 | c.Header("Link", link) 69 | } 70 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shimastripe/go-api-sokushukai/c9a3bf2ecb283112a16a846005c414067b23184a/docs/.gitkeep -------------------------------------------------------------------------------- /docs/account_name.apib: -------------------------------------------------------------------------------- 1 | # Group AccountNames 2 | Welcome to the accountnames API. This API provides access to the accountnames service. 3 | 4 | ## accountnames [/accountnames] 5 | 6 | ### Create accountname [POST] 7 | 8 | Create a new accountname 9 | 10 | + Request accountnames (application/json; charset=utf-8) 11 | + Headers 12 | 13 | Accept: application/vnd.shimastripe+json 14 | + Attributes 15 | 16 | + user_id: 1 (number) 17 | + name: NAME (string) 18 | + user (users) 19 | 20 | + Response 201 (application/json; charset=utf-8) 21 | + Attributes (accountnames) 22 | 23 | ### Get accountnames [GET] 24 | 25 | Returns accountname list. 26 | 27 | + Request accountnames (application/json; charset=utf-8) 28 | + Headers 29 | 30 | Accept: application/vnd.shimastripe+json 31 | 32 | + Response 200 (application/json; charset=utf-8) 33 | + Attributes (array, fixed) 34 | + (accountnames) 35 | 36 | ## accountname details [/accountnames/{id}] 37 | 38 | + Parameters 39 | + id: `1` (enum[string]) - The ID of the desired accountname. 40 | + Members 41 | + `1` 42 | + `2` 43 | + `3` 44 | 45 | ### Get accountname [GET] 46 | 47 | Returns accountname. 48 | 49 | + Request accountnames (application/json; charset=utf-8) 50 | + Headers 51 | 52 | Accept: application/vnd.shimastripe+json 53 | 54 | + Response 200 (application/json; charset=utf-8) 55 | + Attributes (accountnames, fixed) 56 | 57 | ### Update accountname [PUT] 58 | 59 | Update accountname. 60 | 61 | + Request accountnames (application/json; charset=utf-8) 62 | + Headers 63 | 64 | Accept: application/vnd.shimastripe+json 65 | + Attributes 66 | 67 | + user_id: 1 (number) 68 | + name: NAME (string) 69 | + user (users) 70 | 71 | + Response 200 (application/json; charset=utf-8) 72 | + Attributes (accountnames) 73 | 74 | ### Delete accountname [DELETE] 75 | 76 | Delete accountname. 77 | 78 | + Request accountnames (application/json; charset=utf-8) 79 | + Headers 80 | 81 | Accept: application/vnd.shimastripe+json 82 | 83 | + Response 204 84 | 85 | # Data Structures 86 | ## accountnames (object, fixed) 87 | 88 | + id: 1 (number) 89 | + user_id: 1 (number) 90 | + name: NAME (string) 91 | + user (users) 92 | + created_at () 93 | + updated_at () 94 | -------------------------------------------------------------------------------- /docs/email.apib: -------------------------------------------------------------------------------- 1 | # Group Emails 2 | Welcome to the emails API. This API provides access to the emails service. 3 | 4 | ## emails [/emails] 5 | 6 | ### Create email [POST] 7 | 8 | Create a new email 9 | 10 | + Request emails (application/json; charset=utf-8) 11 | + Headers 12 | 13 | Accept: application/vnd.shimastripe+json 14 | + Attributes 15 | 16 | + user_id: 1 (number) 17 | + email: EMAIL (string) 18 | + user () 19 | 20 | + Response 201 (application/json; charset=utf-8) 21 | + Attributes (emails) 22 | 23 | ### Get emails [GET] 24 | 25 | Returns email list. 26 | 27 | + Request emails (application/json; charset=utf-8) 28 | + Headers 29 | 30 | Accept: application/vnd.shimastripe+json 31 | 32 | + Response 200 (application/json; charset=utf-8) 33 | + Attributes (array, fixed) 34 | + (emails) 35 | 36 | ## email details [/emails/{id}] 37 | 38 | + Parameters 39 | + id: `1` (enum[string]) - The ID of the desired email. 40 | + Members 41 | + `1` 42 | + `2` 43 | + `3` 44 | 45 | ### Get email [GET] 46 | 47 | Returns email. 48 | 49 | + Request emails (application/json; charset=utf-8) 50 | + Headers 51 | 52 | Accept: application/vnd.shimastripe+json 53 | 54 | + Response 200 (application/json; charset=utf-8) 55 | + Attributes (emails, fixed) 56 | 57 | ### Update email [PUT] 58 | 59 | Update email. 60 | 61 | + Request emails (application/json; charset=utf-8) 62 | + Headers 63 | 64 | Accept: application/vnd.shimastripe+json 65 | + Attributes 66 | 67 | + user_id: 1 (number) 68 | + email: EMAIL (string) 69 | + user () 70 | 71 | + Response 200 (application/json; charset=utf-8) 72 | + Attributes (emails) 73 | 74 | ### Delete email [DELETE] 75 | 76 | Delete email. 77 | 78 | + Request emails (application/json; charset=utf-8) 79 | + Headers 80 | 81 | Accept: application/vnd.shimastripe+json 82 | 83 | + Response 204 84 | 85 | # Data Structures 86 | ## emails (object, fixed) 87 | 88 | + id: 1 (number) 89 | + user_id: 1 (number) 90 | + email: EMAIL (string) 91 | + user () 92 | + created_at () 93 | + updated_at () 94 | -------------------------------------------------------------------------------- /docs/index.apib: -------------------------------------------------------------------------------- 1 | FORMAT: 1A 2 | HOST: http://localhost:8080 3 | 4 | # Go-Api-Sokushukai API 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /docs/user.apib: -------------------------------------------------------------------------------- 1 | # Group Users 2 | Welcome to the users API. This API provides access to the users service. 3 | 4 | ## users [/users] 5 | 6 | ### Create user [POST] 7 | 8 | Create a new user 9 | 10 | + Request users (application/json; charset=utf-8) 11 | + Headers 12 | 13 | Accept: application/vnd.shimastripe+json 14 | + Attributes 15 | 16 | + account_name () 17 | + emails (array[*emails]) 18 | 19 | + Response 201 (application/json; charset=utf-8) 20 | + Attributes (users) 21 | 22 | ### Get users [GET] 23 | 24 | Returns user list. 25 | 26 | + Request users (application/json; charset=utf-8) 27 | + Headers 28 | 29 | Accept: application/vnd.shimastripe+json 30 | 31 | + Response 200 (application/json; charset=utf-8) 32 | + Attributes (array, fixed) 33 | + (users) 34 | 35 | ## user details [/users/{id}] 36 | 37 | + Parameters 38 | + id: `1` (enum[string]) - The ID of the desired user. 39 | + Members 40 | + `1` 41 | + `2` 42 | + `3` 43 | 44 | ### Get user [GET] 45 | 46 | Returns user. 47 | 48 | + Request users (application/json; charset=utf-8) 49 | + Headers 50 | 51 | Accept: application/vnd.shimastripe+json 52 | 53 | + Response 200 (application/json; charset=utf-8) 54 | + Attributes (users, fixed) 55 | 56 | ### Update user [PUT] 57 | 58 | Update user. 59 | 60 | + Request users (application/json; charset=utf-8) 61 | + Headers 62 | 63 | Accept: application/vnd.shimastripe+json 64 | + Attributes 65 | 66 | + account_name () 67 | + emails (array[*emails]) 68 | 69 | + Response 200 (application/json; charset=utf-8) 70 | + Attributes (users) 71 | 72 | ### Delete user [DELETE] 73 | 74 | Delete user. 75 | 76 | + Request users (application/json; charset=utf-8) 77 | + Headers 78 | 79 | Accept: application/vnd.shimastripe+json 80 | 81 | + Response 204 82 | 83 | # Data Structures 84 | ## users (object, fixed) 85 | 86 | + id: 1 (number) 87 | + account_name () 88 | + emails (array[*emails]) 89 | + created_at () 90 | + updated_at () 91 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: f9ba5e42659046ea3517b8fb01dc7e050a6d3a90cf2c3428482d35d4016b1f82 2 | updated: 2016-08-18T12:13:46.212882701+09:00 3 | imports: 4 | - name: github.com/gin-gonic/gin 5 | version: f931d1ea80ae95a6fc739213cdd9399bd2967fb6 6 | subpackages: 7 | - binding 8 | - render 9 | - name: github.com/golang/protobuf 10 | version: 2402d76f3d41f928c7902a765dfc872356dd3aad 11 | subpackages: 12 | - proto 13 | - name: github.com/jinzhu/gorm 14 | version: 35a2a004d880453f0bbd3716bcc63da1cb15b77b 15 | subpackages: 16 | - dialects/sqlite 17 | - name: github.com/jinzhu/inflection 18 | version: 74387dc39a75e970e7a3ae6a3386b5bd2e5c5cff 19 | - name: github.com/manucorporat/sse 20 | version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d 21 | - name: github.com/mattn/go-sqlite3 22 | version: c3e9588849195eefa783417c3a53d092f6e93526 23 | - name: github.com/serenize/snaker 24 | version: 8824b61eca66d308fcb2d515287d3d7a28dba8d6 25 | - name: github.com/shimastripe/go-api-sokushukai 26 | version: ec26d35050be4d7fdca9bb6b37b8269b51d6305b 27 | subpackages: 28 | - controllers 29 | - db 30 | - helper 31 | - middleware 32 | - models 33 | - router 34 | - server 35 | - version 36 | - name: golang.org/x/net 37 | version: f315505cf3349909cdf013ea56690da34e96a451 38 | subpackages: 39 | - context 40 | - name: gopkg.in/go-playground/validator.v8 41 | version: c193cecd124b5cc722d7ee5538e945bdb3348435 42 | - name: gopkg.in/yaml.v2 43 | version: e4d366fc3c7938e2958e662b4258c7a89e1f0e3e 44 | devImports: [] 45 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/shimastripe/go-api-sokushukai 2 | import: 3 | - package: github.com/gin-gonic/gin 4 | - package: github.com/jinzhu/gorm 5 | subpackages: 6 | - dialects/sqlite 7 | - package: github.com/serenize/snaker 8 | - package: github.com/shimastripe/go-api-sokushukai 9 | subpackages: 10 | - controllers 11 | - db 12 | - helper 13 | - middleware 14 | - models 15 | - router 16 | - server 17 | - version 18 | -------------------------------------------------------------------------------- /helper/field.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "reflect" 5 | "strings" 6 | ) 7 | 8 | func contains(ss map[string]interface{}, s string) bool { 9 | _, ok := ss[s] 10 | 11 | return ok 12 | } 13 | 14 | func merge(m1, m2 map[string]interface{}) map[string]interface{} { 15 | result := make(map[string]interface{}) 16 | 17 | for k, v := range m1 { 18 | result[k] = v 19 | } 20 | 21 | for k, v := range m2 { 22 | result[k] = v 23 | } 24 | 25 | return result 26 | } 27 | 28 | func ParseFields(fields string) map[string]interface{} { 29 | result := make(map[string]interface{}) 30 | 31 | if fields == "*" { 32 | result["*"] = nil 33 | return result 34 | } 35 | 36 | for _, field := range strings.Split(fields, ",") { 37 | parts := strings.SplitN(field, ".", 2) 38 | 39 | if len(parts) == 2 { 40 | if result[parts[0]] == nil { 41 | result[parts[0]] = ParseFields(parts[1]) 42 | } else { 43 | result[parts[0]] = merge(result[parts[0]].(map[string]interface{}), ParseFields(parts[1])) 44 | } 45 | } else { 46 | result[parts[0]] = nil 47 | } 48 | } 49 | 50 | return result 51 | } 52 | 53 | func FieldToMap(model interface{}, fields map[string]interface{}) map[string]interface{} { 54 | u := make(map[string]interface{}) 55 | ts, vs := reflect.TypeOf(model), reflect.ValueOf(model) 56 | 57 | for i := 0; i < ts.NumField(); i++ { 58 | var jsonKey string 59 | field := ts.Field(i) 60 | jsonTag := field.Tag.Get("json") 61 | 62 | if jsonTag == "" { 63 | jsonKey = field.Name 64 | } else { 65 | jsonKey = strings.Split(jsonTag, ",")[0] 66 | } 67 | 68 | if contains(fields, "*") { 69 | u[jsonKey] = vs.Field(i).Interface() 70 | continue 71 | } 72 | 73 | if contains(fields, jsonKey) { 74 | v := fields[jsonKey] 75 | 76 | if vs.Field(i).Kind() == reflect.Ptr { 77 | if !vs.Field(i).IsNil() { 78 | if v == nil { 79 | u[jsonKey] = vs.Field(i).Elem().Interface() 80 | } else { 81 | u[jsonKey] = FieldToMap(vs.Field(i).Elem().Interface(), v.(map[string]interface{})) 82 | } 83 | } else { 84 | u[jsonKey] = nil 85 | } 86 | } else if vs.Field(i).Kind() == reflect.Slice { 87 | var fieldMap []interface{} 88 | s := reflect.ValueOf(vs.Field(i).Interface()) 89 | 90 | for i := 0; i < s.Len(); i++ { 91 | if v == nil { 92 | fieldMap = append(fieldMap, s.Index(i).Interface()) 93 | } else { 94 | fieldMap = append(fieldMap, FieldToMap(s.Index(i).Interface(), v.(map[string]interface{}))) 95 | } 96 | } 97 | 98 | u[jsonKey] = fieldMap 99 | } else { 100 | if v == nil { 101 | u[jsonKey] = vs.Field(i).Interface() 102 | } else { 103 | u[jsonKey] = FieldToMap(vs.Field(i).Interface(), v.(map[string]interface{})) 104 | } 105 | } 106 | } 107 | } 108 | 109 | return u 110 | } 111 | -------------------------------------------------------------------------------- /helper/field_test.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestParseFields_Wildcard(t *testing.T) { 8 | fields := "*" 9 | result := ParseFields(fields) 10 | 11 | if _, ok := result["*"]; !ok { 12 | t.Fatalf("result[*] should exist: %#v", result) 13 | } 14 | 15 | if result["*"] != nil { 16 | t.Fatalf("result[*] should be nil: %#v", result) 17 | } 18 | } 19 | 20 | func TestParseFields_Flat(t *testing.T) { 21 | fields := "profile" 22 | result := ParseFields(fields) 23 | 24 | if _, ok := result["profile"]; !ok { 25 | t.Fatalf("result[profile] should exist: %#v", result) 26 | } 27 | 28 | if result["profile"] != nil { 29 | t.Fatalf("result[profile] should be nil: %#v", result) 30 | } 31 | } 32 | 33 | func TestParseFields_Nested(t *testing.T) { 34 | fields := "profile.nation" 35 | result := ParseFields(fields) 36 | 37 | if _, ok := result["profile"]; !ok { 38 | t.Fatalf("result[profile] should exist: %#v", result) 39 | } 40 | 41 | if _, ok := result["profile"].(map[string]interface{}); !ok { 42 | t.Fatalf("result[profile] should be map: %#v", result) 43 | } 44 | 45 | if _, ok := result["profile"].(map[string]interface{})["nation"]; !ok { 46 | t.Fatalf("result[profile][nation] should exist: %#v", result) 47 | } 48 | 49 | if result["profile"].(map[string]interface{})["nation"] != nil { 50 | t.Fatalf("result[profile][nation] should be nil: %#v", result) 51 | } 52 | } 53 | 54 | func TestParseFields_NestedDeeply(t *testing.T) { 55 | fields := "profile.nation.name" 56 | result := ParseFields(fields) 57 | 58 | if _, ok := result["profile"]; !ok { 59 | t.Fatalf("result[profile] should exist: %#v", result) 60 | } 61 | 62 | if _, ok := result["profile"].(map[string]interface{}); !ok { 63 | t.Fatalf("result[profile] should be map: %#v", result) 64 | } 65 | 66 | if _, ok := result["profile"].(map[string]interface{})["nation"]; !ok { 67 | t.Fatalf("result[profile][nation] should exist: %#v", result) 68 | } 69 | 70 | if _, ok := result["profile"].(map[string]interface{})["nation"].(map[string]interface{}); !ok { 71 | t.Fatalf("result[profile][nation] should be map: %#v", result) 72 | } 73 | 74 | if _, ok := result["profile"].(map[string]interface{})["nation"].(map[string]interface{})["name"]; !ok { 75 | t.Fatalf("result[profile][nation][name] should exist: %#v", result) 76 | } 77 | 78 | if result["profile"].(map[string]interface{})["nation"].(map[string]interface{})["name"] != nil { 79 | t.Fatalf("result[profile][nation][name] should be nil: %#v", result) 80 | } 81 | } 82 | 83 | func TestParseFields_MultipleFields(t *testing.T) { 84 | fields := "profile.nation.name,emails" 85 | result := ParseFields(fields) 86 | 87 | if _, ok := result["profile"]; !ok { 88 | t.Fatalf("result[profile] should exist: %#v", result) 89 | } 90 | 91 | if _, ok := result["profile"].(map[string]interface{}); !ok { 92 | t.Fatalf("result[profile] should be map: %#v", result) 93 | } 94 | 95 | if _, ok := result["profile"].(map[string]interface{})["nation"]; !ok { 96 | t.Fatalf("result[profile][nation] should exist: %#v", result) 97 | } 98 | 99 | if _, ok := result["profile"].(map[string]interface{})["nation"].(map[string]interface{}); !ok { 100 | t.Fatalf("result[profile][nation] should be map: %#v", result) 101 | } 102 | 103 | if _, ok := result["profile"].(map[string]interface{})["nation"].(map[string]interface{})["name"]; !ok { 104 | t.Fatalf("result[profile][nation][name] should exist: %#v", result) 105 | } 106 | 107 | if result["profile"].(map[string]interface{})["nation"].(map[string]interface{})["name"] != nil { 108 | t.Fatalf("result[profile][nation][name] should be nil: %#v", result) 109 | } 110 | 111 | if _, ok := result["emails"]; !ok { 112 | t.Fatalf("result[emails] should exist: %#v", result) 113 | } 114 | 115 | if result["emails"] != nil { 116 | t.Fatalf("result[emails] should be map: %#v", result) 117 | } 118 | } 119 | 120 | func TestParseFields_Included(t *testing.T) { 121 | fields := "profile.nation.name,profile" 122 | result := ParseFields(fields) 123 | 124 | if _, ok := result["profile"]; !ok { 125 | t.Fatalf("result[profile] should exist: %#v", result) 126 | } 127 | 128 | if result["profile"] != nil { 129 | t.Fatalf("result[profile] should be nil: %#v", result) 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/shimastripe/go-api-sokushukai/db" 5 | "github.com/shimastripe/go-api-sokushukai/server" 6 | ) 7 | 8 | // main ... 9 | func main() { 10 | database := db.Connect() 11 | s := server.Setup(database) 12 | s.Run(":8080") 13 | } 14 | -------------------------------------------------------------------------------- /middleware/set_db.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/jinzhu/gorm" 6 | _ "github.com/jinzhu/gorm/dialects/sqlite" 7 | ) 8 | 9 | func SetDBtoContext(db *gorm.DB) gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | c.Set("DB", db) 12 | c.Next() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shimastripe/go-api-sokushukai/c9a3bf2ecb283112a16a846005c414067b23184a/models/.gitkeep -------------------------------------------------------------------------------- /models/accountname.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type AccountName struct { 6 | ID uint `gorm:"primary_key;AUTO_INCREMENT" json:"id"` 7 | UserID uint `json:"user_id"` 8 | Name string `json:"name"` 9 | User *User `json:"user"` 10 | CreatedAt time.Time `json:"created_at"` 11 | UpdatedAt time.Time `json:"updated_at"` 12 | } 13 | -------------------------------------------------------------------------------- /models/email.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Email struct { 6 | ID uint `gorm:"primary_key;AUTO_INCREMENT" json:"id"` 7 | UserID uint `json:"user_id"` 8 | Email string `json:"email"` 9 | User *User `json:"user"` 10 | CreatedAt time.Time `json:"created_at"` 11 | UpdatedAt time.Time `json:"updated_at"` 12 | } 13 | -------------------------------------------------------------------------------- /models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type User struct { 6 | ID uint `gorm:"primary_key;AUTO_INCREMENT" json:"id"` 7 | Name string `json:"name"` 8 | AccountName *AccountName `json:"account_name"` 9 | Emails []*Email `json:"emails"` 10 | CreatedAt time.Time `json:"created_at"` 11 | UpdatedAt time.Time `json:"updated_at"` 12 | } 13 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/shimastripe/go-api-sokushukai/controllers" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func Initialize(r *gin.Engine) { 10 | api := r.Group("api") 11 | { 12 | 13 | api.GET("/emails", controllers.GetEmails) 14 | api.GET("/emails/:id", controllers.GetEmail) 15 | api.POST("/emails", controllers.CreateEmail) 16 | api.PUT("/emails/:id", controllers.UpdateEmail) 17 | api.DELETE("/emails/:id", controllers.DeleteEmail) 18 | 19 | api.GET("/account_names", controllers.GetAccountNames) 20 | api.GET("/account_names/:id", controllers.GetAccountName) 21 | api.POST("/account_names", controllers.CreateAccountName) 22 | api.PUT("/account_names/:id", controllers.UpdateAccountName) 23 | api.DELETE("/account_names/:id", controllers.DeleteAccountName) 24 | 25 | api.GET("/users", controllers.GetUsers) 26 | api.GET("/users/:id", controllers.GetUser) 27 | api.POST("/users", controllers.CreateUser) 28 | api.PUT("/users/:id", controllers.UpdateUser) 29 | api.DELETE("/users/:id", controllers.DeleteUser) 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/shimastripe/go-api-sokushukai/middleware" 5 | "github.com/shimastripe/go-api-sokushukai/router" 6 | 7 | "github.com/gin-gonic/gin" 8 | "github.com/jinzhu/gorm" 9 | _ "github.com/jinzhu/gorm/dialects/sqlite" 10 | ) 11 | 12 | func Setup(db *gorm.DB) *gin.Engine { 13 | r := gin.Default() 14 | r.Use(middleware.SetDBtoContext(db)) 15 | router.Initialize(r) 16 | return r 17 | } 18 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "math" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func New(c *gin.Context) (string, error) { 12 | header := c.Request.Header["Accept"][0] 13 | header = strings.Join(strings.Fields(header), "") 14 | var ver string 15 | 16 | // header version 17 | if strings.Contains(header, "version=") { 18 | ver = strings.Split(strings.SplitAfter(header, "version=")[1], ";")[0] 19 | } 20 | 21 | // query v 22 | v := c.Query("v") 23 | if v != "" { 24 | ver = v 25 | } 26 | 27 | if ver == "" { 28 | return "-1", nil 29 | } 30 | 31 | _, err := strconv.Atoi(strings.Join(strings.Split(ver, "."), "")) 32 | if err != nil { 33 | return "", err 34 | } 35 | return ver, nil 36 | } 37 | 38 | func Range(left string, op string, right string) bool { 39 | switch op { 40 | case "<": 41 | return (compare(left, right) == -1) 42 | case "<=": 43 | return (compare(left, right) <= 0) 44 | case ">": 45 | return (compare(left, right) == 1) 46 | case ">=": 47 | return (compare(left, right) >= 0) 48 | case "==": 49 | return (compare(left, right) == 0) 50 | } 51 | return false 52 | } 53 | 54 | func compare(left string, right string) int { 55 | // l > r : 1 56 | // l == r : 0 57 | // l < r : -1 58 | if left == "-1" { 59 | return 1 60 | } else if right == "-1" { 61 | return -1 62 | } 63 | 64 | lArr := strings.Split(left, ".") 65 | rArr := strings.Split(right, ".") 66 | lItems := len(lArr) 67 | rItems := len(rArr) 68 | min := int(math.Min(float64(lItems), float64(rItems))) 69 | for i := 0; i < min; i++ { 70 | l, _ := strconv.Atoi(lArr[i]) 71 | r, _ := strconv.Atoi(rArr[i]) 72 | if l != r { 73 | if l > r { 74 | return 1 75 | } 76 | return -1 77 | } 78 | } 79 | if lItems == rItems { 80 | return 0 81 | } 82 | if lItems < rItems { 83 | return 1 84 | } else { 85 | return -1 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /version/version_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import "testing" 4 | 5 | func TestRange(t *testing.T) { 6 | 7 | if Range("1.2.3", "<", "0.9") { 8 | t.Errorf("defect in <") 9 | } 10 | 11 | if Range("1.2.3", "<", "0.9.1") { 12 | t.Errorf("defect in <") 13 | } 14 | 15 | if Range("1.2.3", "<", "1.2.2") { 16 | t.Errorf("defect in <") 17 | } 18 | 19 | if Range("1.2.3", "<", "1.2.3") { 20 | t.Errorf("defect in <") 21 | } 22 | 23 | if !Range("1.2.3", "<", "1.2.4") { 24 | t.Errorf("defect in <") 25 | } 26 | 27 | if !Range("1.2.3", "<", "1.2") { 28 | t.Errorf("defect in <") 29 | } 30 | 31 | if !Range("1.2.3", "<", "1.5") { 32 | t.Errorf("defect in <") 33 | } 34 | 35 | if !Range("1.2.3", "<", "-1") { 36 | t.Errorf("defect in <") 37 | } 38 | 39 | if Range("1.2.3", "<=", "0.9") { 40 | t.Errorf("defect in <=") 41 | } 42 | 43 | if Range("1.2.3", "<=", "0.9.1") { 44 | t.Errorf("defect in <=") 45 | } 46 | 47 | if Range("1.2.3", "<=", "1.2.2") { 48 | t.Errorf("defect in <=") 49 | } 50 | 51 | if !Range("1.2.3", "<=", "1.2.3") { 52 | t.Errorf("defect in <=") 53 | } 54 | 55 | if !Range("1.2.3", "<=", "1.2.4") { 56 | t.Errorf("defect in <=") 57 | } 58 | 59 | if !Range("1.2.3", "<=", "1.2") { 60 | t.Errorf("defect in <=") 61 | } 62 | 63 | if !Range("1.2.3", "<=", "1.5") { 64 | t.Errorf("defect in <=") 65 | } 66 | 67 | if !Range("1.2.3", "<=", "-1") { 68 | t.Errorf("defect in <=") 69 | } 70 | 71 | if !Range("1.2.3", ">", "0.9") { 72 | t.Errorf("defect in >") 73 | } 74 | 75 | if !Range("1.2.3", ">", "0.9.1") { 76 | t.Errorf("defect in >") 77 | } 78 | 79 | if !Range("1.2.3", ">", "1.2.2") { 80 | t.Errorf("defect in >") 81 | } 82 | 83 | if Range("1.2.3", ">", "1.2.3") { 84 | t.Errorf("defect in >") 85 | } 86 | 87 | if Range("1.2.3", ">", "1.2.4") { 88 | t.Errorf("defect in >") 89 | } 90 | 91 | if Range("1.2.3", ">", "1.2") { 92 | t.Errorf("defect in >") 93 | } 94 | 95 | if Range("1.2.3", ">", "1.5") { 96 | t.Errorf("defect in >") 97 | } 98 | 99 | if Range("1.2.3", ">", "-1") { 100 | t.Errorf("defect in >") 101 | } 102 | 103 | if !Range("1.2.3", ">=", "0.9") { 104 | t.Errorf("defect in >=") 105 | } 106 | 107 | if !Range("1.2.3", ">=", "0.9.1") { 108 | t.Errorf("defect in >=") 109 | } 110 | 111 | if !Range("1.2.3", ">=", "1.2.2") { 112 | t.Errorf("defect in >=") 113 | } 114 | 115 | if !Range("1.2.3", ">=", "1.2.3") { 116 | t.Errorf("defect in >=") 117 | } 118 | 119 | if Range("1.2.3", ">=", "1.2.4") { 120 | t.Errorf("defect in >=") 121 | } 122 | 123 | if Range("1.2.3", ">=", "1.2") { 124 | t.Errorf("defect in >=") 125 | } 126 | 127 | if Range("1.2.3", ">=", "1.5") { 128 | t.Errorf("defect in >=") 129 | } 130 | 131 | if Range("1.2.3", ">=", "-1") { 132 | t.Errorf("defect in >=") 133 | } 134 | 135 | if Range("1.2.3", "==", "0.9") { 136 | t.Errorf("defect in ==") 137 | } 138 | 139 | if Range("1.2.3", "==", "0.9.1") { 140 | t.Errorf("defect in ==") 141 | } 142 | 143 | if Range("1.2.3", "==", "1.2.2") { 144 | t.Errorf("defect in ==") 145 | } 146 | 147 | if !Range("1.2.3", "==", "1.2.3") { 148 | t.Errorf("defect in ==") 149 | } 150 | 151 | if Range("1.2.3", "==", "1.2.4") { 152 | t.Errorf("defect in ==") 153 | } 154 | 155 | if Range("1.2.3", "==", "1.2") { 156 | t.Errorf("defect in ==") 157 | } 158 | 159 | if Range("1.2.3", "==", "1.5") { 160 | t.Errorf("defect in ==") 161 | } 162 | 163 | if Range("1.2.3", "==", "-1") { 164 | t.Errorf("defect in ==") 165 | } 166 | } 167 | --------------------------------------------------------------------------------