From fa55f46609cbb2478cab5a7cf9a308c2a4d74060 Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Tue, 24 Oct 2023 14:24:51 +0900 Subject: [PATCH 1/5] Extended option character max to 1000 characters --- model/responses_impl.go | 13 +++++----- router/questions.go | 5 ++-- router/questions_test.go | 25 ++++++++++---------- router/responses_test.go | 51 ++++++++++++++++++++-------------------- 4 files changed, 49 insertions(+), 45 deletions(-) diff --git a/model/responses_impl.go b/model/responses_impl.go index e134f4ff..93e070d4 100755 --- a/model/responses_impl.go +++ b/model/responses_impl.go @@ -3,9 +3,10 @@ package model import ( "context" "fmt" + "time" + "gopkg.in/guregu/null.v4" "gorm.io/gorm" - "time" ) // Response ResponseRepositoryの実装 @@ -16,7 +17,7 @@ func NewResponse() *Response { return new(Response) } -//Responses responseテーブルの構造体 +// Responses responseテーブルの構造体 type Responses struct { ResponseID int `json:"-" gorm:"type:int(11);not null"` QuestionID int `json:"-" gorm:"type:int(11);not null"` @@ -25,21 +26,21 @@ type Responses struct { DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL"` } -//BeforeCreate insert時に自動でmodifiedAt更新 +// BeforeCreate insert時に自動でmodifiedAt更新 func (r *Responses) BeforeCreate(tx *gorm.DB) error { r.ModifiedAt = time.Now() return nil } -//BeforeUpdate Update時に自動でmodified_atを現在時刻に +// BeforeUpdate Update時に自動でmodified_atを現在時刻に func (r *Responses) BeforeUpdate(tx *gorm.DB) error { r.ModifiedAt = time.Now() return nil } -//TableName テーブル名が単数形なのでその対応 +// TableName テーブル名が単数形なのでその対応 func (*Responses) TableName() string { return "response" } @@ -49,7 +50,7 @@ type ResponseBody struct { QuestionID int `json:"questionID" gorm:"column:id" validate:"min=0"` QuestionType string `json:"question_type" gorm:"column:type" validate:"required,oneof=Text TextArea Number MultipleChoice Checkbox LinearScale"` Body null.String `json:"response" validate:"required"` - OptionResponse []string `json:"option_response" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=50"` + OptionResponse []string `json:"option_response" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=1000"` } // ResponseMeta 質問に対する回答の構造体 diff --git a/router/questions.go b/router/questions.go index a0349040..fa6211e8 100644 --- a/router/questions.go +++ b/router/questions.go @@ -3,10 +3,11 @@ package router import ( "errors" "fmt" - "github.com/labstack/echo/v4" "net/http" "regexp" + "github.com/labstack/echo/v4" + "github.com/traPtitech/anke-to/model" ) @@ -35,7 +36,7 @@ type PostAndEditQuestionRequest struct { PageNum int `json:"page_num" validate:"min=0"` Body string `json:"body" validate:"required"` IsRequired bool `json:"is_required"` - Options []string `json:"options" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=50"` + Options []string `json:"options" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=1000"` ScaleLabelRight string `json:"scale_label_right" validate:"max=50"` ScaleLabelLeft string `json:"scale_label_left" validate:"max=50"` ScaleMin int `json:"scale_min"` diff --git a/router/questions_test.go b/router/questions_test.go index 5e8fc7fe..009f695d 100644 --- a/router/questions_test.go +++ b/router/questions_test.go @@ -1,6 +1,7 @@ package router import ( + "strings" "testing" "github.com/go-playground/validator/v10" @@ -163,7 +164,7 @@ func TestPostQuestionValidate(t *testing.T) { }, }, { - description: "Textタイプでoptionが50文字以上でもエラー", + description: "Textタイプでoptionが1000文字以上でもエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "Text", @@ -171,7 +172,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "", ScaleLabelLeft: "", ScaleMin: 0, @@ -446,7 +447,7 @@ func TestPostQuestionValidate(t *testing.T) { }, }, { - description: "TextAreaタイプでoptionが50文字以上でもエラー", + description: "TextAreaタイプでoptionが1000文字以上でもエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "TextArea", @@ -454,7 +455,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "", ScaleLabelLeft: "", ScaleMin: 0, @@ -642,7 +643,7 @@ func TestPostQuestionValidate(t *testing.T) { }, }, { - description: "Numberタイプでoptionが50文字以上でもエラー", + description: "Numberタイプでoptionが1000文字以上でもエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "Number", @@ -650,7 +651,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "", ScaleLabelLeft: "", ScaleMin: 0, @@ -839,7 +840,7 @@ func TestPostQuestionValidate(t *testing.T) { isErr: true, }, { - description: "Checkboxタイプでoptionが50文字以上でエラー", + description: "Checkboxタイプでoptionが1000文字以上でエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "Checkbox", @@ -847,7 +848,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "", ScaleLabelLeft: "", ScaleMin: 0, @@ -1036,7 +1037,7 @@ func TestPostQuestionValidate(t *testing.T) { isErr: true, }, { - description: "MultipleChoiceタイプでoptionが50文字以上でエラー", + description: "MultipleChoiceタイプでoptionが1000文字以上でエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "MultipleChoice", @@ -1044,7 +1045,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "", ScaleLabelLeft: "", ScaleMin: 0, @@ -1232,7 +1233,7 @@ func TestPostQuestionValidate(t *testing.T) { }, }, { - description: "LinearScaleタイプでoptionが50文字以上でもエラー", + description: "LinearScaleタイプでoptionが1000文字以上でもエラー", request: &PostAndEditQuestionRequest{ QuestionnaireID: 1, QuestionType: "LinearScale", @@ -1240,7 +1241,7 @@ func TestPostQuestionValidate(t *testing.T) { PageNum: 1, Body: "発表タイトル", IsRequired: true, - Options: []string{"012345678901234567890123456789012345678901234567890"}, + Options: []string{"0" + strings.Repeat("1234567890", 100)}, ScaleLabelRight: "右", ScaleLabelLeft: "左", ScaleMin: 0, diff --git a/router/responses_test.go b/router/responses_test.go index 1ea9845b..6e21b804 100644 --- a/router/responses_test.go +++ b/router/responses_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" "github.com/go-playground/validator/v10" @@ -27,7 +28,7 @@ type responseBody struct { QuestionID int `json:"questionID" validate:"min=0"` QuestionType string `json:"question_type" validate:"required,oneof=Text TextArea Number MultipleChoice Checkbox LinearScale"` Body null.String `json:"response" validate:"required"` - OptionResponse []string `json:"option_response" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=50"` + OptionResponse []string `json:"option_response" validate:"required_if=QuestionType Checkbox,required_if=QuestionType MultipleChoice,dive,max=1000"` } func TestPostResponseValidate(t *testing.T) { @@ -141,7 +142,7 @@ func TestPostResponseValidate(t *testing.T) { isErr: true, }, { - description: "TextタイプでoptionResponseが50文字以上でエラー", + description: "TextタイプでoptionResponseが1000文字以上でエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -150,14 +151,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Text", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "TextタイプでoptionResponseが50文字ピッタリはエラーなし", + description: "TextタイプでoptionResponseが1000文字ピッタリはエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -166,7 +167,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Text", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, @@ -187,7 +188,7 @@ func TestPostResponseValidate(t *testing.T) { }, }, { - description: "TextAreaタイプでoptionResponseが50文字以上でもエラー", + description: "TextAreaタイプでoptionResponseが1000文字以上でもエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -196,14 +197,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "TextArea", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "TextAreaタイプでoptionResponseが50文字ピッタリはエラーなし", + description: "TextAreaタイプでoptionResponseが1000文字ピッタリはエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -212,7 +213,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "TextArea", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, @@ -233,7 +234,7 @@ func TestPostResponseValidate(t *testing.T) { }, }, { - description: "NumberタイプでoptionResponseが50文字以上でもエラー", + description: "NumberタイプでoptionResponseが1000文字以上でもエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -242,14 +243,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Number", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "NumberタイプでoptionResponseが50文字ピッタリでエラーなし", + description: "NumberタイプでoptionResponseが1000文字ピッタリでエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -258,7 +259,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Number", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, @@ -310,7 +311,7 @@ func TestPostResponseValidate(t *testing.T) { isErr: true, }, { - description: "CheckboxタイプでOptionResponseが50文字以上な回答なのでエラー", + description: "CheckboxタイプでOptionResponseが1000文字以上な回答なのでエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -319,14 +320,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Checkbox", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "CheckboxタイプでOptionResponseが50文字ピッタリな回答なのでエラーなし", + description: "CheckboxタイプでOptionResponseが1000文字ピッタリな回答なのでエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -335,7 +336,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "Checkbox", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, @@ -372,7 +373,7 @@ func TestPostResponseValidate(t *testing.T) { isErr: true, }, { - description: "MultipleChoiceタイプでOptionResponseが50文字以上な回答なのでエラー", + description: "MultipleChoiceタイプでOptionResponseが1000文字以上な回答なのでエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -381,14 +382,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "MultipleChoice", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "MultipleChoiceタイプでOptionResponseが50文字ピッタリな回答なのでエラーなし", + description: "MultipleChoiceタイプでOptionResponseが1000文字ピッタリな回答なのでエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -397,7 +398,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "MultipleChoice", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, @@ -418,7 +419,7 @@ func TestPostResponseValidate(t *testing.T) { }, }, { - description: "LinearScaleタイプでoptionResponseが50文字以上でもエラー", + description: "LinearScaleタイプでoptionResponseが1000文字以上でもエラー", request: &Responses{ ID: 1, Temporarily: false, @@ -427,14 +428,14 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "LinearScale", Body: null.String{}, - OptionResponse: []string{"012345678901234567890123456789012345678901234567890"}, + OptionResponse: []string{"0" + strings.Repeat("1234567890", 100)}, }, }, }, isErr: true, }, { - description: "LinearScaleタイプでoptionResponseが50文字ピッタリなのでエラーなし", + description: "LinearScaleタイプでoptionResponseが1000文字ピッタリなのでエラーなし", request: &Responses{ ID: 1, Temporarily: false, @@ -443,7 +444,7 @@ func TestPostResponseValidate(t *testing.T) { QuestionID: 1, QuestionType: "LinearScale", Body: null.String{}, - OptionResponse: []string{"01234567890123456789012345678901234567890123456789"}, + OptionResponse: []string{strings.Repeat("1234567890", 100)}, }, }, }, From cc7b5dd9e935717ae0d49f9c26d1fd7fefd0831c Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Tue, 24 Oct 2023 14:58:20 +0900 Subject: [PATCH 2/5] add validator to PATCH --- router/questions.go | 12 ++++++++++++ router/responses.go | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/router/questions.go b/router/questions.go index fa6211e8..1b446686 100644 --- a/router/questions.go +++ b/router/questions.go @@ -61,6 +61,18 @@ func (q *Question) EditQuestion(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } + validate, err := getValidator(c) + if err != nil { + c.Logger().Errorf("failed to get validator: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + err = validate.Struct(req) + if err != nil { + c.Logger().Infof("validation failed: %+v", err) + return echo.NewHTTPError(http.StatusBadRequest, err) + } + switch req.QuestionType { case "Text": //正規表現のチェック diff --git a/router/responses.go b/router/responses.go index 56d9bb10..81d11d64 100644 --- a/router/responses.go +++ b/router/responses.go @@ -242,6 +242,18 @@ func (r *Response) EditResponse(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } + validate, err := getValidator(c) + if err != nil { + c.Logger().Errorf("failed to get validator: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + err = validate.Struct(req) + if err != nil { + c.Logger().Infof("validation failed: %+v", err) + return echo.NewHTTPError(http.StatusBadRequest, err) + } + limit, err := r.GetQuestionnaireLimit(c.Request().Context(), req.ID) if err != nil { if errors.Is(err, model.ErrRecordNotFound) { From 747217363577196ef8f07a83df412f0a0595e1be Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Tue, 24 Oct 2023 15:55:15 +0900 Subject: [PATCH 3/5] Revert "add validator to PATCH" This reverts commit cc7b5dd9e935717ae0d49f9c26d1fd7fefd0831c. --- router/questions.go | 12 ------------ router/responses.go | 12 ------------ 2 files changed, 24 deletions(-) diff --git a/router/questions.go b/router/questions.go index 1b446686..fa6211e8 100644 --- a/router/questions.go +++ b/router/questions.go @@ -61,18 +61,6 @@ func (q *Question) EditQuestion(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } - validate, err := getValidator(c) - if err != nil { - c.Logger().Errorf("failed to get validator: %+v", err) - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - err = validate.Struct(req) - if err != nil { - c.Logger().Infof("validation failed: %+v", err) - return echo.NewHTTPError(http.StatusBadRequest, err) - } - switch req.QuestionType { case "Text": //正規表現のチェック diff --git a/router/responses.go b/router/responses.go index 81d11d64..56d9bb10 100644 --- a/router/responses.go +++ b/router/responses.go @@ -242,18 +242,6 @@ func (r *Response) EditResponse(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } - validate, err := getValidator(c) - if err != nil { - c.Logger().Errorf("failed to get validator: %+v", err) - return echo.NewHTTPError(http.StatusInternalServerError, err) - } - - err = validate.Struct(req) - if err != nil { - c.Logger().Infof("validation failed: %+v", err) - return echo.NewHTTPError(http.StatusBadRequest, err) - } - limit, err := r.GetQuestionnaireLimit(c.Request().Context(), req.ID) if err != nil { if errors.Is(err, model.ErrRecordNotFound) { From 1230bd1f74f35f8668ddfc1ef8e81e126819a122 Mon Sep 17 00:00:00 2001 From: cp-20 <47262658+mario-hsp@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:16:02 +0900 Subject: [PATCH 4/5] Revert "Revert "add validator to PATCH"" This reverts commit 747217363577196ef8f07a83df412f0a0595e1be. --- router/questions.go | 12 ++++++++++++ router/responses.go | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/router/questions.go b/router/questions.go index fa6211e8..1b446686 100644 --- a/router/questions.go +++ b/router/questions.go @@ -61,6 +61,18 @@ func (q *Question) EditQuestion(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } + validate, err := getValidator(c) + if err != nil { + c.Logger().Errorf("failed to get validator: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + err = validate.Struct(req) + if err != nil { + c.Logger().Infof("validation failed: %+v", err) + return echo.NewHTTPError(http.StatusBadRequest, err) + } + switch req.QuestionType { case "Text": //正規表現のチェック diff --git a/router/responses.go b/router/responses.go index 56d9bb10..81d11d64 100644 --- a/router/responses.go +++ b/router/responses.go @@ -242,6 +242,18 @@ func (r *Response) EditResponse(c echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest) } + validate, err := getValidator(c) + if err != nil { + c.Logger().Errorf("failed to get validator: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, err) + } + + err = validate.Struct(req) + if err != nil { + c.Logger().Infof("validation failed: %+v", err) + return echo.NewHTTPError(http.StatusBadRequest, err) + } + limit, err := r.GetQuestionnaireLimit(c.Request().Context(), req.ID) if err != nil { if errors.Is(err, model.ErrRecordNotFound) { From 63c0f82f3b75898e49b0ab80b621d0f398e90504 Mon Sep 17 00:00:00 2001 From: cp-20 <47262658+mario-hsp@users.noreply.github.com> Date: Mon, 4 Dec 2023 23:17:09 +0900 Subject: [PATCH 5/5] register `SetValidatorMiddleware` during TestEditResponse (`responses_test.go`) --- router/responses_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/responses_test.go b/router/responses_test.go index 6e21b804..2754bfbb 100644 --- a/router/responses_test.go +++ b/router/responses_test.go @@ -1684,7 +1684,7 @@ func TestEditResponse(t *testing.T) { } e := echo.New() - e.PATCH("/api/responses/:responseID", r.EditResponse, m.SetUserIDMiddleware, m.TraPMemberAuthenticate, func(next echo.HandlerFunc) echo.HandlerFunc { + e.PATCH("/api/responses/:responseID", r.EditResponse, m.SetUserIDMiddleware, m.SetValidatorMiddleware, m.TraPMemberAuthenticate, func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { responseID, err := strconv.Atoi(c.Param("responseID")) if err != nil {