Skip to content

GetQuestionnaireMyRemindStatusとEditQuestionnaireMyRemindStatusの再実装 #1303

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
May 19, 2025
Merged
44 changes: 9 additions & 35 deletions controller/questionnaire.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,49 +879,23 @@ func (q *Questionnaire) DeleteQuestionnaire(c echo.Context, questionnaireID int)
return nil
}

func (q *Questionnaire) GetQuestionnaireMyRemindStatus(c echo.Context, questionnaireID int) (bool, error) {
status, err := q.CheckRemindStatus(questionnaireID)
func (q *Questionnaire) GetQuestionnaireMyRemindStatus(c echo.Context, questionnaireID int, userID string) (bool, error) {
status, err := q.GetTargetsCancelStatus(c.Request().Context(), questionnaireID, []string{userID})
if err != nil {
c.Logger().Errorf("failed to check remind status: %+v", err)
return false, echo.NewHTTPError(http.StatusInternalServerError, "failed to check remind status")
}

return status, nil
return !status[0].IsCanceled, nil
}

func (q *Questionnaire) EditQuestionnaireMyRemindStatus(c echo.Context, questionnaireID int, isRemindEnabled bool) error {
if isRemindEnabled {
status, err := q.CheckRemindStatus(questionnaireID)
if err != nil {
c.Logger().Errorf("failed to check remind status: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to check remind status")
}
if status {
return nil
}

questionnaire, _, _, _, _, _, _, _, err := q.GetQuestionnaireInfo(c.Request().Context(), questionnaireID)
if err != nil {
if errors.Is(err, model.ErrRecordNotFound) {
c.Logger().Info("questionnaire not found")
return echo.NewHTTPError(http.StatusNotFound, "questionnaire not found")
}
c.Logger().Errorf("failed to get questionnaire: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to get questionnaire")
}

err = q.PushReminder(questionnaireID, &questionnaire.ResTimeLimit.Time)
if err != nil {
c.Logger().Errorf("failed to push reminder: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to push reminder")
}
} else {
err := q.DeleteReminder(questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete reminder: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to delete reminder")
}
func (q *Questionnaire) EditQuestionnaireMyRemindStatus(c echo.Context, questionnaireID int, userID string, isRemindEnabled bool) error {
err := q.UpdateTargetsCancelStatus(c.Request().Context(), questionnaireID, []string{userID}, !isRemindEnabled)
if err != nil {
c.Logger().Errorf("failed to update remind status: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, "failed to update remind status")
}

return nil
}

Expand Down
16 changes: 14 additions & 2 deletions handler/questionnaire.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,14 @@ func (h Handler) DeleteQuestionnaire(ctx echo.Context, questionnaireID openapi.Q

// (GET /questionnaires/{questionnaireID}/myRemindStatus)
func (h Handler) GetQuestionnaireMyRemindStatus(ctx echo.Context, questionnaireID openapi.QuestionnaireIDInPath) error {
userID, err := h.Middleware.GetUserID(ctx)
if err != nil {
ctx.Logger().Errorf("failed to get userID: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to get userID: %w", err))
}

res := openapi.QuestionnaireIsRemindEnabled{}
status, err := h.Questionnaire.GetQuestionnaireMyRemindStatus(ctx, questionnaireID)
status, err := h.Questionnaire.GetQuestionnaireMyRemindStatus(ctx, questionnaireID, userID)
if err != nil {
ctx.Logger().Errorf("failed to get questionnaire my remind status: %+v", err)
return err
Expand All @@ -111,13 +117,19 @@ func (h Handler) GetQuestionnaireMyRemindStatus(ctx echo.Context, questionnaireI

// (PATCH /questionnaires/{questionnaireID}/myRemindStatus)
func (h Handler) EditQuestionnaireMyRemindStatus(ctx echo.Context, questionnaireID openapi.QuestionnaireIDInPath) error {
userID, err := h.Middleware.GetUserID(ctx)
if err != nil {
ctx.Logger().Errorf("failed to get userID: %+v", err)
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("failed to get userID: %w", err))
}

params := openapi.EditQuestionnaireMyRemindStatusJSONRequestBody{}
if err := ctx.Bind(&params); err != nil {
ctx.Logger().Errorf("failed to bind request body: %+v", err)
return echo.NewHTTPError(http.StatusBadRequest, fmt.Errorf("failed to bind request body: %w", err))
}

err := h.Questionnaire.EditQuestionnaireMyRemindStatus(ctx, questionnaireID, params.IsRemindEnabled)
err = h.Questionnaire.EditQuestionnaireMyRemindStatus(ctx, questionnaireID, userID, params.IsRemindEnabled)
if err != nil {
ctx.Logger().Errorf("failed to edit questionnaire my remind status: %+v", err)
return err
Expand Down
3 changes: 2 additions & 1 deletion model/targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ type ITarget interface {
DeleteTargets(ctx context.Context, questionnaireID int) error
GetTargets(ctx context.Context, questionnaireIDs []int) ([]Targets, error)
IsTargetingMe(ctx context.Context, quesionnairID int, userID string) (bool, error)
CancelTargets(ctx context.Context, questionnaireID int, targets []string) error
GetTargetsCancelStatus(ctx context.Context, questionnaireID int, targets []string) ([]Targets, error)
UpdateTargetsCancelStatus(ctx context.Context, questionnaireID int, targets []string, cancelStatus bool) error
}
25 changes: 22 additions & 3 deletions model/targets_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,27 @@ func (*Target) IsTargetingMe(ctx context.Context, questionnairID int, userID str
return false, nil
}

// CancelTargets アンケートの対象をキャンセル(削除しない)
func (*Target) CancelTargets(ctx context.Context, questionnaireID int, targets []string) error {
func (*Target) GetTargetsCancelStatus(ctx context.Context, questionnaireID int, targets []string) ([]Targets, error) {
db, err := getTx(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get transaction: %w", err)
}

var cancelStatus []Targets
err = db.
Where("questionnaire_id = ? AND user_traqid IN (?)", questionnaireID, targets).
Find(&cancelStatus).Error
if err != nil {
return nil, fmt.Errorf("failed to get targets remind status: %w", err)
}
if len(cancelStatus) != len(targets) {
return nil, fmt.Errorf("not all targets found")
}

return cancelStatus, nil
}

func (*Target) UpdateTargetsCancelStatus(ctx context.Context, questionnaireID int, targets []string, cancelStatus bool) error {
db, err := getTx(ctx)
if err != nil {
return fmt.Errorf("failed to get transaction: %w", err)
Expand All @@ -114,7 +133,7 @@ func (*Target) CancelTargets(ctx context.Context, questionnaireID int, targets [
err = db.
Model(&Targets{}).
Where("questionnaire_id = ? AND user_traqid IN (?)", questionnaireID, targets).
Update("is_canceled", true).Error
Update("is_canceled", cancelStatus).Error
if err != nil {
return fmt.Errorf("failed to cancel targets: %w", err)
}
Expand Down
190 changes: 176 additions & 14 deletions model/targets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package model
import (
"context"
"errors"
"sort"
"testing"
"time"

Expand Down Expand Up @@ -377,9 +378,137 @@ func TestIsTargetingMe(t *testing.T) {
}
}

func TestCancelTargets(t *testing.T) {
func TestGetTargetsCancelStatus(t *testing.T) {
t.Parallel()

assertion := assert.New(t)

ctx := context.Background()

type test struct {
description string
validTargets []string // is_canceled = false
invalidTargets []string // is_canceled = true
argTargets []string
argTargetsValid []bool
isErr bool
err error
}

testCases := []test{
{
description: "all valid targets",
validTargets: []string{"a", "b"},
invalidTargets: []string{},
argTargets: []string{"a"},
argTargetsValid: []bool{true},
},
{
description: "all invalid targets",
validTargets: []string{},
invalidTargets: []string{"a", "b"},
argTargets: []string{"b"},
argTargetsValid: []bool{false},
},
{
description: "both valid and invalid targets",
validTargets: []string{"a", "b"},
invalidTargets: []string{"c", "d"},
argTargets: []string{"a", "c"},
argTargetsValid: []bool{true, false},
},
{
description: "both valid and invalid targets changed order",
validTargets: []string{"a", "b"},
invalidTargets: []string{"c", "d"},
argTargets: []string{"b", "a", "d"},
argTargetsValid: []bool{true, true, false},
},
{
description: "both valid and invalid targets changed order",
validTargets: []string{"a", "b"},
invalidTargets: []string{"c", "d"},
argTargets: []string{"b", "d", "a"},
argTargetsValid: []bool{true, false, true},
},
{
description: "argTargets with target not in db",
validTargets: []string{"a", "b"},
invalidTargets: []string{"c", "d"},
argTargets: []string{"e"},
isErr: true,
},
{
description: "argTargets with some of target not in db",
validTargets: []string{"a", "b"},
invalidTargets: []string{"c", "d"},
argTargets: []string{"a", "e"},
isErr: true,
},
}

for _, testCase := range testCases {
t.Run(testCase.description, func(t *testing.T) {
targets := make([]Targets, 0, len(testCase.validTargets)+len(testCase.invalidTargets))
for _, target := range testCase.validTargets {
targets = append(targets, Targets{
UserTraqid: target,
IsCanceled: false,
})
}
for _, target := range testCase.invalidTargets {
targets = append(targets, Targets{
UserTraqid: target,
IsCanceled: true,
})
}
questionnaire := Questionnaires{
Targets: targets,
}
err := db.
Session(&gorm.Session{}).
Create(&questionnaire).Error
if err != nil {
t.Errorf("failed to create questionnaire: %v", err)
}

cancelStatus, err := targetImpl.GetTargetsCancelStatus(ctx, questionnaire.ID, testCase.argTargets)
if !testCase.isErr {
assertion.NoError(err, testCase.description, "no error")
} else if testCase.err != nil {
assertion.Equal(true, errors.Is(err, testCase.err), testCase.description, "errorIs")
} else {
assertion.Error(err, testCase.description, "any error")
}
if err != nil {
return
}

var actualCancelStatus []Targets
for i := range testCase.argTargets {
actualCancelStatus = append(actualCancelStatus, Targets{
QuestionnaireID: questionnaire.ID,
UserTraqid: testCase.argTargets[i],
IsCanceled: !testCase.argTargetsValid[i],
})
}

sort.Slice(cancelStatus, func(i, j int) bool {
return cancelStatus[i].UserTraqid < cancelStatus[j].UserTraqid
})
sort.Slice(actualCancelStatus, func(i, j int) bool {
return actualCancelStatus[i].UserTraqid < actualCancelStatus[j].UserTraqid
})
assert.Equal(t, cancelStatus, actualCancelStatus, testCase.description, "cancelStatus")
})
}
}

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

assertion := assert.New(t)

ctx := context.Background()

type test struct {
Expand All @@ -388,7 +517,8 @@ func TestCancelTargets(t *testing.T) {
beforeInvalidTargets []string
afterValidTargets []string
afterInvalidTargets []string
argCancelTargets []string
argUpdateTargets []string
argCancelStatus bool
isErr bool
err error
}
Expand All @@ -400,32 +530,62 @@ func TestCancelTargets(t *testing.T) {
beforeInvalidTargets: []string{},
afterValidTargets: []string{},
afterInvalidTargets: []string{"a"},
argCancelTargets: []string{"a"},
argUpdateTargets: []string{"a"},
argCancelStatus: true,
},
{
description: "キャンセルするtargetが複数でエラーなし",
beforeValidTargets: []string{"a", "b"},
beforeInvalidTargets: []string{},
afterValidTargets: []string{},
afterInvalidTargets: []string{"a", "b"},
argCancelTargets: []string{"a", "b"},
argUpdateTargets: []string{"a", "b"},
argCancelStatus: true,
},
{
description: "キャンセルするtargetがないときエラーなし",
beforeValidTargets: []string{"a"},
beforeInvalidTargets: []string{},
afterValidTargets: []string{"a"},
afterInvalidTargets: []string{},
argCancelTargets: []string{},
argUpdateTargets: []string{},
argCancelStatus: true,
},
{
description: "キャンセルするtargetが見つからないときエラー",
description: "キャンセルするtargetが見つからないときエラーなし",
beforeValidTargets: []string{"a"},
beforeInvalidTargets: []string{},
afterValidTargets: []string{"a"},
afterInvalidTargets: []string{},
argCancelTargets: []string{"b"},
isErr: true,
argUpdateTargets: []string{"b"},
argCancelStatus: true,
},
{
description: "再開するtargetが1人でエラーなし",
beforeValidTargets: []string{"a"},
beforeInvalidTargets: []string{"b"},
afterValidTargets: []string{"a", "b"},
afterInvalidTargets: []string{},
argUpdateTargets: []string{"b"},
argCancelStatus: false,
},
{
description: "再開するtargetが複数でエラーなし",
beforeValidTargets: []string{},
beforeInvalidTargets: []string{"a", "b"},
afterValidTargets: []string{"a", "b"},
afterInvalidTargets: []string{},
argUpdateTargets: []string{"a", "b"},
argCancelStatus: false,
},
{
description: "再開するtargetがないときエラーなし",
beforeValidTargets: []string{"a"},
beforeInvalidTargets: []string{},
afterValidTargets: []string{"a"},
afterInvalidTargets: []string{},
argUpdateTargets: []string{},
argCancelStatus: false,
},
}

Expand Down Expand Up @@ -454,13 +614,15 @@ func TestCancelTargets(t *testing.T) {
t.Errorf("failed to create questionnaire: %v", err)
}

err = targetImpl.CancelTargets(ctx, questionnaire.ID, testCase.argCancelTargets)
err = targetImpl.UpdateTargetsCancelStatus(ctx, questionnaire.ID, testCase.argUpdateTargets, testCase.argCancelStatus)
if !testCase.isErr {
assertion.NoError(err, testCase.description, "no error")
} else if testCase.err != nil {
assertion.Equal(true, errors.Is(err, testCase.err), testCase.description, "errorIs")
} else {
assertion.Error(err, testCase.description, "any error")
}
if err != nil {
if !testCase.isErr {
t.Errorf("unexpected error: %v", err)
} else if !errors.Is(err, testCase.err) {
t.Errorf("invalid error: expected: %+v, actual: %+v", testCase.err, err)
}
return
}

Expand Down