Skip to content

fix: delete all the related info when a questionnaire is deleted#1441

Merged
Eraxyso merged 19 commits intofix/openapifrom
fix/delete-target-and-user-when-questionnaire-is-deleted
Apr 4, 2026
Merged

fix: delete all the related info when a questionnaire is deleted#1441
Eraxyso merged 19 commits intofix/openapifrom
fix/delete-target-and-user-when-questionnaire-is-deleted

Conversation

@Eraxyso
Copy link
Copy Markdown
Contributor

@Eraxyso Eraxyso commented Feb 11, 2026

  • Inspect questionnaire deletion flow and respondent draft handling
  • Review tests around questionnaire deletion and draft respondents
  • Update deletion logic to include draft respondents and soft-deleted questions cleanup
  • Add/adjust tests to cover related record cleanup and draft deletions
  • Run targeted tests (model/controller tests fail locally: missing DB env; golangci-lint fails: Go version mismatch)

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 11, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 09b6734b-1dab-4db4-8238-4b5510bcf8b0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/delete-target-and-user-when-questionnaire-is-deleted

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Eraxyso Eraxyso changed the title fix: delete target users and groups when a questionnaire is deleted fix: delete all the related info when a questionnaire is deleted Feb 11, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 11, 2026

Codecov Report

❌ Patch coverage is 27.65957% with 34 lines in your changes missing coverage. Please review.
✅ Project coverage is 66.72%. Comparing base (bc34d44) to head (b1b7237).
⚠️ Report is 24 commits behind head on fix/openapi.

Files with missing lines Patch % Lines
controller/questionnaire.go 26.08% 25 Missing and 9 partials ⚠️
Additional details and impacted files
@@               Coverage Diff               @@
##           fix/openapi    #1441      +/-   ##
===============================================
- Coverage        67.16%   66.72%   -0.44%     
===============================================
  Files               25       25              
  Lines             3706     3751      +45     
===============================================
+ Hits              2489     2503      +14     
- Misses             851      874      +23     
- Partials           366      374       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…tions, scale labels, validations, and responses
@Eraxyso Eraxyso force-pushed the fix/delete-target-and-user-when-questionnaire-is-deleted branch from ecf3331 to 8d23446 Compare February 11, 2026 19:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to ensure that deleting a questionnaire also deletes its related data (targets/admin metadata, questions’ auxiliary records, and respondents’ data) so the system doesn’t retain orphaned records.

Changes:

  • Add deletion of target user/group selections and administrator user/group selections when deleting a questionnaire.
  • Add deletion of question-related auxiliary records (options, scale labels, validations) before deleting questions.
  • Add deletion of responses and respondents associated with the questionnaire.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +961 to +973
