Skip to content

Commit

Permalink
validatorを追加 (#530)
Browse files Browse the repository at this point in the history
* Add validator

* Change signature of model.UpdateItem

* Implement `Validate` for each structs

* Write validation tests

* Add `router.BindAndValidate`
  • Loading branch information
H1rono authored Apr 9, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 37e9d27 commit 2b83f22
Showing 18 changed files with 507 additions and 32 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ go 1.15
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/disintegration/imaging v1.6.2
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/jinzhu/gorm v1.9.16
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.4.0 // indirect
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -11,6 +13,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
@@ -38,6 +42,7 @@ github.com/ncw/swift v1.0.53/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -67,6 +72,7 @@ golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -59,6 +59,9 @@ func main() {
// Echo instance
e := echo.New()

// Validator
router.SetValidator(e)

// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
8 changes: 8 additions & 0 deletions model/comments.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@ package model

import (
"errors"

validation "github.com/go-ozzo/ozzo-validation/v4"
)

// Comment commentの構造体
@@ -20,6 +22,12 @@ type RequestPostCommentBody struct {
Text string `gorm:"type:text;not null" json:"text"`
}

func (body RequestPostCommentBody) Validate() error {
return validation.ValidateStruct(&body,
validation.Field(&body.Text, validation.Required),
)
}

// TableName dbのテーブル名を指定する
func (comment *Comment) TableName() string {
return "comments"
68 changes: 52 additions & 16 deletions model/items.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ package model
import (
"errors"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
"github.com/jinzhu/gorm"
)

@@ -46,6 +48,14 @@ type RentalUser struct {
Count int `gorm:"type:int;" json:"count"`
}

type RequestPutItemBody struct {
Name string `json:"name"`
Code string `json:"code"`
Description string `json:"description"`
ImgURL string `json:"imgUrl"`
Type int `json:"type"`
}

type RequestPostOwnersBody struct {
UserID int `json:"userId"`
Rentalable bool `json:"rentalable"`
@@ -62,6 +72,26 @@ func (item *Item) TableName() string {
return "items"
}

// checkItemType Item.Typeのバリデーション
func checkItemType(value interface{}) error {
i := value.(int)
// item.Type=0⇒個人、1⇒trap所有、2⇒支援課
if !(i == 0 || i == 1 || i == 2) {
return errors.New("must be 0, 1, or 2")
}
return nil
}

func (item Item) Validate() error {
return validation.ValidateStruct(&item,
validation.Field(&item.Name, validation.Required),
validation.Field(&item.Type, validation.By(checkItemType)),
validation.Field(&item.Code, validation.Required),
validation.Field(&item.Description, validation.Required),
validation.Field(&item.ImgURL, is.URL),
)
}

func (owner *Owner) TableName() string {
return "owners"
}
@@ -70,6 +100,24 @@ func (rentalUser *RentalUser) TableName() string {
return "rental_users"
}

func (body RequestPutItemBody) Validate() error {
return validation.ValidateStruct(&body,
validation.Field(&body.Name, validation.Required),
validation.Field(&body.Code, validation.Required),
validation.Field(&body.Description, validation.Required),
validation.Field(&body.ImgURL, is.URL),
validation.Field(&body.Type, validation.By(checkItemType)),
)
}

func (body RequestPostOwnersBody) Validate() error {
return validation.ValidateStruct(&body,
validation.Field(&body.UserID, validation.Required),
validation.Field(&body.Rentalable, validation.Skip),
validation.Field(&body.Count, validation.Required),
)
}

// GetItemByID IDからitemを取得する
func GetItemByID(id uint) (Item, error) {
res := Item{}
@@ -444,25 +492,13 @@ func DestroyItem(item Item) (Item, error) {
}

// UpdateItem itemを変更する
func UpdateItem(item *Item, body *map[string]interface{}, isAdmin bool) (Item, error) {
fields := []string{"name", "code", "description", "imgUrl"}
if isAdmin {
fields = append(fields, "type")
func UpdateItem(item *Item, body *RequestPutItemBody, isAdmin bool) (Item, error) {
if !isAdmin {
body.Type = 0
}
err := db.Model(item).Updates(filterMap(body, fields)).Error
err := db.Model(item).Updates(body).Error
if err != nil {
return Item{}, err
}

return *item, nil
}

func filterMap(input *map[string]interface{}, keys []string) map[string]interface{} {
output := make(map[string]interface{})
for _, key := range keys {
if val, ok := (*input)[key]; ok {
output[key] = val
}
}
return output
}
18 changes: 18 additions & 0 deletions model/items_test.go
Original file line number Diff line number Diff line change
@@ -326,6 +326,24 @@ func TestDestroyItem(t *testing.T) {
})
}

func TestUpdateItem(t *testing.T) {
t.Parallel()

t.Run("success", func(t *testing.T) {
assert := assert.New(t)
item, err := CreateItem(Item{Name: "testUpdateItemSuccess", Type: 1})
assert.NotEmpty(item)
assert.NoError(err)
body := RequestPutItemBody{}
body.Name = "updateTestUpdateItemSuccess"
updateItem, err := UpdateItem(&item, &body, false)
assert.NotEmpty(updateItem)
assert.NoError(err)
assert.Equal("updateTestUpdateItemSuccess", updateItem.Name)
assert.Equal(1, updateItem.Type)
})
}

func TestRentalItem(t *testing.T) {
t.Parallel()

20 changes: 20 additions & 0 deletions model/logs.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,8 @@ package model
import (
"errors"
"time"

validation "github.com/go-ozzo/ozzo-validation/v4"
)

// Log logの構造体
@@ -44,6 +46,24 @@ func (log *Log) TableName() string {
return "logs"
}

func checkLogsType(value interface{}) error {
t, _ := value.(int)
if !(t == 0 || t == 1 || t == 2 || t == 3) {
return errors.New("must be 1, 2, or 3")
}
return nil
}

func (body RequestPostLogsBody) Validate() error {
return validation.ValidateStruct(&body,
validation.Field(&body.OwnerID, validation.Required),
validation.Field(&body.Type, validation.By(checkLogsType)),
validation.Field(&body.Purpose, validation.Required),
validation.Field(&body.DueDate, validation.Date("2006-01-02")),
validation.Field(&body.Count, validation.Required),
)
}

// CreateLog logを作成する
func CreateLog(log Log) (Log, error) {
if log.ItemID == 0 {
8 changes: 8 additions & 0 deletions model/users.go
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package model
import (
"errors"

validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/jinzhu/gorm"
)

@@ -24,6 +25,13 @@ func (user *User) TableName() string {
return "users"
}

func (body RequestPutUsersBody) Validate() error {
return validation.ValidateStruct(&body,
validation.Field(&body.Name, validation.Required),
validation.Field(&body.Admin, validation.Skip),
)
}

// GetUsers 全userを取得する
func GetUsers() ([]User, error) {
res := []User{}
4 changes: 2 additions & 2 deletions router/comments.go
Original file line number Diff line number Diff line change
@@ -29,8 +29,8 @@ func PostComments(c echo.Context) error {
return c.JSON(http.StatusBadRequest, err)
}
commentRequest := model.RequestPostCommentBody{}
if err := c.Bind(&commentRequest); err != nil {
return err
if err := BindAndValidate(c, &commentRequest); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
comment := model.Comment{
ItemID: uint(itemID),
16 changes: 16 additions & 0 deletions router/comments_test.go
Original file line number Diff line number Diff line change
@@ -19,6 +19,9 @@ func TestPostComments(t *testing.T) {
testRequestBody := model.RequestPostCommentBody{
Text: "testPostCommentsText",
}
testRequestInvalidBody := model.RequestPostCommentBody{
Text: "",
}
item, _ := model.CreateItem(model.Item{Name: "testPostCommentsItem"})

t.Run("admin user", func(t *testing.T) {
@@ -40,4 +43,17 @@ func TestPostComments(t *testing.T) {
assert.Equal(item.ID, comment.ItemID)
assert.Equal("traP", comment.User.Name)
})

t.Run("admin user/validation error", func(t *testing.T) {
assert := assert.New(t)
e := echoSetupWithAdminUser()

reqBody, _ := json.Marshal(testRequestInvalidBody)
req := httptest.NewRequest(echo.POST, "/api/items/"+strconv.Itoa(int(item.ID))+"/comments", bytes.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)

assert.Equal(http.StatusBadRequest, rec.Code)
})
}
14 changes: 7 additions & 7 deletions router/items.go
Original file line number Diff line number Diff line change
@@ -65,8 +65,8 @@ func GetItems(c echo.Context) error {
func PostItems(c echo.Context) error {
user := c.Get("user").(model.User)
item := model.Item{}
if err := c.Bind(&item); err != nil {
return err
if err := BindAndValidate(c, &item); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
// item.Type=0⇒個人、1⇒trap所有、2⇒支援課
if item.Type != model.PersonalItem && !user.Admin {
@@ -101,9 +101,9 @@ func GetItem(c echo.Context) error {
func PutItem(c echo.Context) error {
ID := c.Param("id")
user := c.Get("user").(model.User)
body := map[string]interface{}{}
if err := c.Bind(&body); err != nil {
return err
body := model.RequestPutItemBody{}
if err := BindAndValidate(c, &body); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
itemID, err := strconv.Atoi(ID)
if err != nil {
@@ -154,7 +154,7 @@ func PostOwners(c echo.Context) error {
ID := c.Param("id")
me := c.Get("user").(model.User)
body := model.RequestPostOwnersBody{}
if err := c.Bind(&body); err != nil {
if err := BindAndValidate(c, &body); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
itemID, err := strconv.Atoi(ID)
@@ -208,7 +208,7 @@ func PutOwners(c echo.Context) error {
ID := c.Param("id")
me := c.Get("user").(model.User)
body := model.RequestPostOwnersBody{}
if err := c.Bind(&body); err != nil {
if err := BindAndValidate(c, &body); err != nil {
return c.JSON(http.StatusBadRequest, err)
}
itemID, err := strconv.Atoi(ID)
Loading

0 comments on commit 2b83f22

Please sign in to comment.