Skip to content
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

Fix for grant assign email with missing description #2725

Merged
merged 8 commits into from
Mar 12, 2024

Conversation

jeffsmohan
Copy link
Contributor

Ticket #2716

Description

When sending the grant assignment email, issues sending the email can bubble up and cause the node server to crash. Instead, we want to gracefully catch these errors and return a 500 response. Also fixes the proximate cause of the error here, which was a missing description for the grant. Specifically, this PR:

  1. Fixes the grant detail rendering so it renders the grant even when the description is missing
  2. Email utils for the grant assign email now catch errors building/sending the email and logs the error
  3. API endpoint for sending the grant assign email now catches errors and gracefully returns a 500

Screenshots / Demo Video

Testing

Automated and Unit Tests

  • Added Unit tests

Manual tests for Reviewer

  • Added steps to test feature/functionality manually

Checklist

  • Provided ticket and description
  • Provided screenshots/demo
  • Provided testing information
  • Provided adequate test coverage for all new code
  • Added PR reviewers

@jeffsmohan jeffsmohan requested a review from as1729 March 6, 2024 19:54
@github-actions github-actions bot added bug Something isn't working javascript Pull requests that update Javascript code labels Mar 6, 2024
Copy link

github-actions bot commented Mar 6, 2024

QA Summary

QA Check Result
🌐 Client Tests
🔗 Server Tests
🤝 E2E Tests
📏 ESLint
🧹 TFLint

Test Coverage