respondentDetails, err := q.GetRespondentDetails(ctx, questionnaireID, "", false, "", nil)
if err != nil {
c.Logger().Errorf("failed to get respondent details: %+v", err)
return err
}
for _, respondentDetail := range respondentDetails {
err = q.IResponse.DeleteResponse(ctx, respondentDetail.ResponseID)
if err != nil {
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
}

err = q.DeleteRespondent(ctx, respondentDetail.ResponseID)
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetRespondentDetails currently filters respondents with submitted_at IS NOT NULL (see model/respondents_impl.go), so this will only delete submitted responses and will leave draft respondents (submitted_at NULL) and their related records behind. For questionnaire deletion, consider fetching all respondent responseIDs for the questionnaire (draft + submitted) via a dedicated repository method (e.g., ListRespondentResponseIDsByQuestionnaireID) or by updating the underlying query to not exclude drafts when isDraft == nil.

Suggested change
respondentDetails, err := q.GetRespondentDetails(ctx, questionnaireID, "", false, "", nil)
if err != nil {
c.Logger().Errorf("failed to get respondent details: %+v", err)
return err
}
for _, respondentDetail := range respondentDetails {
err = q.IResponse.DeleteResponse(ctx, respondentDetail.ResponseID)
if err != nil {
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
}
err = q.DeleteRespondent(ctx, respondentDetail.ResponseID)
responseIDs, err := q.ListRespondentResponseIDsByQuestionnaireID(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to list respondent response IDs: %+v", err)
return err
}
for _, responseID := range responseIDs {
err = q.IResponse.DeleteResponse(ctx, responseID)
if err != nil {
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
}
err = q.DeleteRespondent(ctx, responseID)

Copilot uses AI. Check for mistakes.
Comment on lines +969 to +970
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IResponse.DeleteResponse returns an error when RowsAffected == 0. Since posting/editing a draft can create a respondent with len(responseMetas)==0 (no rows in the response table yet), deleting the questionnaire can fail here with ErrNoRecordDeleted. Treat "no response rows" as a successful deletion in this flow (e.g., ignore model.ErrNoRecordDeleted/model.ErrRecordNotFound for responses, or add a repository method that deletes by response_id without requiring affected rows).

Suggested change
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
// Treat "no response rows" as a successful deletion for this flow.
if errors.Is(err, model.ErrNoRecordDeleted) || errors.Is(err, model.ErrRecordNotFound) {
// nothing to delete; continue with next respondent
} else {
c.Logger().Errorf("failed to delete responses: %+v", err)
return err
}

Copilot uses AI. Check for mistakes.
Comment on lines +897 to +925
err = q.DeleteTargetUsers(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete target users: %+v", err)
return err
}

err = q.DeleteTargetGroups(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete target groups: %+v", err)
return err
}

err = q.DeleteAdministrators(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete administrators: %+v", err)
return err
}

err = q.DeleteAdministratorUsers(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete administrator users: %+v", err)
return err
}
err = q.DeleteAdministratorGroups(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to delete administrator groups: %+v", err)
return err
}

Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This endpoint now performs a lot of additional cleanup (targets_users/groups, administrator_users/groups, options/scale_labels/validations, respondents/responses), but controller/questionnaire_test.go's DeleteQuestionnaire coverage appears to only assert that the questionnaire itself is deleted. Please add/update tests to verify the related records are removed as well (including draft respondents).

Copilot uses AI. Check for mistakes.
Comment on lines 926 to 930
questions, err := q.GetQuestions(ctx, questionnaireID)
if err != nil {
c.Logger().Errorf("failed to get questions: %+v", err)
return err
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetQuestions uses GORM's default scope, so it will not return soft-deleted questions (questions.deleted_at IS NULL). If a questionnaire previously had questions removed (soft-deleted via DeleteQuestion), this loop won’t delete their dependent rows (options / scale_labels / validations / responses), leaving data behind even after the questionnaire is deleted. Consider deleting question-related rows by questionnaire_id (via a join/subquery) or adding a repository method to fetch question IDs Unscoped() for cleanup.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@kaitoyama kaitoyama left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

論理削除に持っていくでもいいかもとは思いますが、これでもいいと思います。ただ、下書き削除されるかだけ確認お願いします

@Eraxyso
Copy link
Copy Markdown
Contributor Author

Eraxyso commented Mar 28, 2026

@copilot 確認をお願いします

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 28, 2026

論理削除に持っていくでもいいかもとは思いますが、これでもいいと思います。ただ、下書き削除されるかだけ確認お願いします

下書き回答も削除対象になるよう response_id を全件取得して削除するように変更し、下書き(レスポンス未作成)を含む削除テストを追加しました。commit: 86df68c

@copilot 確認をお願いします

削除時に下書き回答も含めた関連データを消すよう修正し、テストも追加しました。commit: 86df68c

@Eraxyso Eraxyso force-pushed the fix/delete-target-and-user-when-questionnaire-is-deleted branch from bf1dbcc to a9e7c73 Compare March 28, 2026 11:43
@Eraxyso Eraxyso force-pushed the fix/delete-target-and-user-when-questionnaire-is-deleted branch from 74ee40b to 705d59d Compare March 28, 2026 19:29
@Eraxyso Eraxyso merged commit dfbe896 into fix/openapi Apr 4, 2026
9 of 11 checks passed
@Eraxyso Eraxyso deleted the fix/delete-target-and-user-when-questionnaire-is-deleted branch April 4, 2026 09:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants