diff --git a/eslint.config.mjs b/eslint.config.mjs
index 09a55d787..029ba22cd 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -115,7 +115,7 @@ export default defineConfig([
eslintImportResolverTypescript.createTypeScriptImportResolver({
project: [
'lambdas/*/tsconfig.json',
- 'tests/tsconfig.json',
+ 'lambdas/*/*/tsconfig.json', 'tests/tsconfig.json',
'internal/*/tsconfig.json',
],
}),
diff --git a/infrastructure/terraform/components/api/README.md b/infrastructure/terraform/components/api/README.md
index b21ae6675..2d1508de4 100644
--- a/infrastructure/terraform/components/api/README.md
+++ b/infrastructure/terraform/components/api/README.md
@@ -60,9 +60,9 @@ No requirements.
| Name | Source | Version |
| ---- | ------ | ------- |
-| [amendment\_event\_transformer](#module\_amendment\_event\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
+| [amendment\_event\_transformer](#module\_amendment\_event\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| [amendments\_queue](#module\_amendments\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.1.5/terraform-sqs.zip | n/a |
-| [authorizer\_lambda](#module\_authorizer\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
+| [authorizer\_lambda](#module\_authorizer\_lambda) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| [ddb\_alarms\_idempotency](#module\_ddb\_alarms\_idempotency) | ../../modules/alarms-ddb | n/a |
| [ddb\_alarms\_letter\_queue](#module\_ddb\_alarms\_letter\_queue) | ../../modules/alarms-ddb | n/a |
| [ddb\_alarms\_letters](#module\_ddb\_alarms\_letters) | ../../modules/alarms-ddb | n/a |
@@ -71,30 +71,33 @@ No requirements.
| [domain\_truststore](#module\_domain\_truststore) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-s3bucket.zip | n/a |
| [eventpub](#module\_eventpub) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/5.0.6/terraform-eventpub.zip | n/a |
| [eventsub](#module\_eventsub) | ../../modules/eventsub | n/a |
-| [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [get\_letter\_data](#module\_get\_letter\_data) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [get\_letters](#module\_get\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [get\_mi](#module\_get\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
-| [get\_status](#module\_get\_status) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
+| [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [get\_letter\_data](#module\_get\_letter\_data) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [get\_letters](#module\_get\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [get\_mi](#module\_get\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [get\_status](#module\_get\_status) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-kms.zip | n/a |
| [lambda\_alarms](#module\_lambda\_alarms) | ../../modules/alarms-lambda | n/a |
| [letter\_status\_updates\_queue](#module\_letter\_status\_updates\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.1.5/terraform-sqs.zip | n/a |
-| [letter\_updates\_transformer](#module\_letter\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [mi\_updates\_transformer](#module\_mi\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [post\_letters](#module\_post\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [post\_mi](#module\_post\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
+| [letter\_updates\_transformer](#module\_letter\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [mi\_updates\_transformer](#module\_mi\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [populate\_sameday\_breaches](#module\_populate\_sameday\_breaches) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [populate\_standard\_breaches](#module\_populate\_standard\_breaches) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [populate\_supplier\_config\_cache](#module\_populate\_supplier\_config\_cache) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [post\_letters](#module\_post\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [post\_mi](#module\_post\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| [s3bucket\_test\_letters](#module\_s3bucket\_test\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip | n/a |
| [sqs\_alarms](#module\_sqs\_alarms) | ../../modules/alarms-sqs | n/a |
| [sqs\_letter\_updates](#module\_sqs\_letter\_updates) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.1.5/terraform-sqs.zip | n/a |
| [sqs\_supplier\_allocator](#module\_sqs\_supplier\_allocator) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.1.5/terraform-sqs.zip | n/a |
| [sqs\_supplier\_config](#module\_sqs\_supplier\_config) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.1.5/terraform-sqs.zip | n/a |
-| [supplier\_allocator](#module\_supplier\_allocator) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [supplier\_config\_ingress](#module\_supplier\_config\_ingress) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.29/terraform-lambda.zip | n/a |
-| [supplier\_mock](#module\_supplier\_mock) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.5/terraform-lambda.zip | n/a |
+| [supplier\_allocator](#module\_supplier\_allocator) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [supplier\_config\_ingress](#module\_supplier\_config\_ingress) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [supplier\_mock](#module\_supplier\_mock) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| [supplier\_ssl](#module\_supplier\_ssl) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-ssl.zip | n/a |
-| [update\_letter\_queue](#module\_update\_letter\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
-| [upsert\_letter](#module\_upsert\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/4.0.2/terraform-lambda.zip | n/a |
+| [update\_letter\_queue](#module\_update\_letter\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
+| [upsert\_letter](#module\_upsert\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
## Outputs
| Name | Description |
diff --git a/infrastructure/terraform/components/api/iam_role_letter_sla_breaches.tf b/infrastructure/terraform/components/api/iam_role_letter_sla_breaches.tf
new file mode 100644
index 000000000..97a35bad2
--- /dev/null
+++ b/infrastructure/terraform/components/api/iam_role_letter_sla_breaches.tf
@@ -0,0 +1,17 @@
+resource "aws_iam_role" "letter_sla_breaches" {
+ name = "${local.csi}-letter-sla-breaches-sfn"
+ description = "Execution role for the letter SLA breaches state machine"
+ assume_role_policy = data.aws_iam_policy_document.letter_sla_breaches_trust_policy.json
+}
+
+data "aws_iam_policy_document" "letter_sla_breaches_trust_policy" {
+ statement {
+ effect = "Allow"
+ actions = ["sts:AssumeRole"]
+
+ principals {
+ type = "Service"
+ identifiers = ["states.amazonaws.com"]
+ }
+ }
+}
diff --git a/infrastructure/terraform/components/api/iam_role_policy_letter_sla_breaches.tf b/infrastructure/terraform/components/api/iam_role_policy_letter_sla_breaches.tf
new file mode 100644
index 000000000..d893cd8b0
--- /dev/null
+++ b/infrastructure/terraform/components/api/iam_role_policy_letter_sla_breaches.tf
@@ -0,0 +1,25 @@
+resource "aws_iam_role_policy" "letter_sla_breaches" {
+ name = "${local.csi}-letter-sla-breaches-sfn"
+ role = aws_iam_role.letter_sla_breaches.id
+ policy = data.aws_iam_policy_document.letter_sla_breaches_policy.json
+}
+
+data "aws_iam_policy_document" "letter_sla_breaches_policy" {
+ statement {
+ sid = "AllowListOwnExecutions"
+ effect = "Allow"
+ actions = ["states:ListExecutions"]
+ resources = [local.letter_sla_breaches_state_machine_arn]
+ }
+
+ statement {
+ sid = "AllowInvokeLambdas"
+ effect = "Allow"
+ actions = ["lambda:InvokeFunction"]
+ resources = [
+ module.populate_supplier_config_cache.function_arn,
+ module.populate_sameday_breaches.function_arn,
+ module.populate_standard_breaches.function_arn,
+ ]
+ }
+}
diff --git a/infrastructure/terraform/components/api/locals.tf b/infrastructure/terraform/components/api/locals.tf
index 6b4d90305..18c52e680 100644
--- a/infrastructure/terraform/components/api/locals.tf
+++ b/infrastructure/terraform/components/api/locals.tf
@@ -52,4 +52,7 @@ locals {
var.region,
var.csoc_destination_account
)
+
+ letter_sla_breaches_state_machine_name = "${local.csi}-letter-sla-breaches"
+ letter_sla_breaches_state_machine_arn = "arn:aws:states:${var.region}:${var.aws_account_id}:stateMachine:${local.letter_sla_breaches_state_machine_name}"
}
diff --git a/infrastructure/terraform/components/api/module_lambda_populate_sameday_breaches.tf b/infrastructure/terraform/components/api/module_lambda_populate_sameday_breaches.tf
new file mode 100644
index 000000000..020c4dd1c
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_lambda_populate_sameday_breaches.tf
@@ -0,0 +1,55 @@
+module "populate_sameday_breaches" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip"
+
+ function_name = "populate_sameday_breaches"
+ description = "Populates same-day breaches for the letter SLA breaches state machine"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ group = var.group
+
+ log_retention_in_days = var.log_retention_in_days
+ kms_key_arn = module.kms.key_arn
+
+ iam_policy_document = {
+ body = data.aws_iam_policy_document.populate_sameday_breaches_lambda.json
+ }
+
+ function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
+ function_code_base_path = local.aws_lambda_functions_dir_path
+ function_code_dir = "letter-sla-breaches/populate-sameday-breaches/dist"
+ function_include_common = true
+ handler_function_name = "populateSamedayBreachesHandler"
+ runtime = "nodejs22.x"
+ memory = 128
+ timeout = 29
+ log_level = var.log_level
+
+ force_lambda_code_deploy = var.force_lambda_code_deploy
+ enable_lambda_insights = false
+
+ log_destination_arn = local.destination_arn
+ odin_log_destination_arn = local.odin_destination_arn
+ log_subscription_role_arn = local.acct.log_subscription_role_arn
+
+ lambda_env_vars = {}
+}
+
+data "aws_iam_policy_document" "populate_sameday_breaches_lambda" {
+ statement {
+ sid = "KMSPermissions"
+ effect = "Allow"
+
+ actions = [
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ ]
+
+ resources = [
+ module.kms.key_arn,
+ ]
+ }
+}
diff --git a/infrastructure/terraform/components/api/module_lambda_populate_standard_breaches.tf b/infrastructure/terraform/components/api/module_lambda_populate_standard_breaches.tf
new file mode 100644
index 000000000..f5665bb82
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_lambda_populate_standard_breaches.tf
@@ -0,0 +1,55 @@
+module "populate_standard_breaches" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip"
+
+ function_name = "populate_standard_breaches"
+ description = "Populates standard breaches for the letter SLA breaches state machine"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ group = var.group
+
+ log_retention_in_days = var.log_retention_in_days
+ kms_key_arn = module.kms.key_arn
+
+ iam_policy_document = {
+ body = data.aws_iam_policy_document.populate_standard_breaches_lambda.json
+ }
+
+ function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
+ function_code_base_path = local.aws_lambda_functions_dir_path
+ function_code_dir = "letter-sla-breaches/populate-standard-breaches/dist"
+ function_include_common = true
+ handler_function_name = "populateStandardBreachesHandler"
+ runtime = "nodejs22.x"
+ memory = 128
+ timeout = 29
+ log_level = var.log_level
+
+ force_lambda_code_deploy = var.force_lambda_code_deploy
+ enable_lambda_insights = false
+
+ log_destination_arn = local.destination_arn
+ odin_log_destination_arn = local.odin_destination_arn
+ log_subscription_role_arn = local.acct.log_subscription_role_arn
+
+ lambda_env_vars = {}
+}
+
+data "aws_iam_policy_document" "populate_standard_breaches_lambda" {
+ statement {
+ sid = "KMSPermissions"
+ effect = "Allow"
+
+ actions = [
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ ]
+
+ resources = [
+ module.kms.key_arn,
+ ]
+ }
+}
diff --git a/infrastructure/terraform/components/api/module_lambda_populate_supplier_config_cache.tf b/infrastructure/terraform/components/api/module_lambda_populate_supplier_config_cache.tf
new file mode 100644
index 000000000..57bf707fa
--- /dev/null
+++ b/infrastructure/terraform/components/api/module_lambda_populate_supplier_config_cache.tf
@@ -0,0 +1,72 @@
+module "populate_supplier_config_cache" {
+ source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip"
+
+ function_name = "populate_supplier_config_cache"
+ description = "Populates the supplier config cache for the letter SLA breaches state machine"
+
+ aws_account_id = var.aws_account_id
+ component = var.component
+ environment = var.environment
+ project = var.project
+ region = var.region
+ group = var.group
+
+ log_retention_in_days = var.log_retention_in_days
+ kms_key_arn = module.kms.key_arn
+
+ iam_policy_document = {
+ body = data.aws_iam_policy_document.populate_supplier_config_cache_lambda.json
+ }
+
+ function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
+ function_code_base_path = local.aws_lambda_functions_dir_path
+ function_code_dir = "letter-sla-breaches/populate-supplier-config-cache/dist"
+ function_include_common = true
+ handler_function_name = "populateSupplierConfigCacheHandler"
+ runtime = "nodejs22.x"
+ memory = 128
+ timeout = 29
+ log_level = var.log_level
+
+ force_lambda_code_deploy = var.force_lambda_code_deploy
+ enable_lambda_insights = false
+
+ log_destination_arn = local.destination_arn
+ odin_log_destination_arn = local.odin_destination_arn
+ log_subscription_role_arn = local.acct.log_subscription_role_arn
+
+ lambda_env_vars = {
+ SUPPLIER_CONFIG_TABLE_NAME = aws_dynamodb_table.supplier-configuration.name
+ EVENT_CACHE_BUCKET_NAME = local.eventsub_event_cache_bucket_name
+ }
+}
+
+data "aws_iam_policy_document" "populate_supplier_config_cache_lambda" {
+ statement {
+ sid = "KMSPermissions"
+ effect = "Allow"
+
+ actions = [
+ "kms:Decrypt",
+ "kms:GenerateDataKey",
+ ]
+
+ resources = [
+ module.kms.key_arn,
+ ]
+ }
+
+ statement {
+ sid = "AllowQuerySupplierConfigTable"
+ effect = "Allow"
+ actions = ["dynamodb:Query"]
+ resources = [aws_dynamodb_table.supplier-configuration.arn]
+ }
+
+ statement {
+ sid = "AllowWriteSupplierConfigCache"
+ effect = "Allow"
+ actions = ["s3:PutObject"]
+ resources = ["arn:aws:s3:::${local.eventsub_event_cache_bucket_name}/supplier_config_cache/*"]
+ }
+}
diff --git a/infrastructure/terraform/components/api/resources/letter_sla_breaches.json b/infrastructure/terraform/components/api/resources/letter_sla_breaches.json
new file mode 100644
index 000000000..725b8eaf3
--- /dev/null
+++ b/infrastructure/terraform/components/api/resources/letter_sla_breaches.json
@@ -0,0 +1,77 @@
+{
+ "Comment": "Identifies letters in breach of SLA for a given date (defaults to today)",
+ "QueryLanguage": "JSONata",
+ "StartAt": "CheckForRunningExecutions",
+ "States": {
+ "AlreadyRunning?": {
+ "Choices": [
+ {
+ "Condition": "{% $count($states.input.Executions) > 1 %}",
+ "Next": "ExitAlreadyRunning"
+ }
+ ],
+ "Default": "PopulateSupplierConfigCache",
+ "Type": "Choice"
+ },
+ "CheckForRunningExecutions": {
+ "Arguments": {
+ "StateMachineArn": "${STATE_MACHINE_ARN}",
+ "StatusFilter": "RUNNING"
+ },
+ "Assign": {
+ "date": "{% $exists($states.context.Execution.Input.date) ? $states.context.Execution.Input.date : $substring($states.context.Execution.StartTime, 0, 10) %}"
+ },
+ "Next": "AlreadyRunning?",
+ "Resource": "arn:aws:states:::aws-sdk:sfn:listExecutions",
+ "Type": "Task"
+ },
+ "ExitAlreadyRunning": {
+ "Type": "Succeed"
+ },
+ "PopulateSamedayBreaches": {
+ "Arguments": {
+ "FunctionName": "${POPULATE_SAMEDAY_BREACHES_FUNCTION_ARN}",
+ "Payload": {
+ "date": "{% $date %}"
+ }
+ },
+ "Next": "PopulateStandardBreaches",
+ "Output": {
+ "populateSamedayBreaches": "{% $states.result.Payload %}",
+ "populateSupplierConfigCache": "{% $states.input.populateSupplierConfigCache %}"
+ },
+ "Resource": "arn:aws:states:::lambda:invoke",
+ "Type": "Task"
+ },
+ "PopulateStandardBreaches": {
+ "Arguments": {
+ "FunctionName": "${POPULATE_STANDARD_BREACHES_FUNCTION_ARN}",
+ "Payload": {
+ "date": "{% $date %}"
+ }
+ },
+ "End": true,
+ "Output": {
+ "populateSamedayBreaches": "{% $states.input.populateSamedayBreaches %}",
+ "populateStandardBreaches": "{% $states.result.Payload %}",
+ "populateSupplierConfigCache": "{% $states.input.populateSupplierConfigCache %}"
+ },
+ "Resource": "arn:aws:states:::lambda:invoke",
+ "Type": "Task"
+ },
+ "PopulateSupplierConfigCache": {
+ "Arguments": {
+ "FunctionName": "${POPULATE_SUPPLIER_CONFIG_CACHE_FUNCTION_ARN}",
+ "Payload": {
+ "date": "{% $date %}"
+ }
+ },
+ "Next": "PopulateSamedayBreaches",
+ "Output": {
+ "populateSupplierConfigCache": "{% $states.result.Payload %}"
+ },
+ "Resource": "arn:aws:states:::lambda:invoke",
+ "Type": "Task"
+ }
+ }
+}
diff --git a/infrastructure/terraform/components/api/sfn_state_machine_letter_sla_breaches.tf b/infrastructure/terraform/components/api/sfn_state_machine_letter_sla_breaches.tf
new file mode 100644
index 000000000..19d726103
--- /dev/null
+++ b/infrastructure/terraform/components/api/sfn_state_machine_letter_sla_breaches.tf
@@ -0,0 +1,11 @@
+resource "aws_sfn_state_machine" "letter_sla_breaches" {
+ name = local.letter_sla_breaches_state_machine_name
+ role_arn = aws_iam_role.letter_sla_breaches.arn
+
+ definition = templatefile("${path.module}/resources/letter_sla_breaches.json", {
+ STATE_MACHINE_ARN = local.letter_sla_breaches_state_machine_arn
+ POPULATE_SUPPLIER_CONFIG_CACHE_FUNCTION_ARN = module.populate_supplier_config_cache.function_arn
+ POPULATE_SAMEDAY_BREACHES_FUNCTION_ARN = module.populate_sameday_breaches.function_arn
+ POPULATE_STANDARD_BREACHES_FUNCTION_ARN = module.populate_standard_breaches.function_arn
+ })
+}
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/.gitignore b/lambdas/letter-sla-breaches/populate-sameday-breaches/.gitignore
new file mode 100644
index 000000000..fc5a7859a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/.gitignore
@@ -0,0 +1,5 @@
+coverage
+node_modules
+dist
+.reports
+.aws-sam
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/jest.config.ts b/lambdas/letter-sla-breaches/populate-sameday-breaches/jest.config.ts
new file mode 100644
index 000000000..ec5a312bb
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/jest.config.ts
@@ -0,0 +1,46 @@
+export const baseJestConfig = {
+ preset: "ts-jest",
+ extensionsToTreatAsEsm: [".ts"],
+ transform: {
+ "^.+\\.ts$": [
+ "ts-jest",
+ {
+ useESM: true,
+ },
+ ],
+ },
+
+ clearMocks: true,
+ collectCoverage: true,
+ coverageDirectory: "./.reports/unit/coverage",
+ coverageProvider: "babel",
+
+ coverageThreshold: {
+ global: {
+ branches: 100,
+ functions: 100,
+ lines: 100,
+ statements: -10,
+ },
+ },
+
+ coveragePathIgnorePatterns: ["/__tests__/"],
+ testPathIgnorePatterns: [".build"],
+ testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
+
+ reporters: [
+ "default",
+ [
+ "jest-html-reporter",
+ {
+ pageTitle: "Test Report",
+ outputPath: "./.reports/unit/test-report.html",
+ includeFailureMsg: true,
+ },
+ ],
+ ],
+
+ testEnvironment: "node",
+};
+
+export default baseJestConfig;
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/package.json b/lambdas/letter-sla-breaches/populate-sameday-breaches/package.json
new file mode 100644
index 000000000..127c212c3
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/package.json
@@ -0,0 +1,15 @@
+{
+ "dependencies": {
+ "esbuild": "0.28.2"
+ },
+ "name": "nhs-notify-supplier-api-populate-sameday-breaches",
+ "private": true,
+ "scripts": {
+ "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "test:unit": "jest",
+ "typecheck": "tsc --noEmit"
+ },
+ "version": "0.0.1"
+}
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/src/__tests__/populate-sameday-breaches.test.ts b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/__tests__/populate-sameday-breaches.test.ts
new file mode 100644
index 000000000..50492a04a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/__tests__/populate-sameday-breaches.test.ts
@@ -0,0 +1,12 @@
+import populateSamedayBreaches from "../handler/populate-sameday-breaches";
+
+describe("populateSamedayBreaches", () => {
+ it("returns a success result", async () => {
+ const result = await populateSamedayBreaches({ date: "2026-10-08" });
+
+ expect(result).toEqual({
+ status: "SUCCESS",
+ message: "populate-sameday-breaches invoked successfully",
+ });
+ });
+});
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/src/handler/populate-sameday-breaches.ts b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/handler/populate-sameday-breaches.ts
new file mode 100644
index 000000000..f63d70492
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/handler/populate-sameday-breaches.ts
@@ -0,0 +1,18 @@
+export type PopulateSamedayBreachesEvent = {
+ date: string;
+};
+
+export type PopulateSamedayBreachesResult = {
+ status: "SUCCESS";
+ message: string;
+};
+
+// Placeholder: cache population is not yet implemented.
+export default async function populateSamedayBreaches(
+ _event: PopulateSamedayBreachesEvent,
+): Promise {
+ return {
+ status: "SUCCESS",
+ message: "populate-sameday-breaches invoked successfully",
+ };
+}
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/src/index.ts b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/index.ts
new file mode 100644
index 000000000..3143567ed
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/src/index.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import-x/prefer-default-export
+export { default as populateSamedayBreachesHandler } from "./handler/populate-sameday-breaches";
diff --git a/lambdas/letter-sla-breaches/populate-sameday-breaches/tsconfig.json b/lambdas/letter-sla-breaches/populate-sameday-breaches/tsconfig.json
new file mode 100644
index 000000000..4a11b255a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-sameday-breaches/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "ignoreDeprecations": "5.0"
+ },
+ "extends": "../../../tsconfig.base.json",
+ "include": [
+ "src/**/*",
+ "jest.config.ts"
+ ]
+}
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/.gitignore b/lambdas/letter-sla-breaches/populate-standard-breaches/.gitignore
new file mode 100644
index 000000000..fc5a7859a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/.gitignore
@@ -0,0 +1,5 @@
+coverage
+node_modules
+dist
+.reports
+.aws-sam
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/jest.config.ts b/lambdas/letter-sla-breaches/populate-standard-breaches/jest.config.ts
new file mode 100644
index 000000000..ec5a312bb
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/jest.config.ts
@@ -0,0 +1,46 @@
+export const baseJestConfig = {
+ preset: "ts-jest",
+ extensionsToTreatAsEsm: [".ts"],
+ transform: {
+ "^.+\\.ts$": [
+ "ts-jest",
+ {
+ useESM: true,
+ },
+ ],
+ },
+
+ clearMocks: true,
+ collectCoverage: true,
+ coverageDirectory: "./.reports/unit/coverage",
+ coverageProvider: "babel",
+
+ coverageThreshold: {
+ global: {
+ branches: 100,
+ functions: 100,
+ lines: 100,
+ statements: -10,
+ },
+ },
+
+ coveragePathIgnorePatterns: ["/__tests__/"],
+ testPathIgnorePatterns: [".build"],
+ testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
+
+ reporters: [
+ "default",
+ [
+ "jest-html-reporter",
+ {
+ pageTitle: "Test Report",
+ outputPath: "./.reports/unit/test-report.html",
+ includeFailureMsg: true,
+ },
+ ],
+ ],
+
+ testEnvironment: "node",
+};
+
+export default baseJestConfig;
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/package.json b/lambdas/letter-sla-breaches/populate-standard-breaches/package.json
new file mode 100644
index 000000000..8875e4acc
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/package.json
@@ -0,0 +1,15 @@
+{
+ "dependencies": {
+ "esbuild": "0.28.2"
+ },
+ "name": "nhs-notify-supplier-api-populate-standard-breaches",
+ "private": true,
+ "scripts": {
+ "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "test:unit": "jest",
+ "typecheck": "tsc --noEmit"
+ },
+ "version": "0.0.1"
+}
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/src/__tests__/populate-standard-breaches.test.ts b/lambdas/letter-sla-breaches/populate-standard-breaches/src/__tests__/populate-standard-breaches.test.ts
new file mode 100644
index 000000000..f64093251
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/src/__tests__/populate-standard-breaches.test.ts
@@ -0,0 +1,12 @@
+import populateStandardBreaches from "../handler/populate-standard-breaches";
+
+describe("populateStandardBreaches", () => {
+ it("returns a success result", async () => {
+ const result = await populateStandardBreaches({ date: "2026-10-08" });
+
+ expect(result).toEqual({
+ status: "SUCCESS",
+ message: "populate-standard-breaches invoked successfully",
+ });
+ });
+});
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/src/handler/populate-standard-breaches.ts b/lambdas/letter-sla-breaches/populate-standard-breaches/src/handler/populate-standard-breaches.ts
new file mode 100644
index 000000000..1bcc2318c
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/src/handler/populate-standard-breaches.ts
@@ -0,0 +1,18 @@
+export type PopulateStandardBreachesEvent = {
+ date: string;
+};
+
+export type PopulateStandardBreachesResult = {
+ status: "SUCCESS";
+ message: string;
+};
+
+// Placeholder: cache population is not yet implemented.
+export default async function populateStandardBreaches(
+ _event: PopulateStandardBreachesEvent,
+): Promise {
+ return {
+ status: "SUCCESS",
+ message: "populate-standard-breaches invoked successfully",
+ };
+}
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/src/index.ts b/lambdas/letter-sla-breaches/populate-standard-breaches/src/index.ts
new file mode 100644
index 000000000..5516fb886
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/src/index.ts
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import-x/prefer-default-export
+export { default as populateStandardBreachesHandler } from "./handler/populate-standard-breaches";
diff --git a/lambdas/letter-sla-breaches/populate-standard-breaches/tsconfig.json b/lambdas/letter-sla-breaches/populate-standard-breaches/tsconfig.json
new file mode 100644
index 000000000..4a11b255a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-standard-breaches/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "ignoreDeprecations": "5.0"
+ },
+ "extends": "../../../tsconfig.base.json",
+ "include": [
+ "src/**/*",
+ "jest.config.ts"
+ ]
+}
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/.gitignore b/lambdas/letter-sla-breaches/populate-supplier-config-cache/.gitignore
new file mode 100644
index 000000000..fc5a7859a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/.gitignore
@@ -0,0 +1,5 @@
+coverage
+node_modules
+dist
+.reports
+.aws-sam
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/jest.config.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/jest.config.ts
new file mode 100644
index 000000000..ec5a312bb
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/jest.config.ts
@@ -0,0 +1,46 @@
+export const baseJestConfig = {
+ preset: "ts-jest",
+ extensionsToTreatAsEsm: [".ts"],
+ transform: {
+ "^.+\\.ts$": [
+ "ts-jest",
+ {
+ useESM: true,
+ },
+ ],
+ },
+
+ clearMocks: true,
+ collectCoverage: true,
+ coverageDirectory: "./.reports/unit/coverage",
+ coverageProvider: "babel",
+
+ coverageThreshold: {
+ global: {
+ branches: 100,
+ functions: 100,
+ lines: 100,
+ statements: -10,
+ },
+ },
+
+ coveragePathIgnorePatterns: ["/__tests__/"],
+ testPathIgnorePatterns: [".build"],
+ testMatch: ["**/?(*.)+(spec|test).[jt]s?(x)"],
+
+ reporters: [
+ "default",
+ [
+ "jest-html-reporter",
+ {
+ pageTitle: "Test Report",
+ outputPath: "./.reports/unit/test-report.html",
+ includeFailureMsg: true,
+ },
+ ],
+ ],
+
+ testEnvironment: "node",
+};
+
+export default baseJestConfig;
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/package.json b/lambdas/letter-sla-breaches/populate-supplier-config-cache/package.json
new file mode 100644
index 000000000..034e0301b
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/package.json
@@ -0,0 +1,18 @@
+{
+ "dependencies": {
+ "@aws-sdk/client-dynamodb": "^3.1138.0",
+ "@aws-sdk/client-s3": "^3.1138.0",
+ "@aws-sdk/lib-dynamodb": "^3.1138.0",
+ "esbuild": "0.28.2"
+ },
+ "name": "nhs-notify-supplier-api-populate-supplier-config-cache",
+ "private": true,
+ "scripts": {
+ "lambda-build": "rm -rf dist && npx esbuild --bundle --minify --sourcemap --target=es2020 --platform=node --loader:.node=file --entry-names=[name] --outdir=dist src/index.ts",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix",
+ "test:unit": "jest",
+ "typecheck": "tsc --noEmit"
+ },
+ "version": "0.0.1"
+}
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/entity-cache.test.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/entity-cache.test.ts
new file mode 100644
index 000000000..1dac234a9
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/entity-cache.test.ts
@@ -0,0 +1,72 @@
+import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
+import { DynamoDBDocumentClient, QueryCommand } from "@aws-sdk/lib-dynamodb";
+import populateEntityCache, { EntityCacheDeps } from "../handler/entity-cache";
+
+function makeDeps(ddbSend: jest.Mock, s3Send: jest.Mock): EntityCacheDeps {
+ return {
+ ddbClient: { send: ddbSend } as unknown as DynamoDBDocumentClient,
+ s3Client: { send: s3Send } as unknown as S3Client,
+ env: {
+ SUPPLIER_CONFIG_TABLE_NAME: "config-table",
+ EVENT_CACHE_BUCKET_NAME: "cache-bucket",
+ },
+ };
+}
+
+describe("populateEntityCache", () => {
+ it("queries all pages for the entity and writes them to S3", async () => {
+ const ddbSend = jest
+ .fn()
+ .mockResolvedValueOnce({
+ Items: [{ pk: "ENTITY#supplier", sk: "ID#1" }],
+ LastEvaluatedKey: { pk: "ENTITY#supplier", sk: "ID#1" },
+ })
+ .mockResolvedValueOnce({
+ Items: [{ pk: "ENTITY#supplier", sk: "ID#2" }],
+ });
+ const s3Send = jest.fn().mockResolvedValue({});
+
+ const count = await populateEntityCache(
+ makeDeps(ddbSend, s3Send),
+ "supplier",
+ );
+
+ expect(count).toBe(2);
+ expect(ddbSend).toHaveBeenCalledTimes(2);
+ const firstQuery = ddbSend.mock.calls[0][0] as QueryCommand;
+ expect(firstQuery.input).toEqual({
+ TableName: "config-table",
+ KeyConditionExpression: "pk = :pk",
+ ExpressionAttributeValues: { ":pk": "ENTITY#supplier" },
+ ExclusiveStartKey: undefined,
+ });
+ const secondQuery = ddbSend.mock.calls[1][0] as QueryCommand;
+ expect(secondQuery.input.ExclusiveStartKey).toEqual({
+ pk: "ENTITY#supplier",
+ sk: "ID#1",
+ });
+
+ const put = s3Send.mock.calls[0][0] as PutObjectCommand;
+ expect(put.input.Bucket).toBe("cache-bucket");
+ expect(put.input.Key).toBe("supplier_config_cache/supplier.json");
+ expect(JSON.parse(put.input.Body as string)).toEqual([
+ { pk: "ENTITY#supplier", sk: "ID#1" },
+ { pk: "ENTITY#supplier", sk: "ID#2" },
+ ]);
+ });
+
+ it("writes an empty file when no rows are returned", async () => {
+ const ddbSend = jest.fn().mockResolvedValue({});
+ const s3Send = jest.fn().mockResolvedValue({});
+
+ const count = await populateEntityCache(
+ makeDeps(ddbSend, s3Send),
+ "volume-group",
+ );
+
+ expect(count).toBe(0);
+ const put = s3Send.mock.calls[0][0] as PutObjectCommand;
+ expect(put.input.Key).toBe("supplier_config_cache/volume-group.json");
+ expect(put.input.Body).toBe("[]");
+ });
+});
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/populate-supplier-config-cache.test.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/populate-supplier-config-cache.test.ts
new file mode 100644
index 000000000..54374eb2f
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/__tests__/populate-supplier-config-cache.test.ts
@@ -0,0 +1,32 @@
+import populateEntityCache, { EntityCacheDeps } from "../handler/entity-cache";
+import createHandler, {
+ ENTITY_TYPES,
+} from "../handler/populate-supplier-config-cache";
+
+jest.mock("../handler/entity-cache");
+
+describe("populateSupplierConfigCache", () => {
+ it("populates the cache once for each entity type and returns success", async () => {
+ const deps = {} as EntityCacheDeps;
+ const handler = createHandler(deps);
+
+ const result = await handler({ date: "2026-10-08" });
+
+ expect(ENTITY_TYPES).toEqual([
+ "volume-group",
+ "supplier-pack",
+ "letter-variant",
+ "pack-specification",
+ "supplier-allocation",
+ "supplier",
+ ]);
+ expect(populateEntityCache).toHaveBeenCalledTimes(ENTITY_TYPES.length);
+ for (const entityType of ENTITY_TYPES) {
+ expect(populateEntityCache).toHaveBeenCalledWith(deps, entityType);
+ }
+ expect(result).toEqual({
+ status: "SUCCESS",
+ message: "populate-supplier-config-cache invoked successfully",
+ });
+ });
+});
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/deps.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/deps.ts
new file mode 100644
index 000000000..6fdf2f926
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/deps.ts
@@ -0,0 +1,15 @@
+import { S3Client } from "@aws-sdk/client-s3";
+import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
+import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
+import { EntityCacheDeps } from "./entity-cache";
+
+export default function createDependenciesContainer(): EntityCacheDeps {
+ return {
+ ddbClient: DynamoDBDocumentClient.from(new DynamoDBClient()),
+ s3Client: new S3Client(),
+ env: {
+ SUPPLIER_CONFIG_TABLE_NAME: process.env.SUPPLIER_CONFIG_TABLE_NAME!,
+ EVENT_CACHE_BUCKET_NAME: process.env.EVENT_CACHE_BUCKET_NAME!,
+ },
+ };
+}
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/entity-cache.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/entity-cache.ts
new file mode 100644
index 000000000..eab1beef6
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/entity-cache.ts
@@ -0,0 +1,49 @@
+import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
+import {
+ DynamoDBDocumentClient,
+ QueryCommand,
+ QueryCommandOutput,
+} from "@aws-sdk/lib-dynamodb";
+
+export type EntityCacheDeps = {
+ ddbClient: DynamoDBDocumentClient;
+ s3Client: S3Client;
+ env: {
+ SUPPLIER_CONFIG_TABLE_NAME: string;
+ EVENT_CACHE_BUCKET_NAME: string;
+ };
+};
+
+export const CACHE_FOLDER = "supplier_config_cache";
+
+export default async function populateEntityCache(
+ deps: EntityCacheDeps,
+ entityType: string,
+): Promise {
+ const items: Record[] = [];
+ let exclusiveStartKey: QueryCommandOutput["LastEvaluatedKey"];
+
+ do {
+ const page: QueryCommandOutput = await deps.ddbClient.send(
+ new QueryCommand({
+ TableName: deps.env.SUPPLIER_CONFIG_TABLE_NAME,
+ KeyConditionExpression: "pk = :pk",
+ ExpressionAttributeValues: { ":pk": `ENTITY#${entityType}` },
+ ExclusiveStartKey: exclusiveStartKey,
+ }),
+ );
+ items.push(...(page.Items ?? []));
+ exclusiveStartKey = page.LastEvaluatedKey;
+ } while (exclusiveStartKey);
+
+ await deps.s3Client.send(
+ new PutObjectCommand({
+ Bucket: deps.env.EVENT_CACHE_BUCKET_NAME,
+ Key: `${CACHE_FOLDER}/${entityType}.json`,
+ Body: JSON.stringify(items),
+ ContentType: "application/json",
+ }),
+ );
+
+ return items.length;
+}
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/populate-supplier-config-cache.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/populate-supplier-config-cache.ts
new file mode 100644
index 000000000..b3c3d57fb
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/handler/populate-supplier-config-cache.ts
@@ -0,0 +1,34 @@
+import populateEntityCache, { EntityCacheDeps } from "./entity-cache";
+
+export type PopulateSupplierConfigCacheEvent = {
+ date: string;
+};
+
+export type PopulateSupplierConfigCacheResult = {
+ status: "SUCCESS";
+ message: string;
+};
+
+export const ENTITY_TYPES = [
+ "volume-group",
+ "supplier-pack",
+ "letter-variant",
+ "pack-specification",
+ "supplier-allocation",
+ "supplier",
+];
+
+export default function createHandler(deps: EntityCacheDeps) {
+ return async (
+ _event: PopulateSupplierConfigCacheEvent,
+ ): Promise => {
+ await Promise.all(
+ ENTITY_TYPES.map((entityType) => populateEntityCache(deps, entityType)),
+ );
+
+ return {
+ status: "SUCCESS",
+ message: "populate-supplier-config-cache invoked successfully",
+ };
+ };
+}
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/index.ts b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/index.ts
new file mode 100644
index 000000000..fd06c3b3f
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/src/index.ts
@@ -0,0 +1,7 @@
+import createDependenciesContainer from "./handler/deps";
+import createHandler from "./handler/populate-supplier-config-cache";
+
+// eslint-disable-next-line import-x/prefer-default-export
+export const populateSupplierConfigCacheHandler = createHandler(
+ createDependenciesContainer(),
+);
diff --git a/lambdas/letter-sla-breaches/populate-supplier-config-cache/tsconfig.json b/lambdas/letter-sla-breaches/populate-supplier-config-cache/tsconfig.json
new file mode 100644
index 000000000..4a11b255a
--- /dev/null
+++ b/lambdas/letter-sla-breaches/populate-supplier-config-cache/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "ignoreDeprecations": "5.0"
+ },
+ "extends": "../../../tsconfig.base.json",
+ "include": [
+ "src/**/*",
+ "jest.config.ts"
+ ]
+}
diff --git a/package-lock.json b/package-lock.json
index cae5e3cea..a80f25d9a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"docs",
"internal/*",
"lambdas/*",
+ "lambdas/letter-sla-breaches/*",
"pact-contracts",
"scripts/utilities/*",
"tests",
@@ -155,30 +156,6 @@
"node": ">=20.0.0"
}
},
- "internal/datastore/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "internal/datastore/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"internal/datastore/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -196,33 +173,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "internal/datastore/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "internal/datastore/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"internal/datastore/node_modules/@nhsdigital/nhs-notify-event-schemas-supplier-config": {
"version": "1.1.1",
"dependencies": {
@@ -241,7 +191,7 @@
},
"internal/events": {
"name": "@nhsdigital/nhs-notify-event-schemas-supplier-api",
- "version": "1.0.21",
+ "version": "1.0.22",
"license": "MIT",
"dependencies": {
"@asyncapi/bundler": "^1.0.2",
@@ -365,30 +315,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/api-handler/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/api-handler/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/api-handler/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -406,20 +332,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "lambdas/api-handler/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/api-handler/node_modules/@aws-sdk/middleware-sdk-sqs": {
"version": "3.972.43",
"license": "Apache-2.0",
@@ -433,19 +345,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/api-handler/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/authorizer": {
"name": "nhs-notify-supplier-authorizer",
"version": "0.0.1",
@@ -480,30 +379,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/authorizer/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/authorizer/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/authorizer/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -521,31 +396,28 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "lambdas/authorizer/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
+ "lambdas/letter-sla-breaches/populate-sameday-breaches": {
+ "name": "nhs-notify-supplier-api-populate-sameday-breaches",
+ "version": "0.0.1",
"dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
+ "esbuild": "0.28.2"
}
},
- "lambdas/authorizer/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
+ "lambdas/letter-sla-breaches/populate-standard-breaches": {
+ "name": "nhs-notify-supplier-api-populate-standard-breaches",
+ "version": "0.0.1",
"dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
+ "esbuild": "0.28.2"
+ }
+ },
+ "lambdas/letter-sla-breaches/populate-supplier-config-cache": {
+ "name": "nhs-notify-supplier-api-populate-supplier-config-cache",
+ "version": "0.0.1",
+ "dependencies": {
+ "@aws-sdk/client-dynamodb": "^3.1138.0",
+ "@aws-sdk/client-s3": "^3.1138.0",
+ "@aws-sdk/lib-dynamodb": "^3.1138.0",
+ "esbuild": "0.28.2"
}
},
"lambdas/letter-updates-transformer": {
@@ -565,90 +437,6 @@
"zod": "^4.6.5"
}
},
- "lambdas/letter-updates-transformer/node_modules/@aws-sdk/client-dynamodb": {
- "version": "3.1146.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1146.0.tgz",
- "integrity": "sha512-3Mc8YkSu2KlgNNSuspaW+Fo0pTwX+fLYlC5ovH3yX3OoDg2RPtp3Py8KuQJr0FDlQj1tHw0K3+AtaH4NXlI3aQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@aws-sdk/credential-provider-node": "^3.972.84",
- "@aws-sdk/dynamodb-codec": "^3.973.46",
- "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/fetch-http-handler": "^5.8.0",
- "@smithy/node-http-handler": "^4.12.1",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/letter-updates-transformer/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.46.tgz",
- "integrity": "sha512-ApnSubn+5C6HQGP7025q9w/YiNzKwimIMksP76xVij779BCCqcmJ9poB6hQ4syY9NUxqmEQLBqY0PktX53L8iQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/letter-updates-transformer/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.11.tgz",
- "integrity": "sha512-8q1ICxcDjHId3bBryuu/j+1L9y5/3uQnwzLDt5j2ElcjZSoWmFtymdJy7OjLrluSMe0Z4mq5bcH4fxBXvlEHfw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/letter-updates-transformer/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.31.tgz",
- "integrity": "sha512-JqOIXupzP6cpAmCCmuEaacbXo/bVCcg1NkglCJC6q4wDAvJO09qIs7POf50JNzmhOhWPtfXvzgWxb5nMQl8QiA==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/letter-updates-transformer/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.996.9.tgz",
- "integrity": "sha512-16x2tRvl7OYpZ0W/DdFJieFriD13+RvuRBDbe5sj/tCEfK86HSGd7I2s5j0ivz8p6KWGkS+5wKRO9OliJkjUOQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/mi-updates-transformer": {
"name": "nhs-notify-supplier-api-mi-updates-transformer",
"version": "0.0.1",
@@ -665,90 +453,6 @@
"zod": "^4.6.5"
}
},
- "lambdas/mi-updates-transformer/node_modules/@aws-sdk/client-dynamodb": {
- "version": "3.1146.0",
- "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1146.0.tgz",
- "integrity": "sha512-3Mc8YkSu2KlgNNSuspaW+Fo0pTwX+fLYlC5ovH3yX3OoDg2RPtp3Py8KuQJr0FDlQj1tHw0K3+AtaH4NXlI3aQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@aws-sdk/credential-provider-node": "^3.972.84",
- "@aws-sdk/dynamodb-codec": "^3.973.46",
- "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/fetch-http-handler": "^5.8.0",
- "@smithy/node-http-handler": "^4.12.1",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/mi-updates-transformer/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.46.tgz",
- "integrity": "sha512-ApnSubn+5C6HQGP7025q9w/YiNzKwimIMksP76xVij779BCCqcmJ9poB6hQ4syY9NUxqmEQLBqY0PktX53L8iQ==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/mi-updates-transformer/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.11.tgz",
- "integrity": "sha512-8q1ICxcDjHId3bBryuu/j+1L9y5/3uQnwzLDt5j2ElcjZSoWmFtymdJy7OjLrluSMe0Z4mq5bcH4fxBXvlEHfw==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/mi-updates-transformer/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.31.tgz",
- "integrity": "sha512-JqOIXupzP6cpAmCCmuEaacbXo/bVCcg1NkglCJC6q4wDAvJO09qIs7POf50JNzmhOhWPtfXvzgWxb5nMQl8QiA==",
- "license": "Apache-2.0",
- "peer": true,
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/mi-updates-transformer/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.996.9.tgz",
- "integrity": "sha512-16x2tRvl7OYpZ0W/DdFJieFriD13+RvuRBDbe5sj/tCEfK86HSGd7I2s5j0ivz8p6KWGkS+5wKRO9OliJkjUOQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/supplier-allocator": {
"name": "nhs-notify-supplier-api-allocate-letter",
"version": "0.0.1",
@@ -856,30 +560,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/supplier-allocator/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/supplier-allocator/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/supplier-allocator/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -897,20 +577,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "lambdas/supplier-allocator/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/supplier-allocator/node_modules/@aws-sdk/middleware-sdk-sqs": {
"version": "3.972.43",
"license": "Apache-2.0",
@@ -924,19 +590,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/supplier-allocator/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/supplier-allocator/node_modules/@aws/lambda-invoke-store": {
"version": "0.3.0",
"license": "Apache-2.0",
@@ -1014,30 +667,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/supplier-config-ingress/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/supplier-config-ingress/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/supplier-config-ingress/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -1055,33 +684,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "lambdas/supplier-config-ingress/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/supplier-config-ingress/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/supplier-config-ingress/node_modules/@nhsdigital/nhs-notify-event-schemas-supplier-config": {
"version": "1.1.1",
"dependencies": {
@@ -1167,30 +769,6 @@
"node": ">=20.0.0"
}
},
- "lambdas/update-letter-queue/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/update-letter-queue/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"lambdas/update-letter-queue/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -1208,33 +786,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "lambdas/update-letter-queue/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/update-letter-queue/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"lambdas/upsert-letter": {
"name": "nhs-notify-supplier-api-upsert-letter",
"version": "0.0.1",
@@ -1295,79 +846,24 @@
}
},
"lambdas/upsert-letter/node_modules/@aws-lambda-powertools/jmespath": {
- "version": "2.35.0",
- "license": "MIT-0",
- "dependencies": {
- "@aws-lambda-powertools/commons": "2.35.0"
- }
- },
- "lambdas/upsert-letter/node_modules/@aws-sdk/client-dynamodb": {
- "version": "3.1145.0",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@aws-sdk/credential-provider-node": "^3.972.84",
- "@aws-sdk/dynamodb-codec": "^3.973.46",
- "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/fetch-http-handler": "^5.8.0",
- "@smithy/node-http-handler": "^4.12.1",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/upsert-letter/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/upsert-letter/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "lambdas/upsert-letter/node_modules/@aws-sdk/lib-dynamodb": {
- "version": "3.1145.0",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@aws-sdk/util-dynamodb": "^3.996.9",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1145.0"
+ "version": "2.35.0",
+ "license": "MIT-0",
+ "dependencies": {
+ "@aws-lambda-powertools/commons": "2.35.0"
}
},
- "lambdas/upsert-letter/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
+ "lambdas/upsert-letter/node_modules/@aws-sdk/client-dynamodb": {
+ "version": "3.1145.0",
"license": "Apache-2.0",
"dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/credential-provider-node": "^3.972.84",
+ "@aws-sdk/dynamodb-codec": "^3.973.46",
+ "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
"@aws-sdk/types": "^3.974.6",
"@smithy/core": "^3.35.0",
+ "@smithy/fetch-http-handler": "^5.8.0",
+ "@smithy/node-http-handler": "^4.12.1",
"@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
@@ -1375,17 +871,21 @@
"node": ">=20.0.0"
}
},
- "lambdas/upsert-letter/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
+ "lambdas/upsert-letter/node_modules/@aws-sdk/lib-dynamodb": {
+ "version": "3.1145.0",
"license": "Apache-2.0",
"dependencies": {
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/util-dynamodb": "^3.996.9",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
"tslib": "^2.6.2"
},
"engines": {
"node": ">=20.0.0"
},
"peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
+ "@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
"lambdas/upsert-letter/node_modules/@aws/lambda-invoke-store": {
@@ -1536,6 +1036,27 @@
"node": ">=20.0.0"
}
},
+ "node_modules/@aws-sdk/client-dynamodb": {
+ "version": "3.1147.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1147.0.tgz",
+ "integrity": "sha512-lONixsTyj2jt5bb14ir4VYuBEy8kMNMZt7DSVxZ4HHE89hEfND5lO4Qj+F3Z74sENdFCaSAgK8RYQaiQWm5pfw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/credential-provider-node": "^3.972.84",
+ "@aws-sdk/dynamodb-codec": "^3.973.46",
+ "@aws-sdk/middleware-endpoint-discovery": "^3.972.31",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/fetch-http-handler": "^5.8.0",
+ "@smithy/node-http-handler": "^4.12.1",
+ "@smithy/types": "^4.19.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@aws-sdk/client-kinesis": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -1780,6 +1301,69 @@
"node": ">=20.0.0"
}
},
+ "node_modules/@aws-sdk/dynamodb-codec": {
+ "version": "3.973.46",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.46.tgz",
+ "integrity": "sha512-ApnSubn+5C6HQGP7025q9w/YiNzKwimIMksP76xVij779BCCqcmJ9poB6hQ4syY9NUxqmEQLBqY0PktX53L8iQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.978.1",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/endpoint-cache": {
+ "version": "3.972.11",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.972.11.tgz",
+ "integrity": "sha512-8q1ICxcDjHId3bBryuu/j+1L9y5/3uQnwzLDt5j2ElcjZSoWmFtymdJy7OjLrluSMe0Z4mq5bcH4fxBXvlEHfw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "mnemonist": "0.38.3",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/@aws-sdk/lib-dynamodb": {
+ "version": "3.1147.0",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.1147.0.tgz",
+ "integrity": "sha512-232+7ND08nb0YZrCyCviYSpySiYDusCZo8mdovBo9eOHDqBvndiP7n5/CrtN5HjegGuwJYQ/0AedWu1ORVUGDA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/core": "^3.978.1",
+ "@aws-sdk/util-dynamodb": "^3.996.9",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-dynamodb": "^3.1147.0"
+ }
+ },
+ "node_modules/@aws-sdk/middleware-endpoint-discovery": {
+ "version": "3.972.31",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.972.31.tgz",
+ "integrity": "sha512-JqOIXupzP6cpAmCCmuEaacbXo/bVCcg1NkglCJC6q4wDAvJO09qIs7POf50JNzmhOhWPtfXvzgWxb5nMQl8QiA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@aws-sdk/endpoint-cache": "^3.972.11",
+ "@aws-sdk/types": "^3.974.6",
+ "@smithy/core": "^3.35.0",
+ "@smithy/types": "^4.19.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
"node_modules/@aws-sdk/middleware-sdk-api-gateway": {
"version": "3.972.30",
"license": "Apache-2.0",
@@ -1879,6 +1463,21 @@
"node": ">=20.0.0"
}
},
+ "node_modules/@aws-sdk/util-dynamodb": {
+ "version": "3.996.9",
+ "resolved": "https://registry.npmjs.org/@aws-sdk/util-dynamodb/-/util-dynamodb-3.996.9.tgz",
+ "integrity": "sha512-16x2tRvl7OYpZ0W/DdFJieFriD13+RvuRBDbe5sj/tCEfK86HSGd7I2s5j0ivz8p6KWGkS+5wKRO9OliJkjUOQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@aws-sdk/client-dynamodb": "^3.1111.0"
+ }
+ },
"node_modules/@aws-sdk/xml-builder": {
"version": "3.972.41",
"license": "Apache-2.0",
@@ -12379,6 +11978,8 @@
},
"node_modules/mnemonist": {
"version": "0.38.3",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz",
+ "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==",
"license": "MIT",
"dependencies": {
"obliterator": "^1.6.1"
@@ -12537,6 +12138,18 @@
"resolved": "lambdas/mi-updates-transformer",
"link": true
},
+ "node_modules/nhs-notify-supplier-api-populate-sameday-breaches": {
+ "resolved": "lambdas/letter-sla-breaches/populate-sameday-breaches",
+ "link": true
+ },
+ "node_modules/nhs-notify-supplier-api-populate-standard-breaches": {
+ "resolved": "lambdas/letter-sla-breaches/populate-standard-breaches",
+ "link": true
+ },
+ "node_modules/nhs-notify-supplier-api-populate-supplier-config-cache": {
+ "resolved": "lambdas/letter-sla-breaches/populate-supplier-config-cache",
+ "link": true
+ },
"node_modules/nhs-notify-supplier-api-supplier-config-ingress": {
"resolved": "lambdas/supplier-config-ingress",
"link": true
@@ -12818,6 +12431,8 @@
},
"node_modules/obliterator": {
"version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz",
+ "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==",
"license": "MIT"
},
"node_modules/obug": {
@@ -16760,30 +16375,6 @@
"node": ">=20.0.0"
}
},
- "scripts/utilities/letter-test-data/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "scripts/utilities/letter-test-data/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"scripts/utilities/letter-test-data/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -16801,33 +16392,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "scripts/utilities/letter-test-data/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "scripts/utilities/letter-test-data/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"scripts/utilities/letter-test-data/node_modules/ansi-regex": {
"version": "6.4.0",
"license": "MIT",
@@ -16989,30 +16553,6 @@
"node": ">=20.0.0"
}
},
- "scripts/utilities/supplier-data/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "scripts/utilities/supplier-data/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"scripts/utilities/supplier-data/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -17030,33 +16570,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "scripts/utilities/supplier-data/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "scripts/utilities/supplier-data/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"scripts/utilities/supplier-data/node_modules/ansi-regex": {
"version": "6.4.0",
"license": "MIT",
@@ -17274,30 +16787,6 @@
"node": ">=20.0.0"
}
},
- "tests/node_modules/@aws-sdk/dynamodb-codec": {
- "version": "3.973.46",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/core": "^3.978.1",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
- "tests/node_modules/@aws-sdk/endpoint-cache": {
- "version": "3.972.11",
- "license": "Apache-2.0",
- "dependencies": {
- "mnemonist": "0.38.3",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"tests/node_modules/@aws-sdk/lib-dynamodb": {
"version": "3.1145.0",
"license": "Apache-2.0",
@@ -17315,20 +16804,6 @@
"@aws-sdk/client-dynamodb": "^3.1145.0"
}
},
- "tests/node_modules/@aws-sdk/middleware-endpoint-discovery": {
- "version": "3.972.31",
- "license": "Apache-2.0",
- "dependencies": {
- "@aws-sdk/endpoint-cache": "^3.972.11",
- "@aws-sdk/types": "^3.974.6",
- "@smithy/core": "^3.35.0",
- "@smithy/types": "^4.19.0",
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- }
- },
"tests/node_modules/@aws-sdk/middleware-sdk-sqs": {
"version": "3.972.43",
"license": "Apache-2.0",
@@ -17342,19 +16817,6 @@
"node": ">=20.0.0"
}
},
- "tests/node_modules/@aws-sdk/util-dynamodb": {
- "version": "3.996.9",
- "license": "Apache-2.0",
- "dependencies": {
- "tslib": "^2.6.2"
- },
- "engines": {
- "node": ">=20.0.0"
- },
- "peerDependencies": {
- "@aws-sdk/client-dynamodb": "^3.1111.0"
- }
- },
"tests/node_modules/@babel/code-frame": {
"version": "8.0.6",
"dev": true,
diff --git a/package.json b/package.json
index 4e7f0e70d..d6e0a3e9b 100644
--- a/package.json
+++ b/package.json
@@ -149,7 +149,7 @@
"docs",
"internal/*",
"lambdas/*",
- "pact-contracts",
+ "lambdas/letter-sla-breaches/*", "pact-contracts",
"scripts/utilities/*",
"tests",
"tests/contracts/*"