Coverage report for `packages/client`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🔴 All files 16.22 14.28 20.97 16.48
🟡  ...nents/src/arpa_reporter/components 50.9 26.66 62.16 50.9
🔴   AlertBox.vue 0 0 0 0 20-21
🟡   DownloadButton.vue 50 50 50 50 39,56-59
🟢   DownloadFileButton.vue 100 100 100 100
🟢   DownloadTemplateBtn.vue 100 100 100 100
🟡   Navigation.vue 63.15 100 62.5 63.15 137-143,152-159
🔴   StandardForm.vue 34.78 20 55.55 34.78 86-90,97-119
🟢  arpa_reporter/helpers 84.61 79.48 87.5 84.61
🟢   form-helpers.js 84.21 79.48 85.71 84.21 7,16,25,81-83
🟢   short-uuid.js 100 100 100 100
🔴  arpa_reporter/store 4.85 0 2.17 5.1
🔴   index.js 4.85 0 2.17 5.1 13-16,34-263
🔴  ...rter/views/src/arpa_reporter/views 41.71 39.21 70.58 41.71
🟢   Agencies.vue 100 100 100 100
🔴   Home.vue 26.66 25 60 26.66 ...05-130,137-166
🔴   Upload.vue 48.21 50 78.94 48.21 ...39,345-346,356
🔴   Uploads.vue 43.18 38.88 66.66 43.18 ...65,181,232-256
🔴   User.vue 38.7 28.57 68.75 38.7 73-118
🔴  ...nents/Modals/src/components/Modals 4 2.17 10.52 4.01
🔴   AddOrganization.vue 7.69 0 25 7.69 136-171
🔴   AddTeam.vue 37.5 33.33 60 37.5 178,184-212
🔴   EditOrganization.vue 11.11 0 28.57 11.11 50-76
🔴   EditTeam.vue 2.5 0 16.66 2.5 151-260
🔴   GrantDetailsLegacy.vue 0 0 0 0 103-268
🔴   ImportTeams.vue 0 0 0 0 44-79
🔴   ProfileSettings.vue 0 0 0 0 49-128
🔴   SavedSearchPanel.vue 0 0 0 0 98-208
🔴   SearchPanel.vue 1.75 0 0 1.78 266-452
🔴  components/src/components 4.78 3.66 9.72 4.78
🔴   ActivityTable.vue 0 0 0 0 68-137
🔴   ClosingDatesTable.vue 0 0 0 0 52-84
🔴   GrantsTable.vue 3 0 0 3 136-512
🟡   Layout.vue 50 66.66 53.84 50 133-148
🔴   SearchFilter.vue 0 0 0 0 26-68
🔴   Uploader.vue 0 0 0 0 47-100
🔴   UserAvatar.vue 0 0 0 0 25-36
🔴  helpers 11.53 12.64 12.12 11.88
🟢   constants.js 100 100 100 100
🟢   dates.js 100 100 100 100
🔴   fetchApi.js 5.55 12.5 5.55 5.55 10-102
🔴   filters.js 4 0 0 4.54 19-51
🔴   form-helpers.js 0 0 0 0 5-82
🟢   gtag.js 100 100 100 100
🟢  helpers/featureFlags 100 100 100 100
🟢   index.js 100 100 100 100
🟢   utils.js 100 100 100 100
🔴  mixin 23.8 0 28.57 23.8
🔴   resizableTable.js 23.8 0 28.57 23.8 16-31,36-37,42
🔴  router 20 11.53 17.64 20
🔴   index.js 20 11.53 17.64 20 ...74-175,179-198
🟢  store 100 100 100 100
🟢   index.js 100 100 100 100
🔴  store/modules 6.29 0 4.97 6.59
🔴   agencies.js 10 100 8.33 10.52 13-70
🔴   alerts.js 20 100 20 20 10-24
🔴   dashboard.js 4.65 0 4.54 4.65 22-96
🔴   grants.js 2.79 0 1.05 2.94 59-353
🔴   keywords.js 14.28 0 9.09 16.66 13-33
🔴   organization.js 33.33 100 33.33 33.33 21-25
🔴   roles.js 33.33 100 20 40 13-22
🔴   tenants.js 20 100 14.28 22.22 13-32
🔴   users.js 4.76 0 4.76 4.87 17-100
🟡  views 50 100 0 50
🟡   NotFound.vue 50 100 0 50 2-4
🔴  views/src/views 14.89 0 26 14.89
🔴   ArpaAnnualPerformanceReporter.vue 0 0 0 0 81-134
🔴   Dashboard.vue 26.31 0 36.36 26.31 ...12-122,137-148
🔴   Login.vue 0 0 0 0 63-115
🔴   Organizations.vue 40 100 40 40 64,74-77,88-92
🟡   Teams.vue 50 100 50 50 108,119-126
Coverage report for `packages/server`
St File % Stmts % Branch % Funcs % Lines Uncovered Line #s
🟡 All files 55.62 46.97 50.2 55.99
🟢  src 82.35 33.33 60 82.35
🟢   configure.js 82.35 33.33 60 82.35 44,63-70,99-101
🟢  src/arpa_reporter 98.75 66.66 100 98.75
🟢   configure.js 97.36 40 100 97.36 36
🟢   environment.js 100 100 100 100
🟢   use-request.js 100 100 100 100
🔴  src/arpa_reporter/db 38.58 32.92 44.44 40.16
🔴   arpa-subrecipients.js 13.15 4.34 15.38 14.28 23-92
🔴   reporting-periods.js 37.2 46.87 40 38.09 46,77-156
🟢   settings.js 100 83.33 100 100 13
🟡   uploads.js 50 28.57 52.38 51.42 18-29,83,98-123,140-149
🔴  src/arpa_reporter/lib 29.09 23.85 29.68 29.41
🟢   arpa-ec-codes.js 100 100 100 100
🔴   audit-report.js 27.24 25.35 32.6 26.66 ...96-397,439-533,581-607
🟡   ensure-async-context.js 75 100 50 100
🔴   format.js 13.79 0 0 19.04 28-82
🟡   log.js 75 50 50 75 13,25
🟡   preconditions.js 66.66 33.33 100 66.66 3
🔴   spreadsheet.js 9.09 0 0 9.09 15-32
🟢   validation-error.js 85.71 100 50 85.71 16
🔴  src/arpa_reporter/routes 40.08 14.92 14.28 40.69
🔴   agencies.js 22.58 0 0 23.33 13-21,26-53
🟡   application_settings.js 75 100 0 75 10-11
🟡   audit-report.js 69.86 58.33 100 69.86 57-58,64-78,100-115
🟢   exports.js 81.42 83.33 100 81.42 61-75,98-99
🔴   reporting-periods.js 20 0 0 20.43 ...25-137,143-149,154-180
🔴   subrecipients.js 23.8 0 0 23.8 12-13,17-27,31-48,52-63
🔴   uploads.js 28.28 7.89 9.09 29.16 ...33-154,164-166,173-180
🔴   users.js 19.6 0 0 20 15-35,39-44,48-81
🔴  src/arpa_reporter/services 42.63 30.33 45.39 43.01
🔴   generate-arpa-report.js 36.86 2.79 50 37.24 ...-944,953-966,1040-1107
🔴   get-template.js 21.62 0 0 21.62 18-79
🟡   persist-upload.js 68.6 90 69.56 68.67 ...58-200,221-235,273-295
🔴   records.js 21.15 0 11.11 21.56 38-202,219-272
🔴   revalidate-uploads.js 37.5 100 0 37.5 5-14
🔴   validate-upload.js 37.93 49.41 32.14 38.83 ...10,329,351,369-657,672
🟢   validation-rules.js 98.18 90 90.9 100 157,173
🟡  src/db 75.41 72.27 69.94 75.51
🟢   connection.js 100 50 100 100 6
🟢   constants.js 100 100 100 100
🟢   helpers.js 83.33 100 50 83.33 21-22
🟢   index.js 83.18 78.61 82.87 83.17 ...05-1571,1753-1754,1761
🟢   saved_search_migration.js 92 88.23 71.42 93.61 5,69,134
🔴   tenant_creation.js 10.58 2.7 0 11.11 15-40,48-210,220
🔴  src/db/arpa_reporter_db_shims 23.68 0 0 23.68
🔴   agencies.js 22.22 100 0 22.22 11-51
🔴   users.js 25 0 0 25 12-62
🟡  src/lib 65.92 61.75 61.29 66.84
🟢   access-helpers.js 93.44 89.18 100 93.44 91-92,97-98
🟢   agencyImporter.js 90.38 88.46 100 90.19 26,29,35,93-94
🔴   batchProcessor.js 2.94 0 0 3.03 35-104
🟢   email.js 91.6 75 100 91.42 ...88,130,271-274,311-312
🔴   gost-aws.js 21.05 11.11 20 19.44 13-58,65-116
🟢   grants-ingest.js 83.33 97.5 90 83.33 ...28-131,138-140,155-159
🔴   grantsgov.js 6.25 6.52 0 6.97 12-220
🟡   logging.js 77.77 85.71 100 77.77 11,13
🟢   redirect_validation.js 100 100 100 100
🟢   userImporter.js 82.27 58.33 88.88 81.57 32,47,53,62,73-81,143-152
🔴  src/lib/annualReports 27.38 0 0 27.38
🔴   doc-builder.js 7.69 0 0 7.69 19-352
🟡   index.js 80 100 0 80 6
🟢   placeholderTextStrings.js 100 100 100 100
🔴   reportBuilder.js 17.24 0 0 17.24 21-33,50-62,86-103
🟢  src/lib/arpa_reporter_shims 100 100 100 100
🟢   email.js 100 100 100 100
🟢  src/lib/email 92.85 87.5 100 92.3
🟢   constants.js 100 100 100 100
🟢   email-nodemailer.js 88.23 83.33 100 86.66 33,56
🟢   service-email.js 100 100 100 100
🟢  src/lib/fieldConfigs 100 100 100 100
🟢   fundingActivityCategories.js 100 100 100 100
🔴  src/lib/grantscraper 11.86 0 0 12.96
🔴   index.js 11.86 0 0 12.96 11-92,98-122
🟡  src/routes 68.36 57.52 60.49 68.43
🔴   agencies.js 45.54 30 40 45.54 ...32-140,144-165,173-179
🔴   annualReports.js 47.05 100 0 47.05 15-27
🔴   dashboard.js 21.42 0 0 21.42 8-53
🟢   eligibilityCodes.js 100 100 100 100
🟢   grants.js 80.43 71.02 73.52 81.25 ...80-381,396-399,412-413
🟡   grantsSavedSearch.js 80 83.33 100 80 38-39,56-57,70-72,78
🟢   health.js 100 100 100 100
🟡   interestedCodes.js 71.42 100 0 71.42 7-8
🟢   keywords.js 86.95 50 100 86.95 26-27,34
🟡   refresh.js 71.42 100 0 71.42 7-8
🟡   roles.js 75 100 0 75 8-9
🟡   searchConfig.js 58.33 100 0 58.33 9-14
🟡   sessions.js 63.93 39.39 50 63.93 ...-73,79-80,84-85,98-104
🔴   tenants.js 39.13 0 0 39.13 11-12,16-27,32-35
🟢   users.js 80.41 74.19 85.71 80.41 ...17,134-136,160,169-177

