From 98c1c5521957a0d32524202f6d84016ec96a3d91 Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:59:22 +0900 Subject: [PATCH 1/9] rough implmented ispublished --- model/questionnaires.go | 5 ++- model/questionnaires_impl.go | 32 ++++++++++++-- model/questionnaires_test.go | 85 ++++++++++++++++++++++++++++++++++-- model/respondents_test.go | 19 ++++---- model/responses_test.go | 4 +- model/scale_labels_test.go | 10 ++--- model/validations_test.go | 8 ++-- router.go | 6 +-- router/middleware.go | 55 ++++++++++++++++++++++- router/middleware_test.go | 73 +++++++++++++++++++++++++++++++ router/questionnaires.go | 27 ++++++------ 11 files changed, 276 insertions(+), 48 deletions(-) diff --git a/model/questionnaires.go b/model/questionnaires.go index ec759d2e..f5131c34 100644 --- a/model/questionnaires.go +++ b/model/questionnaires.go @@ -10,8 +10,8 @@ import ( // IQuestionnaire QuestionnaireのRepository type IQuestionnaire interface { - InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error) - UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error + InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isPublished bool) (int, error) + UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int, isPublished bool) error DeleteQuestionnaire(ctx context.Context, questionnaireID int) error GetQuestionnaires(ctx context.Context, userID string, sort string, search string, pageNum int, nontargeted bool) ([]QuestionnaireInfo, int, error) GetAdminQuestionnaires(ctx context.Context, userID string) ([]Questionnaires, error) @@ -21,4 +21,5 @@ type IQuestionnaire interface { GetQuestionnaireLimitByResponseID(ctx context.Context, responseID int) (null.Time, error) GetResponseReadPrivilegeInfoByResponseID(ctx context.Context, userID string, responseID int) (*ResponseReadPrivilegeInfo, error) GetResponseReadPrivilegeInfoByQuestionnaireID(ctx context.Context, userID string, questionnaireID int) (*ResponseReadPrivilegeInfo, error) + CheckQuestionnairePublished(ctx context.Context, questionnaireID int) (bool, error) } diff --git a/model/questionnaires_impl.go b/model/questionnaires_impl.go index e7756bc7..a1e07b9a 100755 --- a/model/questionnaires_impl.go +++ b/model/questionnaires_impl.go @@ -33,6 +33,7 @@ type Questionnaires struct { Targets []Targets `json:"-" gorm:"foreignKey:QuestionnaireID"` Questions []Questions `json:"-" gorm:"foreignKey:QuestionnaireID"` Respondents []Respondents `json:"-" gorm:"foreignKey:QuestionnaireID"` + IsPublished bool `json:"is_published" gorm:"type:boolean;default:false"` } // BeforeCreate Update時に自動でmodified_atを現在時刻に @@ -79,7 +80,7 @@ type ResponseReadPrivilegeInfo struct { } // InsertQuestionnaire アンケートの追加 -func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string) (int, error) { +func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, isPublished bool) (int, error) { db, err := getTx(ctx) if err != nil { return 0, fmt.Errorf("failed to get tx: %w", err) @@ -91,6 +92,7 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des Title: title, Description: description, ResSharedTo: resSharedTo, + IsPublished: isPublished, } } else { questionnaire = Questionnaires{ @@ -98,6 +100,7 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des Description: description, ResTimeLimit: resTimeLimit, ResSharedTo: resSharedTo, + IsPublished: isPublished, } } @@ -110,7 +113,7 @@ func (*Questionnaire) InsertQuestionnaire(ctx context.Context, title string, des } // UpdateQuestionnaire アンケートの更新 -func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int) error { +func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, description string, resTimeLimit null.Time, resSharedTo string, questionnaireID int, isPublished bool) error { db, err := getTx(ctx) if err != nil { return fmt.Errorf("failed to get tx: %w", err) @@ -123,6 +126,7 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des Description: description, ResTimeLimit: resTimeLimit, ResSharedTo: resSharedTo, + IsPublished: isPublished, } } else { questionnaire = map[string]interface{}{ @@ -130,6 +134,7 @@ func (*Questionnaire) UpdateQuestionnaire(ctx context.Context, title string, des "description": description, "res_time_limit": gorm.Expr("NULL"), "res_shared_to": resSharedTo, + "is_published": isPublished, } } @@ -184,7 +189,7 @@ func (*Questionnaire) GetQuestionnaires(ctx context.Context, userID string, sort query := db. Table("questionnaires"). - Where("deleted_at IS NULL"). + Where("deleted_at IS NULL AND is_published IS TRUE"). Joins("LEFT OUTER JOIN targets ON questionnaires.id = targets.questionnaire_id") query, err = setQuestionnairesOrder(query, sort) @@ -458,6 +463,27 @@ func (*Questionnaire) GetResponseReadPrivilegeInfoByQuestionnaireID(ctx context. return &responseReadPrivilegeInfo, nil } +func (*Questionnaire) CheckQuestionnairePublished(ctx context.Context, questionnaireID int) (bool, error) { + db, err := getTx(ctx) + if err != nil { + return false, fmt.Errorf("failed to get tx: %w", err) + } + + var questionnaire Questionnaires + err = db. + Where("id = ?", questionnaireID). + Select("is_published"). + First(&questionnaire).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, ErrRecordNotFound + } + if err != nil { + return false, fmt.Errorf("failed to get the questionnaires: %w", err) + } + + return questionnaire.IsPublished, nil +} + func setQuestionnairesOrder(query *gorm.DB, sort string) (*gorm.DB, error) { switch sort { case "created_at": diff --git a/model/questionnaires_test.go b/model/questionnaires_test.go index 16b80506..01cda07e 100644 --- a/model/questionnaires_test.go +++ b/model/questionnaires_test.go @@ -67,6 +67,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(questionnairesNow, true), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -80,6 +81,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "respondents", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -93,6 +95,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "administrators", + IsPublished: true, CreatedAt: questionnairesNow.Add(time.Second), ModifiedAt: questionnairesNow.Add(2 * time.Second), }, @@ -106,6 +109,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -126,6 +130,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -145,6 +150,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow.Add(2 * time.Second), ModifiedAt: questionnairesNow.Add(3 * time.Second), }, @@ -158,6 +164,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, DeletedAt: gorm.DeletedAt{ @@ -177,6 +184,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow.Add(time.Duration(len(datas)) * time.Second), ModifiedAt: questionnairesNow, }, @@ -191,6 +199,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow.Add(2 * time.Second), ModifiedAt: questionnairesNow.Add(3 * time.Second), }, @@ -203,6 +212,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -222,6 +232,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -240,6 +251,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(questionnairesNow, true), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -259,6 +271,7 @@ func setupQuestionnairesTest(t *testing.T) { Description: "第1回集会らん☆ぷろ参加者募集", ResTimeLimit: null.NewTime(time.Time{}, false), ResSharedTo: "public", + IsPublished: true, CreatedAt: questionnairesNow, ModifiedAt: questionnairesNow, }, @@ -355,6 +368,7 @@ func insertQuestionnaireTest(t *testing.T) { description string resTimeLimit null.Time resSharedTo string + isPublished bool } type expect struct { isErr bool @@ -375,6 +389,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, { @@ -384,6 +399,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, }, { @@ -393,6 +409,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "respondents", + isPublished: true, }, }, { @@ -402,6 +419,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "administrators", + isPublished: true, }, }, { @@ -411,6 +429,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, { @@ -420,6 +439,7 @@ func insertQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, expect: expect{ isErr: true, @@ -432,6 +452,7 @@ func insertQuestionnaireTest(t *testing.T) { description: strings.Repeat("a", 2000), resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, { @@ -441,17 +462,28 @@ func insertQuestionnaireTest(t *testing.T) { description: strings.Repeat("a", 200000), resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, expect: expect{ isErr: true, }, }, + { + description: "not published questionnaire", + args: args{ + title: "第1回集会らん☆ぷろ募集アンケート", + description: "第1回集会らん☆ぷろ参加者募集", + resTimeLimit: null.NewTime(time.Time{}, false), + resSharedTo: "public", + isPublished: false, + }, + }, } for _, testCase := range testCases { ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, testCase.args.title, testCase.args.description, testCase.args.resTimeLimit, testCase.args.resSharedTo) + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, testCase.args.title, testCase.args.description, testCase.args.resTimeLimit, testCase.args.resSharedTo, testCase.args.isPublished) if !testCase.expect.isErr { assertion.NoError(err, testCase.description, "no error") @@ -475,6 +507,7 @@ func insertQuestionnaireTest(t *testing.T) { assertion.Equal(testCase.args.description, questionnaire.Description, testCase.description, "description") assertion.WithinDuration(testCase.args.resTimeLimit.ValueOrZero(), questionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "res_time_limit") assertion.Equal(testCase.args.resSharedTo, questionnaire.ResSharedTo, testCase.description, "res_shared_to") + assertion.Equal(testCase.args.isPublished, questionnaire.IsPublished, testCase.description, "is_published") assertion.WithinDuration(time.Now(), questionnaire.CreatedAt, 2*time.Second, testCase.description, "created_at") assertion.WithinDuration(time.Now(), questionnaire.ModifiedAt, 2*time.Second, testCase.description, "modified_at") @@ -491,6 +524,7 @@ func updateQuestionnaireTest(t *testing.T) { description string resTimeLimit null.Time resSharedTo string + isPublished bool } type expect struct { isErr bool @@ -512,12 +546,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "respondents", + isPublished: true, }, }, { @@ -527,12 +563,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第2回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, { @@ -542,12 +580,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第2回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, { @@ -557,12 +597,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "respondents", + isPublished: true, }, }, { @@ -572,12 +614,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第2回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, }, { @@ -587,12 +631,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第2回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, }, { @@ -602,12 +648,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, }, { @@ -617,12 +665,14 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now().Add(time.Minute), true), resSharedTo: "public", + isPublished: true, }, }, { @@ -632,12 +682,31 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, + }, + after: args{ + title: "第1回集会らん☆ぷろ募集アンケート", + description: "第1回集会らん☆ぷろ参加者募集", + resTimeLimit: null.NewTime(time.Time{}, false), + resSharedTo: "public", + isPublished: true, + }, + }, + { + description: "update is_published(false->true)", + before: args{ + title: "第1回集会らん☆ぷろ募集アンケート", + description: "第1回集会らん☆ぷろ参加者募集", + resTimeLimit: null.NewTime(time.Time{}, false), + resSharedTo: "public", + isPublished: false, }, after: args{ title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, } @@ -651,6 +720,7 @@ func updateQuestionnaireTest(t *testing.T) { Description: before.description, ResTimeLimit: before.resTimeLimit, ResSharedTo: before.resSharedTo, + IsPublished: before.isPublished, } err := db. Session(&gorm.Session{NewDB: true}). @@ -662,7 +732,7 @@ func updateQuestionnaireTest(t *testing.T) { createdAt := questionnaire.CreatedAt questionnaireID := questionnaire.ID after := &testCase.after - err = questionnaireImpl.UpdateQuestionnaire(ctx, after.title, after.description, after.resTimeLimit, after.resSharedTo, questionnaireID) + err = questionnaireImpl.UpdateQuestionnaire(ctx, after.title, after.description, after.resTimeLimit, after.resSharedTo, questionnaireID, after.isPublished) if !testCase.expect.isErr { assertion.NoError(err, testCase.description, "no error") @@ -686,6 +756,7 @@ func updateQuestionnaireTest(t *testing.T) { assertion.Equal(after.description, questionnaire.Description, testCase.description, "description") assertion.WithinDuration(after.resTimeLimit.ValueOrZero(), questionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "res_time_limit") assertion.Equal(after.resSharedTo, questionnaire.ResSharedTo, testCase.description, "res_shared_to") + assertion.Equal(after.isPublished, questionnaire.IsPublished, testCase.description, "is_published") assertion.WithinDuration(createdAt, questionnaire.CreatedAt, 2*time.Second, testCase.description, "created_at") assertion.WithinDuration(time.Now(), questionnaire.ModifiedAt, 2*time.Second, testCase.description, "modified_at") @@ -714,19 +785,21 @@ func updateQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, { title: "第1回集会らん☆ぷろ募集アンケート", description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Now(), true), resSharedTo: "public", + isPublished: true, }, } for _, arg := range invalidTestCases { ctx := context.Background() - err := questionnaireImpl.UpdateQuestionnaire(ctx, arg.title, arg.description, arg.resTimeLimit, arg.resSharedTo, invalidQuestionnaireID) + err := questionnaireImpl.UpdateQuestionnaire(ctx, arg.title, arg.description, arg.resTimeLimit, arg.resSharedTo, invalidQuestionnaireID, arg.isPublished) if !errors.Is(err, ErrNoRecordUpdated) { if err == nil { t.Errorf("Succeeded with invalid questionnaireID") @@ -747,6 +820,7 @@ func deleteQuestionnaireTest(t *testing.T) { description string resTimeLimit null.Time resSharedTo string + isPublished bool } type expect struct { isErr bool @@ -764,6 +838,7 @@ func deleteQuestionnaireTest(t *testing.T) { description: "第1回集会らん☆ぷろ参加者募集", resTimeLimit: null.NewTime(time.Time{}, false), resSharedTo: "public", + isPublished: true, }, }, } @@ -776,6 +851,7 @@ func deleteQuestionnaireTest(t *testing.T) { Description: testCase.args.description, ResTimeLimit: testCase.args.resTimeLimit, ResSharedTo: testCase.args.resSharedTo, + IsPublished: testCase.args.isPublished, } err := db. Session(&gorm.Session{NewDB: true}). @@ -1070,7 +1146,7 @@ func getQuestionnairesTest(t *testing.T) { err = db. Session(&gorm.Session{NewDB: true}). Model(&Questionnaires{}). - Where("deleted_at IS NULL"). + Where("deleted_at IS NULL AND is_published IS TRUE"). Count(&questionnaireNum).Error if err != nil { t.Errorf("failed to count questionnaire(%s): %v", testCase.description, err) @@ -1341,6 +1417,7 @@ func getQuestionnaireInfoTest(t *testing.T) { assertion.Equal(testCase.expect.questionnaire.Title, actualQuestionnaire.Title, testCase.description, "questionnaire(Title)") assertion.Equal(testCase.expect.questionnaire.Description, actualQuestionnaire.Description, testCase.description, "questionnaire(Description)") assertion.Equal(testCase.expect.questionnaire.ResSharedTo, actualQuestionnaire.ResSharedTo, testCase.description, "questionnaire(ResSharedTo)") + assertion.Equal(testCase.expect.questionnaire.IsPublished, actualQuestionnaire.IsPublished, testCase.description, "questionnaire(IsPublished)") assertion.WithinDuration(testCase.expect.questionnaire.ResTimeLimit.ValueOrZero(), actualQuestionnaire.ResTimeLimit.ValueOrZero(), 2*time.Second, testCase.description, "questionnaire(ResTimeLimit)") assertion.WithinDuration(testCase.expect.questionnaire.CreatedAt, actualQuestionnaire.CreatedAt, 2*time.Second, testCase.description, "questionnaire(CreatedAt)") assertion.WithinDuration(testCase.expect.questionnaire.ModifiedAt, actualQuestionnaire.ModifiedAt, 2*time.Second, testCase.description, "questionnaire(ModifiedAt)") diff --git a/model/respondents_test.go b/model/respondents_test.go index cafc7dd7..e7a029da 100644 --- a/model/respondents_test.go +++ b/model/respondents_test.go @@ -19,7 +19,7 @@ func TestInsertRespondent(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -129,7 +129,7 @@ func TestUpdateSubmittedAt(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -203,7 +203,7 @@ func TestDeleteRespondent(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -290,6 +290,7 @@ func TestGetRespondent(t *testing.T) { Description: "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", ResTimeLimit: null.NewTime(time.Now(), false), ResSharedTo: "private", + IsPublished: true, } err := db. Session(&gorm.Session{NewDB: true}). @@ -390,9 +391,9 @@ func TestGetRespondentInfos(t *testing.T) { args expect } - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第2回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第2回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) - questionnaireID2, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第2回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID2, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第2回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) questionnaire := Questionnaires{} @@ -522,7 +523,7 @@ func TestGetRespondentDetail(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) questionnaire := Questionnaires{} @@ -619,7 +620,7 @@ func TestGetRespondentDetails(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) questionnaire := Questionnaires{} @@ -908,7 +909,7 @@ func TestGetRespondentsUserIDs(t *testing.T) { } questionnaireIDs := make([]int, 0, 3) for i := 0; i < 3; i++ { - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) questionnaireIDs = append(questionnaireIDs, questionnaireID) } @@ -996,7 +997,7 @@ func TestTestCheckRespondent(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "private", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) diff --git a/model/responses_test.go b/model/responses_test.go index 9790b350..ec0b5b1d 100644 --- a/model/responses_test.go +++ b/model/responses_test.go @@ -19,7 +19,7 @@ func TestInsertResponses(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -142,7 +142,7 @@ func TestDeleteResponse(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) diff --git a/model/scale_labels_test.go b/model/scale_labels_test.go index f4f79ccd..ad5ea98e 100644 --- a/model/scale_labels_test.go +++ b/model/scale_labels_test.go @@ -20,7 +20,7 @@ func TestInsertScaleLabel(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -163,7 +163,7 @@ func TestUpdateScaleLabel(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -283,7 +283,7 @@ func TestDeleteScaleLabel(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -371,7 +371,7 @@ func TestGetScaleLabels(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -489,7 +489,7 @@ func TestCheckScaleLabel(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) diff --git a/model/validations_test.go b/model/validations_test.go index a56434a3..54ab6548 100644 --- a/model/validations_test.go +++ b/model/validations_test.go @@ -20,7 +20,7 @@ func TestInsertValidation(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -212,7 +212,7 @@ func TestUpdateValidation(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -360,7 +360,7 @@ func TestDeleteValidation(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) @@ -458,7 +458,7 @@ func TestGetValidations(t *testing.T) { assertion := assert.New(t) ctx := context.Background() - questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public") + questionnaireID, err := questionnaireImpl.InsertQuestionnaire(ctx, "第1回集会らん☆ぷろ募集アンケート", "第1回メンバー集会でのらん☆ぷろで発表したい人を募集します らん☆ぷろで発表したい人あつまれー!", null.NewTime(time.Now(), false), "public", true) require.NoError(t, err) err = administratorImpl.InsertAdministrators(ctx, questionnaireID, []string{userOne}) diff --git a/router.go b/router.go index 389a4d19..af5141b9 100644 --- a/router.go +++ b/router.go @@ -35,11 +35,11 @@ func SetRouting(port string) { { apiQuestionnnaires.GET("", api.GetQuestionnaires, api.TrapRateLimitMiddlewareFunc()) apiQuestionnnaires.POST("", api.PostQuestionnaire) - apiQuestionnnaires.GET("/:questionnaireID", api.GetQuestionnaire) + apiQuestionnnaires.GET("/:questionnaireID", api.GetQuestionnaire, api.QuestionnaireReadAuthenticate) apiQuestionnnaires.PATCH("/:questionnaireID", api.EditQuestionnaire, api.QuestionnaireAdministratorAuthenticate) apiQuestionnnaires.DELETE("/:questionnaireID", api.DeleteQuestionnaire, api.QuestionnaireAdministratorAuthenticate) - apiQuestionnnaires.GET("/:questionnaireID/questions", api.GetQuestions) - apiQuestionnnaires.POST("/:questionnaireID/questions", api.PostQuestionByQuestionnaireID) + apiQuestionnnaires.GET("/:questionnaireID/questions", api.GetQuestions, api.QuestionnaireReadAuthenticate) + apiQuestionnnaires.POST("/:questionnaireID/questions", api.PostQuestionByQuestionnaireID, api.QuestionnaireReadAuthenticate) } apiQuestions := echoAPI.Group("/questions") diff --git a/router/middleware.go b/router/middleware.go index c1ee10f5..03a3f5d8 100644 --- a/router/middleware.go +++ b/router/middleware.go @@ -47,8 +47,11 @@ func (*Middleware) SetValidatorMiddleware(next echo.HandlerFunc) echo.HandlerFun } } -/* 消せないアンケートの発生を防ぐための管理者 -暫定的にハードコーディングで対応*/ +/* + 消せないアンケートの発生を防ぐための管理者 + +暫定的にハードコーディングで対応 +*/ var adminUserIDs = []string{"ryoha", "xxarupakaxx", "kaitoyama", "cp20", "itzmeowww"} // SetUserIDMiddleware X-Showcase-UserからユーザーIDを取得しセットする @@ -140,6 +143,54 @@ func (m *Middleware) QuestionnaireAdministratorAuthenticate(next echo.HandlerFun } } +// QuestionnaireReadAuthenticate アンケートの閲覧権限があるかの認証 +func (m *Middleware) QuestionnaireReadAuthenticate(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + userID, err := getUserID(c) + if err != nil { + c.Logger().Errorf("failed to get userID: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("ffailed to get userID: %w", err)) + } + + strQuestionnaireID := c.Param("questionnaireID") + questionnaireID, err := strconv.Atoi(strQuestionnaireID) + if err != nil { + c.Logger().Infof("failed to convert questionnaireID to int: %+v", err) + return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("invalid questionnaireID:%s(error: %w)", strQuestionnaireID, err)) + } + + isPublished, err := m.CheckQuestionnairePublished(c.Request().Context(), questionnaireID) + if err != nil { + c.Logger().Errorf("failed to check if questionnaire is published: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to check if questionnaire is published: %w", err)) + } + + if !isPublished { + for _, adminID := range adminUserIDs { + if userID == adminID { + c.Set(questionnaireIDKey, questionnaireID) + + return next(c) + } + } + + isAdmin, err := m.CheckQuestionnaireAdmin(c.Request().Context(), userID, questionnaireID) + if err != nil { + c.Logger().Errorf("failed to check questionnaire admin: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to check if you are administrator: %w", err)) + } + if !isAdmin { + return c.String(http.StatusForbidden, "You are not a administrator of this questionnaire.") + } + c.Set(questionnaireIDKey, questionnaireID) + + return next(c) + } + c.Set(questionnaireIDKey, questionnaireID) + return next(c) + } +} + // ResponseReadAuthenticate 回答閲覧権限があるかの認証 func (m *Middleware) ResponseReadAuthenticate(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { diff --git a/router/middleware_test.go b/router/middleware_test.go index 84503255..2e7bfdd5 100644 --- a/router/middleware_test.go +++ b/router/middleware_test.go @@ -169,6 +169,79 @@ func TestTraPMemberAuthenticate(t *testing.T) { } } +func TestQuestionnaireReadAuthenticate(t *testing.T) { + t.Parallel() + + assertion := assert.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockRespondent := mock_model.NewMockIRespondent(ctrl) + mockAdministrator := mock_model.NewMockIAdministrator(ctrl) + mockQuestionnaire := mock_model.NewMockIQuestionnaire(ctrl) + mockQuestion := mock_model.NewMockIQuestion(ctrl) + + middleware := NewMiddleware(mockAdministrator, mockRespondent, mockQuestion, mockQuestionnaire) + + type args struct { + userID string + questionnaireID int + isPublished bool + } + + type expect struct { + statusCode int + isCalled bool + } + + type test struct { + description string + args + expect + } + + testCases := []test{ + { + description: "公開されているアンケートなので通す", + args: args{ + userID: "user1", + questionnaireID: 1, + isPublished: true, + }, + expect: expect{ + statusCode: http.StatusOK, + isCalled: true, + }, + }, + } + + for _, testCase := range testCases { + e := echo.New() + req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/questionnaires/%d", testCase.args.questionnaireID), nil) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.SetPath("/questionnaires/:questionnaireID") + c.SetParamNames("questionnaireID") + c.SetParamValues(strconv.Itoa(testCase.args.questionnaireID)) + c.Set(userIDKey, testCase.args.userID) + + mockQuestionnaire. + EXPECT(). + CheckQuestionnairePublished(c.Request().Context(), testCase.args.questionnaireID). + Return(testCase.args.isPublished, nil) + + callChecker := CallChecker{} + + e.HTTPErrorHandler(middleware.QuestionnaireReadAuthenticate(callChecker.Handler)(c), c) + + assertion.Equal(testCase.expect.statusCode, rec.Code, testCase.description, "status code") + assertion.Equal(testCase.expect.isCalled, callChecker.IsCalled, testCase.description, "isCalled") + + } + +} + func TestResponseReadAuthenticate(t *testing.T) { t.Parallel() diff --git a/router/questionnaires.go b/router/questionnaires.go index b33db7f4..d9352178 100644 --- a/router/questionnaires.go +++ b/router/questionnaires.go @@ -145,6 +145,7 @@ type PostAndEditQuestionnaireRequest struct { Description string `json:"description"` ResTimeLimit null.Time `json:"res_time_limit"` ResSharedTo string `json:"res_shared_to" validate:"required,oneof=administrators respondents public"` + IsPublished bool `json:"is_published" validate:"required` Targets []string `json:"targets" validate:"dive,max=32"` Administrators []string `json:"administrators" validate:"required,min=1,dive,max=32"` } @@ -182,7 +183,7 @@ func (q *Questionnaire) PostQuestionnaire(c echo.Context) error { var questionnaireID int err = q.ITransaction.Do(c.Request().Context(), nil, func(ctx context.Context) error { - questionnaireID, err = q.InsertQuestionnaire(ctx, req.Title, req.Description, req.ResTimeLimit, req.ResSharedTo) + questionnaireID, err = q.InsertQuestionnaire(ctx, req.Title, req.Description, req.ResTimeLimit, req.ResSharedTo, req.IsPublished) if err != nil { c.Logger().Errorf("failed to insert a questionnaire: %+v", err) return err @@ -243,11 +244,10 @@ func (q *Questionnaire) PostQuestionnaire(c echo.Context) error { // GetQuestionnaire GET /questionnaires/:questionnaireID func (q *Questionnaire) GetQuestionnaire(c echo.Context) error { - strQuestionnaireID := c.Param("questionnaireID") - questionnaireID, err := strconv.Atoi(strQuestionnaireID) + questionnaireID, err := getQuestionnaireID(c) if err != nil { - c.Logger().Infof("failed to convert questionnaireID to int: %+v", err) - return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("invalid questionnaireID:%s(error: %w)", strQuestionnaireID, err)) + c.Logger().Errorf("failed to get questionnaireID: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError) } questionnaire, targets, administrators, respondents, err := q.GetQuestionnaireInfo(c.Request().Context(), questionnaireID) @@ -268,6 +268,7 @@ func (q *Questionnaire) GetQuestionnaire(c echo.Context) error { "created_at": questionnaire.CreatedAt.Format(time.RFC3339), "modified_at": questionnaire.ModifiedAt.Format(time.RFC3339), "res_shared_to": questionnaire.ResSharedTo, + "is_published": questionnaire.IsPublished, "targets": targets, "administrators": administrators, "respondents": respondents, @@ -276,11 +277,10 @@ func (q *Questionnaire) GetQuestionnaire(c echo.Context) error { // PostQuestionByQuestionnaireID POST /questionnaires/:questionnaireID/questions func (q *Questionnaire) PostQuestionByQuestionnaireID(c echo.Context) error { - strQuestionnaireID := c.Param("questionnaireID") - questionnaireID, err := strconv.Atoi(strQuestionnaireID) + questionnaireID, err := getQuestionnaireID(c) if err != nil { - c.Logger().Info("failed to convert questionnaireID to int: %+v", err) - return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("invalid questionnaireID:%s(error: %w)", strQuestionnaireID, err)) + c.Logger().Errorf("failed to get questionnaireID: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError) } req := PostAndEditQuestionRequest{} if err := c.Bind(&req); err != nil { @@ -409,7 +409,7 @@ func (q *Questionnaire) EditQuestionnaire(c echo.Context) error { } err = q.ITransaction.Do(c.Request().Context(), nil, func(ctx context.Context) error { - err = q.UpdateQuestionnaire(ctx, req.Title, req.Description, req.ResTimeLimit, req.ResSharedTo, questionnaireID) + err = q.UpdateQuestionnaire(ctx, req.Title, req.Description, req.ResTimeLimit, req.ResSharedTo, questionnaireID, req.IsPublished) if err != nil && !errors.Is(err, model.ErrNoRecordUpdated) { c.Logger().Errorf("failed to update questionnaire: %+v", err) return err @@ -498,11 +498,10 @@ func (q *Questionnaire) DeleteQuestionnaire(c echo.Context) error { // GetQuestions GET /questionnaires/:questionnaireID/questions func (q *Questionnaire) GetQuestions(c echo.Context) error { - strQuestionnaireID := c.Param("questionnaireID") - questionnaireID, err := strconv.Atoi(strQuestionnaireID) + questionnaireID, err := getQuestionnaireID(c) if err != nil { - c.Logger().Infof("failed to convert questionnaireID to int: %+v", err) - return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("invalid questionnaireID:%s(error: %w)", strQuestionnaireID, err)) + c.Logger().Errorf("failed to get questionnaireID: %+v", err) + return echo.NewHTTPError(http.StatusInternalServerError) } allquestions, err := q.IQuestion.GetQuestions(c.Request().Context(), questionnaireID) From b840e285da83fb2d5dcb038bbebf73bb0b472899 Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Fri, 19 Apr 2024 11:19:56 +0900 Subject: [PATCH 2/9] Fix isPublished parameter in TestPostQuestionnaire and TestEditQuestionnaire --- router/questionnaires_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index 8e934895..6bee0673 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -601,6 +601,7 @@ func TestPostQuestionnaire(t *testing.T) { testCase.request.Description, mockTimeLimit, testCase.request.ResSharedTo, + testCase.request.IsPublished, ). Return(testCase.questionnaireID, testCase.InsertQuestionnaireError) @@ -1490,6 +1491,7 @@ func TestEditQuestionnaire(t *testing.T) { mockTimeLimit, testCase.request.ResSharedTo, testCase.questionnaireID, + testCase.request.IsPublished, ). Return(testCase.InsertQuestionnaireError) From 045987df2647cfb2b83890963aaf88a8a0d9b342 Mon Sep 17 00:00:00 2001 From: cp-20 <47262658+mario-hsp@users.noreply.github.com> Date: Mon, 22 Apr 2024 11:45:03 +0900 Subject: [PATCH 3/9] fix test --- router/questionnaires_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index 6bee0673..fd674467 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -733,6 +733,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { description: "一般的なリクエストなので201", invalidRequest: false, request: PostAndEditQuestionRequest{ + QuestionnaireID: 1, QuestionType: "Text", QuestionNum: 1, PageNum: 1, From 4d634337aa942673e858f65b7de6bb5b9e0cf646 Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:33:57 +0900 Subject: [PATCH 4/9] fix test 2 --- router/questionnaires_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index fd674467..6d9cd52c 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -759,6 +759,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { { description: "questionIDが0でも201", request: PostAndEditQuestionRequest{ + QuestionnaireID: 1, QuestionType: "Text", QuestionNum: 1, PageNum: 1, From 8846b724034fe72533982a56b9126be77b4fdb79 Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:43:49 +0900 Subject: [PATCH 5/9] fix set questionnaireID --- router/questionnaires_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index 6d9cd52c..fb24560b 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -733,7 +733,6 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { description: "一般的なリクエストなので201", invalidRequest: false, request: PostAndEditQuestionRequest{ - QuestionnaireID: 1, QuestionType: "Text", QuestionNum: 1, PageNum: 1, @@ -759,7 +758,6 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { { description: "questionIDが0でも201", request: PostAndEditQuestionRequest{ - QuestionnaireID: 1, QuestionType: "Text", QuestionNum: 1, PageNum: 1, @@ -1168,7 +1166,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { c.SetParamValues(test.questionnaireID) - c.Set(questionnaireIDKey, test.request.QuestionnaireID) + c.Set(questionnaireIDKey, test.questionnaireID) if test.validator != "" { c.Set(test.validator, validator.New()) } else { From 65df70af7b3fa177ecfd54e8a77f975b93701d3b Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:46:28 +0900 Subject: [PATCH 6/9] fix type error --- router/questionnaires_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index fb24560b..c96fa43f 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -1166,7 +1166,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { c.SetParamValues(test.questionnaireID) - c.Set(questionnaireIDKey, test.questionnaireID) + c.Set(questionnaireIDKey, intQuestionnaireID) if test.validator != "" { c.Set(test.validator, validator.New()) } else { From e33d1a215ca4083a3ed55c62466a267191920bad Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:54:52 +0900 Subject: [PATCH 7/9] Fix error in TestPostQuestionByQuestionnaireID --- router/questionnaires_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index c96fa43f..7722f87c 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -1075,6 +1075,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { MaxBound: "10", }, InsertQuestionError: errors.New("正規表現が間違っています"), + questionnaireID: "1", ExecutesCreation: false, ExecutesCheckQuestionNum: false, expect: expect{ From b476e2ecb1a3e5fa4e417dc6d485cf95c4da0de6 Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:21:03 +0900 Subject: [PATCH 8/9] Fix ExecutesCheckQuestionNum in TestPostQuestionByQuestionnaireID --- router/questionnaires_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/questionnaires_test.go b/router/questionnaires_test.go index 7722f87c..4ab35aca 100644 --- a/router/questionnaires_test.go +++ b/router/questionnaires_test.go @@ -1077,7 +1077,7 @@ func TestPostQuestionByQuestionnaireID(t *testing.T) { InsertQuestionError: errors.New("正規表現が間違っています"), questionnaireID: "1", ExecutesCreation: false, - ExecutesCheckQuestionNum: false, + ExecutesCheckQuestionNum: true, expect: expect{ statusCode: http.StatusBadRequest, }, From 5af40c21ca2af33bf274d6da814e2238bade6dca Mon Sep 17 00:00:00 2001 From: kaitoyama <45167401+kaitoyama@users.noreply.github.com> Date: Thu, 2 May 2024 11:28:35 +0900 Subject: [PATCH 9/9] Add v3 migration for questionnaires table --- docs/db_schema.md | 1 + model/current.go | 4 +++- model/v3.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 model/v3.go diff --git a/docs/db_schema.md b/docs/db_schema.md index 45f30206..33238247 100644 --- a/docs/db_schema.md +++ b/docs/db_schema.md @@ -50,6 +50,7 @@ | res_shared_to | char(30) | NO | | administrators | | アンケートの結果を, 運営は見られる ("administrators"), 回答済みの人は見られる ("respondents") 誰でも見られる ("public") | | created_at | timestamp | NO | | CURRENT_TIMESTAMP | | アンケートが作成された日時 | | modified_at | timestamp | NO | | CURRENT_TIMESTAMP | | アンケートが更新された日時 | +| is_published | boolean | NO | | false | | アンケートが公開かどうか | ### respondents diff --git a/model/current.go b/model/current.go index 9b3fea3e..973a7fda 100644 --- a/model/current.go +++ b/model/current.go @@ -6,7 +6,9 @@ import ( // Migrations is all db migrations func Migrations() []*gormigrate.Migration { - return []*gormigrate.Migration{} + return []*gormigrate.Migration{ + v3(), + } } func AllTables() []interface{} { diff --git a/model/v3.go b/model/v3.go new file mode 100644 index 00000000..ebfd9478 --- /dev/null +++ b/model/v3.go @@ -0,0 +1,41 @@ +package model + +import ( + "time" + + "github.com/go-gormigrate/gormigrate/v2" + "gopkg.in/guregu/null.v4" + "gorm.io/gorm" +) + +func v3() *gormigrate.Migration { + return &gormigrate.Migration{ + ID: "3", + Migrate: func(tx *gorm.DB) error { + if err := tx.AutoMigrate(&v3Questionnaires{}); err != nil { + return err + } + return nil + }, + } +} + +type v3Questionnaires struct { + ID int `json:"questionnaireID" gorm:"type:int(11) AUTO_INCREMENT;not null;primaryKey"` + Title string `json:"title" gorm:"type:char(50);size:50;not null"` + Description string `json:"description" gorm:"type:text;not null"` + ResTimeLimit null.Time `json:"res_time_limit,omitempty" gorm:"type:TIMESTAMP NULL;default:NULL;"` + DeletedAt gorm.DeletedAt `json:"-" gorm:"type:TIMESTAMP NULL;default:NULL;"` + ResSharedTo string `json:"res_shared_to" gorm:"type:char(30);size:30;not null;default:administrators"` + CreatedAt time.Time `json:"created_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"` + ModifiedAt time.Time `json:"modified_at" gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP"` + Administrators []Administrators `json:"-" gorm:"foreignKey:QuestionnaireID"` + Targets []Targets `json:"-" gorm:"foreignKey:QuestionnaireID"` + Questions []Questions `json:"-" gorm:"foreignKey:QuestionnaireID"` + Respondents []Respondents `json:"-" gorm:"foreignKey:QuestionnaireID"` + IsPublished bool `json:"is_published" gorm:"type:boolean;default:false"` +} + +func (*v3Questionnaires) TableName() string { + return "questionnaires" +}