From f575252d92b8fc18bed7db7e4cf66ac5ebfba001 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Mon, 10 Nov 2025 12:43:13 -0500 Subject: [PATCH 1/4] Use --wait to ensure each job is queued up and doesn't exhaust quota --- .circleci/cron.sh | 60 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/.circleci/cron.sh b/.circleci/cron.sh index 2dd09a68d..148257870 100755 --- a/.circleci/cron.sh +++ b/.circleci/cron.sh @@ -2,7 +2,7 @@ # Fail if anything within this script returns # a non-zero exit code -set -e +# set -e echo "Logging into cloud.gov" cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE @@ -13,18 +13,20 @@ cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_S echo "Running tasks in Staging..." +STAGING_TASK="cf run-task touchpoints-staging-sidekiq-worker --wait -c" + # Users -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_one_week_until_inactivation_warning" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:deactivate_inactive_users" +$STAGING_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" +$STAGING_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" +$STAGING_TASK "rake scheduled_jobs:deactivate_inactive_users" # Forms -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_daily_notifications" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_weekly_notifications" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:archive_forms" -cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash" +$STAGING_TASK "rake scheduled_jobs:send_daily_notifications" +$STAGING_TASK "rake scheduled_jobs:send_weekly_notifications" +$STAGING_TASK "rake scheduled_jobs:check_expiring_forms" +$STAGING_TASK "rake scheduled_jobs:archive_forms" +$STAGING_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" +# $STAGING_TASK "rake scheduled_jobs:delete_submissions_trash" echo "Staging tasks have completed." @@ -33,19 +35,20 @@ echo "Staging tasks have completed." # echo "Running tasks in Demo..." +DEMO_TASK="cf run-task touchpoints-demo-sidekiq-worker --wait -c" # Users -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_one_week_until_inactivation_warning" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:deactivate_inactive_users" +$DEMO_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" +$DEMO_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" +$DEMO_TASK "rake scheduled_jobs:deactivate_inactive_users" # Forms -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_daily_notifications" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_weekly_notifications" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:archive_forms" -cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash" +$DEMO_TASK "rake scheduled_jobs:send_daily_notifications" +$DEMO_TASK "rake scheduled_jobs:send_weekly_notifications" +$DEMO_TASK "rake scheduled_jobs:check_expiring_forms" +$DEMO_TASK "rake scheduled_jobs:archive_forms" +$DEMO_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" +# $DEMO_TASK "rake scheduled_jobs:delete_submissions_trash" echo "Demo tasks have completed." @@ -60,18 +63,19 @@ cf login -a $CF_API_ENDPOINT -u $CF_PRODUCTION_SPACE_DEPLOYER_USERNAME -p $CF_PR echo "Running tasks in Production..." +PROD_TASK="cf run-task touchpoints-production-sidekiq-worker --wait -c" # Users -cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:send_one_week_until_inactivation_warning" -cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:deactivate_inactive_users" +$PROD_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" +$PROD_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" +$PROD_TASK "rake scheduled_jobs:deactivate_inactive_users" # Forms -# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:send_daily_notifications" -# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:send_weekly_notifications" -# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms" -# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:archive_forms" -cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash" +# $PROD_TASK "rake scheduled_jobs:send_daily_notifications" +# $PROD_TASK "rake scheduled_jobs:send_weekly_notifications" +# $PROD_TASK "rake scheduled_jobs:check_expiring_forms" +# $PROD_TASK "rake scheduled_jobs:archive_forms" +$PROD_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" +# $PROD_TASK "rake scheduled_jobs:delete_submissions_trash" echo "Production tasks have completed." From 3eaffd6b459f97b60eb44e30abbab0ef334b7045 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Mon, 10 Nov 2025 12:46:37 -0500 Subject: [PATCH 2/4] Revert set -e --- .circleci/cron.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/cron.sh b/.circleci/cron.sh index 148257870..31d9f664c 100755 --- a/.circleci/cron.sh +++ b/.circleci/cron.sh @@ -2,7 +2,7 @@ # Fail if anything within this script returns # a non-zero exit code -# set -e +set -e echo "Logging into cloud.gov" cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE From 84f1a8825a68361538dbf888b0898f1e233b62d6 Mon Sep 17 00:00:00 2001 From: Peter Burkholder Date: Mon, 10 Nov 2025 14:37:54 -0500 Subject: [PATCH 3/4] Catch failures and count 'em I non-prod I noticed that some of the tasks with --wait are failing. We don't want failing tasks to kill the entire job. We also don't want to bury failures. So this will keep track of the failues and mark the tasks as failing in CircleCI -- while still running as many as possible. --- .circleci/cron.sh | 146 ++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 71 deletions(-) diff --git a/.circleci/cron.sh b/.circleci/cron.sh index 31d9f664c..d33ab8af7 100755 --- a/.circleci/cron.sh +++ b/.circleci/cron.sh @@ -4,79 +4,83 @@ # a non-zero exit code set -e -echo "Logging into cloud.gov" +# Set # of failures to 0 +F=0 + +function run_production_tasks() { + # === PRODUCTION environment =================================================== + echo "Running tasks in Production..." + + PROD_TASK="cf run-task touchpoints-production-sidekiq-worker --wait -c" + # Users + $PROD_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" || F=$((F+=1)) + $PROD_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" || F=$((F+=1)) + $PROD_TASK "rake scheduled_jobs:deactivate_inactive_users" || F=$((F+=1)) + + # Forms + # $PROD_TASK "rake scheduled_jobs:send_daily_notifications" || F=$((F+=1)) + # $PROD_TASK "rake scheduled_jobs:send_weekly_notifications" || F=$((F+=1)) + # $PROD_TASK "rake scheduled_jobs:check_expiring_forms" || F=$((F+=1)) + # $PROD_TASK "rake scheduled_jobs:archive_forms" || F=$((F+=1)) + $PROD_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" || F=$((F+=1)) + # $PROD_TASK "rake scheduled_jobs:delete_submissions_trash" || F=$((F+=1)) + echo "Production tasks have completed." +} + + +function run_staging_tasks() { + # === STAGING environment ====================================================== + echo "Running tasks in Staging..." + + STAGING_TASK="cf run-task touchpoints-staging-sidekiq-worker --wait -c" + + # Users + $STAGING_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:deactivate_inactive_users" || F=$((F+=1)) + + # Forms + $STAGING_TASK "rake scheduled_jobs:send_daily_notifications" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:send_weekly_notifications" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:check_expiring_forms" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:archive_forms" || F=$((F+=1)) + $STAGING_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" || F=$((F+=1)) + # $STAGING_TASK "rake scheduled_jobs:delete_submissions_trash" || F=$((F+=1)) + + echo "Staging tasks have completed." +} + +function run_demo_tasks() { + # === DEMO environment ========================================================= + echo "Running tasks in Demo..." + DEMO_TASK="cf run-task touchpoints-demo-sidekiq-worker --wait -c" + + # Users + $DEMO_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:deactivate_inactive_users" || F=$((F+=1)) + + # Forms + $DEMO_TASK "rake scheduled_jobs:send_daily_notifications" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:send_weekly_notifications" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:check_expiring_forms" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:archive_forms" || F=$((F+=1)) + $DEMO_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" || F=$((F+=1)) + # $DEMO_TASK "rake scheduled_jobs:delete_submissions_trash" || F=$((F+=1)) + + echo "Demo tasks have completed." +} + +echo "Logging into cloud.gov non-prod" cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE - -# -# === STAGING environment ====================================================== -# - -echo "Running tasks in Staging..." - -STAGING_TASK="cf run-task touchpoints-staging-sidekiq-worker --wait -c" - -# Users -$STAGING_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" -$STAGING_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -$STAGING_TASK "rake scheduled_jobs:deactivate_inactive_users" - -# Forms -$STAGING_TASK "rake scheduled_jobs:send_daily_notifications" -$STAGING_TASK "rake scheduled_jobs:send_weekly_notifications" -$STAGING_TASK "rake scheduled_jobs:check_expiring_forms" -$STAGING_TASK "rake scheduled_jobs:archive_forms" -$STAGING_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# $STAGING_TASK "rake scheduled_jobs:delete_submissions_trash" - -echo "Staging tasks have completed." - -# -# === DEMO environment ========================================================= -# - -echo "Running tasks in Demo..." -DEMO_TASK="cf run-task touchpoints-demo-sidekiq-worker --wait -c" - -# Users -$DEMO_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" -$DEMO_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -$DEMO_TASK "rake scheduled_jobs:deactivate_inactive_users" - -# Forms -$DEMO_TASK "rake scheduled_jobs:send_daily_notifications" -$DEMO_TASK "rake scheduled_jobs:send_weekly_notifications" -$DEMO_TASK "rake scheduled_jobs:check_expiring_forms" -$DEMO_TASK "rake scheduled_jobs:archive_forms" -$DEMO_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# $DEMO_TASK "rake scheduled_jobs:delete_submissions_trash" - -echo "Demo tasks have completed." - +run_staging_tasks +run_demo_tasks cf logout -# -# === PRODUCTION environment =================================================== -# - -echo "Logging into cloud.gov" +echo "Logging into cloud.gov production environment" cf login -a $CF_API_ENDPOINT -u $CF_PRODUCTION_SPACE_DEPLOYER_USERNAME -p $CF_PRODUCTION_SPACE_DEPLOYER_PASSWORD -o $CF_ORG -s prod - -echo "Running tasks in Production..." - -PROD_TASK="cf run-task touchpoints-production-sidekiq-worker --wait -c" -# Users -$PROD_TASK "rake scheduled_jobs:send_one_week_until_inactivation_warning" -$PROD_TASK "rake scheduled_jobs:send_two_weeks_until_inactivation_warning" -$PROD_TASK "rake scheduled_jobs:deactivate_inactive_users" - -# Forms -# $PROD_TASK "rake scheduled_jobs:send_daily_notifications" -# $PROD_TASK "rake scheduled_jobs:send_weekly_notifications" -# $PROD_TASK "rake scheduled_jobs:check_expiring_forms" -# $PROD_TASK "rake scheduled_jobs:archive_forms" -$PROD_TASK "rake scheduled_jobs:notify_form_managers_of_inactive_forms" -# $PROD_TASK "rake scheduled_jobs:delete_submissions_trash" - -echo "Production tasks have completed." - +run_production_tasks cf logout + +echo "$0 exiting with failure count: $F" +exit $F \ No newline at end of file From eea739d095287e5badcbd18224005e5efff729ec Mon Sep 17 00:00:00 2001 From: Shelley Nason Date: Mon, 9 Mar 2026 14:33:49 -0400 Subject: [PATCH 4/4] Don't validate submissions during deletion. (#1995) * Don't validate submissions during deletion. --- .../admin/submissions_controller.rb | 27 +++++----- app/models/submission.rb | 4 +- .../admin/submissions_controller_spec.rb | 50 ++++++++++++------- spec/features/admin/submissions_spec.rb | 4 +- 4 files changed, 48 insertions(+), 37 deletions(-) diff --git a/app/controllers/admin/submissions_controller.rb b/app/controllers/admin/submissions_controller.rb index efa5c11da..77de70000 100644 --- a/app/controllers/admin/submissions_controller.rb +++ b/app/controllers/admin/submissions_controller.rb @@ -186,23 +186,20 @@ def unmark def delete ensure_form_manager(form: @form) - if @submission.update(deleted: true, deleted_at: Time.now) - Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id) - else - Rails.logger.warn("Failed to delete submission: #{@submission.errors.full_messages.join(', ')}") - end + Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id) + @submission.assign_attributes(deleted: true, deleted_at: Time.now) + @submission.save(validate: false) end def destroy ensure_form_manager(form: @form) - if @submission.update(deleted: true, deleted_at: Time.now) - Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id) - respond_to do |format| - format.js { render :destroy } - end - else - Rails.logger.warn("Failed to delete submission: #{@submission.errors.full_messages.join(', ')}") + Event.log_event(Event.names[:response_deleted], 'Submission', @submission.id, "Submission #{@submission.id} deleted at #{DateTime.now}", current_user.id) + @submission.assign_attributes(deleted: true, deleted_at: Time.now) + @submission.save(validate: false) + + respond_to do |format| + format.js { render :destroy } end end @@ -210,7 +207,8 @@ def undelete ensure_form_manager(form: @form) Event.log_event(Event.names[:response_undeleted], 'Submission', @submission.id, "Submission #{@submission.id} undeleted at #{DateTime.now}", current_user.id) - @submission.update(deleted: false, deleted_at: nil) + @submission.assign_attributes(deleted: false, deleted_at: nil) + @submission.save(validate: false) end def feed @@ -302,7 +300,8 @@ def bulk_update when 'delete' submissions.each do |submission| Event.log_event(Event.names[:response_deleted], 'Submission', submission.id, "Submission #{submission.id} deleted at #{DateTime.now}", current_user.id) - submission.update(deleted: true, deleted_at: Time.now) + submission.assign_attributes(deleted: true, deleted_at: Time.now) + submission.save(validate: false) end flash[:notice] = "#{view_context.pluralize(submissions.count, 'Submission')} deleted." else diff --git a/app/models/submission.rb b/app/models/submission.rb index 22b441cb1..05fee0aed 100644 --- a/app/models/submission.rb +++ b/app/models/submission.rb @@ -6,7 +6,7 @@ class Submission < ApplicationRecord belongs_to :form, counter_cache: :response_count attr_accessor :fba_directive # for SPAM capture - validate :validate_custom_form + validate :validate_answers, on: :create validates :uuid, uniqueness: true before_create :set_uuid @@ -47,7 +47,7 @@ class Submission < ApplicationRecord end # Validate each submitted field against its question type - def validate_custom_form + def validate_answers # Isolate questions that were answered answered_questions = attributes.select { |_key, value| value.present? } diff --git a/spec/controllers/admin/submissions_controller_spec.rb b/spec/controllers/admin/submissions_controller_spec.rb index 2cbac1e57..cb529eec6 100644 --- a/spec/controllers/admin/submissions_controller_spec.rb +++ b/spec/controllers/admin/submissions_controller_spec.rb @@ -9,28 +9,27 @@ let!(:user_role) { FactoryBot.create(:user_role, user: admin, form:, role: UserRole::Role::FormManager) } let!(:questions) { FactoryBot.create(:question, - form:, - question_type: 'text_field', - form_section: form.form_sections.first, - answer_field: 'answer_02', - position: 2, - text: 'Two' + form:, + question_type: 'text_field', + form_section: form.form_sections.first, + answer_field: 'answer_02', + position: 2, + text: 'Two', ) FactoryBot.create(:question, - form:, - question_type: 'text_field', - form_section: form.form_sections.first, - answer_field: 'answer_03', - position: 3, - text: 'Three' + form:, + question_type: 'text_field', + form_section: form.form_sections.first, + answer_field: 'answer_03', + position: 3, + text: 'Three', ) FactoryBot.create(:question, - form:, - question_type: 'text_field', - form_section: form.form_sections.first, - answer_field: 'answer_04', - position: 4, - text: 'Four' + :email, + form:, + form_section: form.form_sections.first, + answer_field: 'answer_04', + position: 4, ) } @@ -47,10 +46,11 @@ let(:invalid_attributes) do { + form_id: form.id, answer_01: nil, answer_02: 'James', answer_03: 'Madison', - answer_04: nil, + answer_04: 'not_an_email', } end @@ -99,6 +99,18 @@ end.to change { Submission.active.count }.by(-1) end + it 'deletes the requested submission even if it has invalid answers' do + expect { Submission.create! invalid_attributes }.to raise_error(ActiveRecord::RecordInvalid) + + submission = Submission.create! valid_attributes + submission.assign_attributes(invalid_attributes) + submission.save(validate: false) + + expect do + delete :delete, params: { id: submission.to_param, form_id: form.short_uuid }, session: valid_session, format: :js + end.to change { Submission.active.count }.by(-1) + end + it 'redirects to the submissions list' do submission = Submission.create! valid_attributes delete :destroy, params: { id: submission.to_param, form_id: form.short_uuid }, session: valid_session, format: :js diff --git a/spec/features/admin/submissions_spec.rb b/spec/features/admin/submissions_spec.rb index 213e38916..844c7e9f9 100644 --- a/spec/features/admin/submissions_spec.rb +++ b/spec/features/admin/submissions_spec.rb @@ -103,9 +103,9 @@ visit admin_form_submission_path(form, submission) end - it 'try to update a submission that has had its question validations changed' do + it 'can update status of a submission that has had its question validations changed' do click_on("Acknowledge") - expect(page).to have_content("Response could not be updated.") + expect(page).to have_content("Response was successfully updated.") end end end