Pusher: @jeffsmohan, Action: pull_request_target, Workflow: Continuous Integration

Copy link

github-actions bot commented Mar 6, 2024

Terraform Summary

Step Result
🖌 Terraform Format & Style
⚙️ Terraform Initialization
🤖 Terraform Validation
📖 Terraform Plan

Hint: If "Terraform Format & Style" failed, run terraform fmt -recursive from the terraform/ directory and commit the results.

Output

Validation Output
Success! The configuration is valid.


Plan Output
Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the
last "terraform apply" which may have affected this plan:

  # module.api.aws_ecs_task_definition.default[0] has changed
  ~ resource "aws_ecs_task_definition" "default" {
        id                       = "gost-staging-api"
+       tags                     = {}
        # (13 unchanged attributes hidden)

+       volume {
+           name = "data"

+           efs_volume_configuration {
+               file_system_id          = "fs-08f95063c1cdbe191"
+               root_directory          = "/"
+               transit_encryption      = "ENABLED"
+               transit_encryption_port = 0

+               authorization_config {
+                   access_point_id = "fsap-03bc0296928aade4f"
                }
            }
        }
-       volume {
-           name = "data" -> null

-           efs_volume_configuration {
-               file_system_id     = "fs-08f95063c1cdbe191" -> null
-               root_directory     = "/" -> null
-               transit_encryption = "ENABLED" -> null

-               authorization_config {
-                   access_point_id = "fsap-03bc0296928aade4f" -> null
                }
            }
        }

        # (1 unchanged block hidden)
    }

  # module.api.aws_iam_role.execution[0] has changed
  ~ resource "aws_iam_role" "execution" {
        id                    = "gost-staging-api-ECSTaskExecution-20230217010414321500000009"
        name                  = "gost-staging-api-ECSTaskExecution-20230217010414321500000009"
      ~ role_last_used        = [
          ~ {
              ~ last_used_date = "2024-03-11T20:13:51Z" -> "2024-03-12T16:32:30Z"
                # (1 unchanged element hidden)
            },
        ]
        tags                  = {}
        # (11 unchanged attributes hidden)

        # (2 unchanged blocks hidden)
    }

  # module.api.aws_iam_role.task[0] has changed
  ~ resource "aws_iam_role" "task" {
        id                    = "gost-staging-api-ECSTask-2023021701041477300000000a"
        name                  = "gost-staging-api-ECSTask-2023021701041477300000000a"
      ~ role_last_used        = [
          ~ {
              ~ last_used_date = "2024-03-11T20:13:50Z" -> "2024-03-12T15:04:51Z"
                # (1 unchanged element hidden)
            },
        ]
        tags                  = {}
        # (11 unchanged attributes hidden)

        # (6 unchanged blocks hidden)
    }


Unless you have made equivalent changes to your configuration, or ignored the
relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.

─────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+   create
  ~ update in-place
-   destroy
+/- create replacement and then destroy

Terraform will perform the following actions:

  # module.api.aws_ecs_service.default[0] will be updated in-place
  ~ resource "aws_ecs_service" "default" {
        id                                 = "arn:aws:ecs:us-west-2:357150818708:service/gost-staging/gost-staging-api"
        name                               = "gost-staging-api"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-api:178" -> (known after apply)
        # (15 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.api.aws_ecs_task_definition.default[0] must be replaced
+/- resource "aws_ecs_task_definition" "default" {
      ~ arn                      = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-api:178" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-api" -> (known after apply)
      ~ container_definitions    = jsonencode(
          ~ [ # forces replacement
              ~ {
                  ~ dockerLabels           = {
                      ~ "com.datadoghq.tags.version" = "be99d94e2e0dc1f2757497adf3602972f70d86f9" -> "f8d887ec0e6054c8b8039e9b32e926c07b8ea550"
                        # (2 unchanged elements hidden)
                    }
                  ~ environment            = [
                        # (7 unchanged elements hidden)
                        {
                            name  = "DD_SERVICE"
                            value = "gost"
                        },
                      ~ {
                            name  = "DD_VERSION"
                          ~ value = "be99d94e2e0dc1f2757497adf3602972f70d86f9" -> "f8d887ec0e6054c8b8039e9b32e926c07b8ea550"
                        },
                        {
                            name  = "ENABLE_GRANTS_DIGEST"
                            value = "false"
                        },
                        # (12 unchanged elements hidden)
                    ]
                  ~ image                  = "ghcr.io/usdigitalresponse/usdr-gost-api:be99d94e2e0dc1f2757497adf3602972f70d86f9@sha256:f38f1240c41d0b7c7f9b1615af0eda627c611c98af0a623f357267488e08a2d6" -> "ghcr.io/usdigitalresponse/usdr-gost-api:f8d887ec0e6054c8b8039e9b32e926c07b8ea550@sha256:68d983846ef8ffcb70bf8ce571a8c0df6da6c74f91eaa6a085b6960f8c89517f"
                    name                   = "api"
-                   systemControls         = [] -> null
-                   volumesFrom            = [] -> null
                    # (9 unchanged elements hidden)
                } # forces replacement,
              ~ {
                  ~ dockerLabels           = {
                      ~ "com.datadoghq.tags.version" = "be99d94e2e0dc1f2757497adf3602972f70d86f9" -> "f8d887ec0e6054c8b8039e9b32e926c07b8ea550"
                        # (2 unchanged elements hidden)
                    }
                  ~ environment            = [
                        # (3 unchanged elements hidden)
                        {
                            name  = "DD_SERVICE"
                            value = "gost"
                        },
                      ~ {
                            name  = "DD_VERSION"
                          ~ value = "be99d94e2e0dc1f2757497adf3602972f70d86f9" -> "f8d887ec0e6054c8b8039e9b32e926c07b8ea550"
                        },
                        {
                            name  = "ECS_FARGATE"
                            value = "true"
                        },
                    ]
-                   mountPoints            = [] -> null
                    name                   = "datadog"
-                   portMappings           = [] -> null
-                   systemControls         = [] -> null
-                   volumesFrom            = [] -> null
                    # (6 unchanged elements hidden)
                } # forces replacement,
            ]
        )
      ~ id                       = "gost-staging-api" -> (known after apply)
      ~ revision                 = 178 -> (known after apply)
-       tags                     = {} -> null
        # (9 unchanged attributes hidden)

-       volume {
-           name = "data" -> null

-           efs_volume_configuration {
-               file_system_id          = "fs-08f95063c1cdbe191" -> null
-               root_directory          = "/" -> null
-               transit_encryption      = "ENABLED" -> null
-               transit_encryption_port = 0 -> null

-               authorization_config {
-                   access_point_id = "fsap-03bc0296928aade4f" -> null
                }
            }
        }
+       volume {
+           name = "data"

+           efs_volume_configuration {
+               file_system_id     = "fs-08f95063c1cdbe191"
+               root_directory     = "/"
+               transit_encryption = "ENABLED"

+               authorization_config {
+                   access_point_id = "fsap-03bc0296928aade4f"
                }
            }
        }

        # (1 unchanged block hidden)
    }

  # module.arpa_audit_report.aws_ecs_service.default will be updated in-place
  ~ resource "aws_ecs_service" "default" {
        id                                 = "arn:aws:ecs:us-west-2:357150818708:service/gost-staging/gost-staging-arpa_audit_report"
        name                               = "gost-staging-arpa_audit_report"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-arpa_audit_report:147" -> (known after apply)
        # (15 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

  # module.arpa_audit_report.aws_ecs_task_definition.consumer must be replaced
+/- resource "aws_ecs_task_definition" "consumer" {
      ~ arn                      = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-arpa_audit_report:147" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-arpa_audit_report" -> (known after apply)
      ~ container_definitions    = (sensitive value) # forces replacement
      ~ id                       = "gost-staging-arpa_audit_report" -> (known after apply)
-       ipc_mode                 = "" -> null
-       pid_mode                 = "" -> null
      ~ revision                 = 147 -> (known after apply)
-       tags                     = {} -> null
        # (9 unchanged attributes hidden)

-       volume {
-           name = "data" -> null

-           efs_volume_configuration {
-               file_system_id          = "fs-08f95063c1cdbe191" -> null
-               root_directory          = "/" -> null
-               transit_encryption      = "ENABLED" -> null
-               transit_encryption_port = 0 -> null

-               authorization_config {
-                   access_point_id = "fsap-03bc0296928aade4f" -> null
                }
            }
        }
+       volume {
+           name = "data"

+           efs_volume_configuration {
+               file_system_id     = "fs-08f95063c1cdbe191"
+               root_directory     = "/"
+               transit_encryption = "ENABLED"

+               authorization_config {
+                   access_point_id = "fsap-03bc0296928aade4f"
                }
            }
        }

        # (1 unchanged block hidden)
    }

  # module.arpa_treasury_report.aws_ecs_service.default will be updated in-place
  ~ resource "aws_ecs_service" "default" {
        id                                 = "arn:aws:ecs:us-west-2:357150818708:service/gost-staging/gost-staging-treasury_report"
        name                               = "gost-staging-treasury_report"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-treasury_report:136" -> (known after apply)
        # (15 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

  # module.arpa_treasury_report.aws_ecs_task_definition.consumer must be replaced
+/- resource "aws_ecs_task_definition" "consumer" {
      ~ arn                      = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-treasury_report:136" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-treasury_report" -> (known after apply)
      ~ container_definitions    = (sensitive value) # forces replacement
      ~ id                       = "gost-staging-treasury_report" -> (known after apply)
-       ipc_mode                 = "" -> null
-       pid_mode                 = "" -> null
      ~ revision                 = 136 -> (known after apply)
-       tags                     = {} -> null
        # (9 unchanged attributes hidden)

-       volume {
-           name = "data" -> null

-           efs_volume_configuration {
-               file_system_id          = "fs-08f95063c1cdbe191" -> null
-               root_directory          = "/" -> null
-               transit_encryption      = "ENABLED" -> null
-               transit_encryption_port = 0 -> null

-               authorization_config {
-                   access_point_id = "fsap-03bc0296928aade4f" -> null
                }
            }
        }
+       volume {
+           name = "data"

+           efs_volume_configuration {
+               file_system_id     = "fs-08f95063c1cdbe191"
+               root_directory     = "/"
+               transit_encryption = "ENABLED"

+               authorization_config {
+                   access_point_id = "fsap-03bc0296928aade4f"
                }
            }
        }

        # (1 unchanged block hidden)
    }

  # module.consume_grants.aws_ecs_service.default will be updated in-place
  ~ resource "aws_ecs_service" "default" {
        id                                 = "arn:aws:ecs:us-west-2:357150818708:service/gost-staging/gost-staging-consume_grants"
        name                               = "gost-staging-consume_grants"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-consume_grants:163" -> (known after apply)
        # (15 unchanged attributes hidden)

        # (3 unchanged blocks hidden)
    }

  # module.consume_grants.aws_ecs_task_definition.consume_grants must be replaced
+/- resource "aws_ecs_task_definition" "consume_grants" {
      ~ arn                      = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-consume_grants:163" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-west-2:357150818708:task-definition/gost-staging-consume_grants" -> (known after apply)
      ~ container_definitions    = (sensitive value) # forces replacement
      ~ id                       = "gost-staging-consume_grants" -> (known after apply)
-       ipc_mode                 = "" -> null
-       pid_mode                 = "" -> null
      ~ revision                 = 163 -> (known after apply)
-       tags                     = {} -> null
        # (9 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.website.aws_s3_object.deploy-config[0] will be updated in-place
  ~ resource "aws_s3_object" "deploy-config" {
      ~ content                = <<-EOT
            window.APP_CONFIG = window.APP_CONFIG || {};
            window.APP_CONFIG.apiURLForGOST = 'https://api.staging.grants.usdr.dev/';
            window.apiURLForGOST = window.APP_CONFIG.apiURLForGOST; // Legacy
            
            window.APP_CONFIG.DD_RUM_ENABLED = true;
-           window.APP_CONFIG.DD_RUM_CONFIG = {"allowedTracingUrls":["https://api.staging.grants.usdr.dev"],"applicationId":"15db471e-2ccb-4d3c-a6bf-99b750d748f5","clientToken":"pub50834fcc1999d53e546519b1a0f03934","defaultPrivacyLevel":"mask","env":"staging","service":"gost","sessionReplaySampleRate":1,"sessionSampleRate":10,"site":"datadoghq.com","trackLongTasks":true,"trackResources":true,"trackUserInteractions":true,"version":"be99d94e2e0dc1f2757497adf3602972f70d86f9"};
+           window.APP_CONFIG.DD_RUM_CONFIG = {"allowedTracingUrls":["https://api.staging.grants.usdr.dev"],"applicationId":"15db471e-2ccb-4d3c-a6bf-99b750d748f5","clientToken":"pub50834fcc1999d53e546519b1a0f03934","defaultPrivacyLevel":"mask","env":"staging","service":"gost","sessionReplaySampleRate":1,"sessionSampleRate":10,"site":"datadoghq.com","trackLongTasks":true,"trackResources":true,"trackUserInteractions":true,"version":"f8d887ec0e6054c8b8039e9b32e926c07b8ea550"};
            
            window.APP_CONFIG.GOOGLE_TAG_ID = 'G-D5DFR7BN0N';
            
            window.APP_CONFIG.featureFlags = {"myProfileEnabled":true,"newGrantsDetailPageEnabled":false,"newTerminologyEnabled":true};
            
            window.APP_CONFIG.overrideFeatureFlag = (flagName, overrideValue) => {
              const storageKey = 'featureFlags';
              let overrides = {};
              try {
                overrides = JSON.parse(window.sessionStorage.getItem(storageKey)) || {};
              } catch (e) {
                console.error(`Error parsing window.sessionStorage.${storageKey} as JSON:`, e);
                console.warn(`window.sessionStorage.${storageKey} will be replaced.`);
              }
              overrides[flagName] = overrideValue;
              window.sessionStorage.setItem(storageKey, JSON.stringify(overrides));
              console.log('New feature flag overrides in page session:',
                window.sessionStorage.getItem(storageKey));
            };
        EOT
      ~ etag                   = "89e1681445ff707cb9de35703745f828" -> "227d7d55b91fe7e972bcd294c72a082d"
        id                     = "/config/deploy-config.js"
        tags                   = {}
      ~ version_id             = "TIhO5Sjs4GUvP3grD.Z_BkJ9ir7e5Jqi" -> (known after apply)
        # (10 unchanged attributes hidden)
    }

  # module.website.aws_s3_object.origin_dist_artifact["arpa_reporter/index.html"] will be updated in-place
  ~ resource "aws_s3_object" "origin_dist_artifact" {
      ~ etag                   = "8026671159f0a16cd10b90ff79ea7fb4" -> "98bd6830e44f09dcf6da78b338782131"
        id                     = "dist/arpa_reporter/index.html"
      ~ source_hash            = "8026671159f0a16cd10b90ff79ea7fb4" -> "98bd6830e44f09dcf6da78b338782131"
        tags                   = {}
      ~ version_id             = "4760JWZnpI41Rw9DTlPRMvRt4hDzJn2Z" -> (known after apply)
        # (11 unchanged attributes hidden)
    }

  # module.website.aws_s3_object.origin_dist_artifact["index.html"] will be updated in-place
  ~ resource "aws_s3_object" "origin_dist_artifact" {
      ~ etag                   = "7383bb19c3d5d33ab4afee4c0e351c9a" -> "e893b701e4036ccdbfb5bf436de2efd6"
        id                     = "dist/index.html"
      ~ source_hash            = "7383bb19c3d5d33ab4afee4c0e351c9a" -> "e893b701e4036ccdbfb5bf436de2efd6"
        tags                   = {}
      ~ version_id             = "YRIRSxY0JmVCIzrOnKQ9Tpb5N0r6jCJu" -> (known after apply)
        # (11 unchanged attributes hidden)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/111.1e4fca07.js"] will be destroyed
  # (because key ["js/111.1e4fca07.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "57aeb29af0bd13e17d23cb236143ddf0" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/111.1e4fca07.js" -> null
-       key                    = "dist/js/111.1e4fca07.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/111.1e4fca07.js" -> null
-       source_hash            = "57aeb29af0bd13e17d23cb236143ddf0" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "0AHwwUJjjTRrwDSwUgFB824w0CIyprDJ" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/111.1e4fca07.js.map"] will be destroyed
  # (because key ["js/111.1e4fca07.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "6c01e1460c0f6d0bb323c0a328ee4369" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/111.1e4fca07.js.map" -> null
-       key                    = "dist/js/111.1e4fca07.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/111.1e4fca07.js.map" -> null
-       source_hash            = "6c01e1460c0f6d0bb323c0a328ee4369" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "QxPurXAkPSZ569DlNEU4JLDqo1pnC0ia" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/111.5ede5a13.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "39b06d6b97f6d84869dd044e04c3f776"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/111.5ede5a13.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/111.5ede5a13.js"
+       source_hash            = "39b06d6b97f6d84869dd044e04c3f776"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/111.5ede5a13.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "8ecacdc42626de1f5616f0d224cf16bb"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/111.5ede5a13.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/111.5ede5a13.js.map"
+       source_hash            = "8ecacdc42626de1f5616f0d224cf16bb"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/207.72726673.js"] will be destroyed
  # (because key ["js/207.72726673.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "5525973a304a7ff59cca7e7e1f864c92" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/207.72726673.js" -> null
-       key                    = "dist/js/207.72726673.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/207.72726673.js" -> null
-       source_hash            = "5525973a304a7ff59cca7e7e1f864c92" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "WQUWy99KBpnQDlqgM9UwpSN95XNDyP9P" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/207.72726673.js.map"] will be destroyed
  # (because key ["js/207.72726673.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "a7681de6413d1de980457c6e6a034137" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/207.72726673.js.map" -> null
-       key                    = "dist/js/207.72726673.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/207.72726673.js.map" -> null
-       source_hash            = "a7681de6413d1de980457c6e6a034137" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "w4XkXfLxhlQccUIt_1_0axSA8fP2XBL9" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/207.e518d42b.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "fce752f2b7545073ff52287d9d549582"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/207.e518d42b.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/207.e518d42b.js"
+       source_hash            = "fce752f2b7545073ff52287d9d549582"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/207.e518d42b.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "e83e84fc61f6c76379141b9c3ba67ce1"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/207.e518d42b.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/207.e518d42b.js.map"
+       source_hash            = "e83e84fc61f6c76379141b9c3ba67ce1"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/217.e5cbb2dc.js"] will be destroyed
  # (because key ["js/217.e5cbb2dc.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "652b94d836ac9039eda952c0e7721656" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/217.e5cbb2dc.js" -> null
-       key                    = "dist/js/217.e5cbb2dc.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/217.e5cbb2dc.js" -> null
-       source_hash            = "652b94d836ac9039eda952c0e7721656" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "5SqyDRyUEzes0yYNN33fg97CMAdobTfU" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/217.e5cbb2dc.js.map"] will be destroyed
  # (because key ["js/217.e5cbb2dc.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "9a8b194dd51e67c48c367922d360d58c" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/217.e5cbb2dc.js.map" -> null
-       key                    = "dist/js/217.e5cbb2dc.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/217.e5cbb2dc.js.map" -> null
-       source_hash            = "9a8b194dd51e67c48c367922d360d58c" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "PfdKVdDp6VspNDt993seenBra.evYyiZ" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/217.ff9b6b3e.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "6c3670849ed47ebc346b7233b95dfd26"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/217.ff9b6b3e.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/217.ff9b6b3e.js"
+       source_hash            = "6c3670849ed47ebc346b7233b95dfd26"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/217.ff9b6b3e.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "d1f1272daaf994c38eac3b5ae3def5cc"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/217.ff9b6b3e.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/217.ff9b6b3e.js.map"
+       source_hash            = "d1f1272daaf994c38eac3b5ae3def5cc"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/319.01d8c946.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "b4d50cb9e461607a34c8093ed990815b"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/319.01d8c946.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/319.01d8c946.js"
+       source_hash            = "b4d50cb9e461607a34c8093ed990815b"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/319.01d8c946.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "fc9cc5d4d465431cd3519cd2fb42d18d"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/319.01d8c946.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/319.01d8c946.js.map"
+       source_hash            = "fc9cc5d4d465431cd3519cd2fb42d18d"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/319.b1313d96.js"] will be destroyed
  # (because key ["js/319.b1313d96.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "4a7f1ac52f78a27caf50d353aa44d0c4" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/319.b1313d96.js" -> null
-       key                    = "dist/js/319.b1313d96.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/319.b1313d96.js" -> null
-       source_hash            = "4a7f1ac52f78a27caf50d353aa44d0c4" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "k9L2cgk3nQlh9pBX_PlbYnyO7hkcIUwJ" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/319.b1313d96.js.map"] will be destroyed
  # (because key ["js/319.b1313d96.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "08efe0a10e9de7fbf1b38d58db209ebc" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/319.b1313d96.js.map" -> null
-       key                    = "dist/js/319.b1313d96.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/319.b1313d96.js.map" -> null
-       source_hash            = "08efe0a10e9de7fbf1b38d58db209ebc" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "89Ngma.EpqmmCtQiRz.fREychY8kPdL9" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/332.78842ec1.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "fb92d7cee7f3969020d63fff5e49abbe"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/332.78842ec1.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/332.78842ec1.js"
+       source_hash            = "fb92d7cee7f3969020d63fff5e49abbe"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/332.78842ec1.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "f5bd06908c9b57fc44f54de432a59ed7"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/332.78842ec1.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/332.78842ec1.js.map"
+       source_hash            = "f5bd06908c9b57fc44f54de432a59ed7"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/332.7e1d0fc9.js"] will be destroyed
  # (because key ["js/332.7e1d0fc9.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "7aea6afa0393f62bdb838f650543fbef" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/332.7e1d0fc9.js" -> null
-       key                    = "dist/js/332.7e1d0fc9.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/332.7e1d0fc9.js" -> null
-       source_hash            = "7aea6afa0393f62bdb838f650543fbef" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "neZ69pLsYRZ07OTEIjZSX0aeQ9GioUBM" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/332.7e1d0fc9.js.map"] will be destroyed
  # (because key ["js/332.7e1d0fc9.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "39de4f7af98fb37d970ac4cc60065ae3" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/332.7e1d0fc9.js.map" -> null
-       key                    = "dist/js/332.7e1d0fc9.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/332.7e1d0fc9.js.map" -> null
-       source_hash            = "39de4f7af98fb37d970ac4cc60065ae3" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "IH9gcgf6H0mhx7jn114W8LfrCm0lzSxM" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/362.16cb8272.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "667c79476f9eb153ba7c38abce61325d"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/362.16cb8272.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/362.16cb8272.js"
+       source_hash            = "667c79476f9eb153ba7c38abce61325d"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/362.16cb8272.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "56227089d0505176dd9773137667d9b9"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/362.16cb8272.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/362.16cb8272.js.map"
+       source_hash            = "56227089d0505176dd9773137667d9b9"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/362.7be5561e.js"] will be destroyed
  # (because key ["js/362.7be5561e.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "6cc06578438e2b5c8413bede456de61d" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/362.7be5561e.js" -> null
-       key                    = "dist/js/362.7be5561e.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/362.7be5561e.js" -> null
-       source_hash            = "6cc06578438e2b5c8413bede456de61d" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "TstGtmcO5KuQvV5zMW1KPimhSK4ka7.4" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/362.7be5561e.js.map"] will be destroyed
  # (because key ["js/362.7be5561e.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "afe01a157cef2242ea325a1cdee72c84" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/362.7be5561e.js.map" -> null
-       key                    = "dist/js/362.7be5561e.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/362.7be5561e.js.map" -> null
-       source_hash            = "afe01a157cef2242ea325a1cdee72c84" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "Gcculrrhx18XC_veh_DC5NE9bcSGFNTL" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/409.4cd49965.js"] will be destroyed
  # (because key ["js/409.4cd49965.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "b7503f4e113a8cf5dd0d5624438fb728" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/409.4cd49965.js" -> null
-       key                    = "dist/js/409.4cd49965.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/409.4cd49965.js" -> null
-       source_hash            = "b7503f4e113a8cf5dd0d5624438fb728" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "Qg_tm2tLmsT3BHUIB4ct77acdG_rvmo1" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/409.4cd49965.js.map"] will be destroyed
  # (because key ["js/409.4cd49965.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "e5952dd15816155e4559ec16cea43381" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/409.4cd49965.js.map" -> null
-       key                    = "dist/js/409.4cd49965.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/409.4cd49965.js.map" -> null
-       source_hash            = "e5952dd15816155e4559ec16cea43381" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "uaaUZUyWWeiXHCse1OIsxMCXQKC8gPBZ" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/409.edf71f85.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "31abe823831f6a8cf08ddc3ff2de8984"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/409.edf71f85.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/409.edf71f85.js"
+       source_hash            = "31abe823831f6a8cf08ddc3ff2de8984"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/409.edf71f85.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "a5101ccc162fc74f878bcff1c2cc4ae1"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/409.edf71f85.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/409.edf71f85.js.map"
+       source_hash            = "a5101ccc162fc74f878bcff1c2cc4ae1"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/553.57500e98.js"] will be destroyed
  # (because key ["js/553.57500e98.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "f23ae52e35d2ce034e15e2570c99ecbe" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/553.57500e98.js" -> null
-       key                    = "dist/js/553.57500e98.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/553.57500e98.js" -> null
-       source_hash            = "f23ae52e35d2ce034e15e2570c99ecbe" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "fSfkBaGjLCcm3x6DnNCwEs146nfNHLmz" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/553.57500e98.js.map"] will be destroyed
  # (because key ["js/553.57500e98.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "4bdb6a2f9851df0b4bb7ec79fcfbe8ab" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/553.57500e98.js.map" -> null
-       key                    = "dist/js/553.57500e98.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/553.57500e98.js.map" -> null
-       source_hash            = "4bdb6a2f9851df0b4bb7ec79fcfbe8ab" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "qO0oHnZLUjPrk5KD1F71TlPGCh6gFQGA" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/553.8c6b6bce.js"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "text/javascript"
+       etag                   = "6a1a1dfafb4705cff64c1a6aa9ea7d59"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/553.8c6b6bce.js"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/553.8c6b6bce.js"
+       source_hash            = "6a1a1dfafb4705cff64c1a6aa9ea7d59"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/553.8c6b6bce.js.map"] will be created
+   resource "aws_s3_object" "origin_dist_artifact" {
+       acl                    = "private"
+       bucket                 = "gost-staging-origin-357150818708-us-west-2-website"
+       bucket_key_enabled     = (known after apply)
+       content_type           = "application/json"
+       etag                   = "3134ace8c0962132b9c0d9424cc7186c"
+       force_destroy          = false
+       id                     = (known after apply)
+       key                    = "dist/js/553.8c6b6bce.js.map"
+       kms_key_id             = (known after apply)
+       server_side_encryption = "AES256"
+       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/553.8c6b6bce.js.map"
+       source_hash            = "3134ace8c0962132b9c0d9424cc7186c"
+       storage_class          = (known after apply)
+       tags_all               = {
+           "env"        = "staging"
+           "management" = "terraform"
+           "owner"      = "grants"
+           "repo"       = "usdr-gost"
+           "service"    = "gost"
+           "usage"      = "workload"
        }
+       version_id             = (known after apply)
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/61.ad02d82d.js"] will be destroyed
  # (because key ["js/61.ad02d82d.js"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "text/javascript" -> null
-       etag                   = "187bf7aeee29f8c51bc6db420ee6c6b7" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/61.ad02d82d.js" -> null
-       key                    = "dist/js/61.ad02d82d.js" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/61.ad02d82d.js" -> null
-       source_hash            = "187bf7aeee29f8c51bc6db420ee6c6b7" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           "service"    = "gost"
-           "usage"      = "workload"
        } -> null
-       version_id             = "jA7.ufnaN58xNS8Sfjg6fFNs_31ny3Y_" -> null
    }

  # module.website.aws_s3_object.origin_dist_artifact["js/61.ad02d82d.js.map"] will be destroyed
  # (because key ["js/61.ad02d82d.js.map"] is not in for_each map)
-   resource "aws_s3_object" "origin_dist_artifact" {
-       acl                    = "private" -> null
-       bucket                 = "gost-staging-origin-357150818708-us-west-2-website" -> null
-       bucket_key_enabled     = false -> null
-       content_type           = "application/json" -> null
-       etag                   = "f24c1265bd0c19a2497c9bf93e0aaed7" -> null
-       force_destroy          = false -> null
-       id                     = "dist/js/61.ad02d82d.js.map" -> null
-       key                    = "dist/js/61.ad02d82d.js.map" -> null
-       metadata               = {} -> null
-       server_side_encryption = "AES256" -> null
-       source                 = "/home/runner/work/usdr-gost/usdr-gost/packages/client/dist/js/61.ad02d82d.js.map" -> null
-       source_hash            = "f24c1265bd0c19a2497c9bf93e0aaed7" -> null
-       storage_class          = "STANDARD" -> null
-       tags                   = {} -> null
-       tags_all               = {
-           "env"        = "staging"
-           "management" = "terraform"
-           "owner"      = "grants"
-           "repo"       = "usdr-gost"
-           ...*[Comment body truncated]*

@@ -348,7 +348,12 @@ router.put('/:grantId/assign/agencies', requireUser, async (req, res) => {
}

await db.assignGrantsToAgencies({ grantId, agencyIds, userId: user.id });
email.sendGrantAssignedEmail({ grantId, agencyIds, userId: user.id });
try {
email.sendGrantAssignedEmail({ grantId, agencyIds, userId: user.id });
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be await email.sendGrantAssignedEmail to ensure that it waits for the function to complete to catch the error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, good catch! And that probably also explains why the second test I was trying to write wasn't working... let me see if I can get a test for the 500 response as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ugh, I think the test is unfortunately not worth the effort (unless you have a quick read on how to get it working). I was trying to add something like this to the PUT /api/grants/:grantId/assign/agencies tests in __tests__/api/grants.test.js:

            it('gracefully returns 500 response for unexpected email errors', async () => {
                const emailSpy = sandbox.spy(email, 'sendGrantAssignedNotficationForAgency');
                sandbox.stub(email, 'buildGrantDetail').throws();
                const response = await fetchApi(`/grants/${assignEndpoint}`, agencies.own, {
                    ...fetchOptions.admin,
                    method: 'put',
                    body: JSON.stringify({ agencyIds: [agencies.own] }),
                });
                expect(response.statusText).to.equal('Internal Server Error');
                expect(emailSpy.notCalled).to.equal(true);
            });

I believe the issue is that our existing tools for stubbing and throwing don't have access to stub out the method that's getting called, since it's really more of an integration test than a unit test here. (Otherwise, we could use rewire or sinon to mock out the function the code is calling.) Anyway, I'm going to move on, but if you have the answer here, I'd love to hear it!

@jeffsmohan jeffsmohan requested a review from as1729 March 7, 2024 17:54
Copy link
Member

@TylerHendrickson TylerHendrickson left a comment

Choose a reason for hiding this comment

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

Chiming in to say I think this is okay to merge without testing the 500 scenario explicitly 🙂

Side note...
Just my 2¢, but I'm not entirely convinced the user/client needs to be made aware of the error in this case. If we had a paradigm where this type of thing rolled back the DB change (which would be nice, but definitely out-of-scope), it might be justified in giving the user a prompt to retry the failed operation, but given that the assignment has already occurred, I'm not sure what recourse the user really has when presented with "500 internal server error".

If there's a way to avoid swallowing the error without making it the user's problem, that feels better to me in the context of this particular situation. Better yet, it would be great if there was a more generic way to configure Express so that a request/response loop's unhandled error didn't bubble up to the point of crashing the process.

To be clear, I think this PR is okay to ship as-is; just take this as food-for-thought.

@jeffsmohan
Copy link
Contributor Author

jeffsmohan commented Mar 11, 2024

If there's a way to avoid swallowing the error without making it the user's problem, that feels better to me in the context of this particular situation. Better yet, it would be great if there was a more generic way to configure Express so that a request/response loop's unhandled error didn't bubble up to the point of crashing the process.

@TylerHendrickson Yeah totally! In general I like to push email sending into a task queue outside of the main API's request-response flow. Emails can be flaky or slow to send, so this gives us retries and keeps the send time from affecting API response latency. In general, if retries don't solve the issue, then there's probably nothing the user can do anyway, so it's not that helpful to surface the error.

I could see a broader refactoring of the email machinery to make it easier to catch errors at the right places, attempt retries, and handle it all on a message queue. But obviously that's beyond the scope for this little PR. :)

@jeffsmohan
Copy link
Contributor Author

@TylerHendrickson had to fix a merge conflict. Please re-approve when you get a chance!

@jeffsmohan jeffsmohan enabled auto-merge (squash) March 12, 2024 16:48
@jeffsmohan jeffsmohan merged commit 95e29f4 into main Mar 12, 2024
19 checks passed
@jeffsmohan jeffsmohan deleted the jmo-grant-assignment-email-errors branch March 12, 2024 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working javascript Pull requests that update Javascript code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants