Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
03a813f
add state machine, popluate supplier config, and dummy lambdas
RossFaulds171 Oct 8, 2026
729b9e2
add glue definitions
RossFaulds171 Oct 12, 2026
2159b29
run SLA lambdas in parallel
RossFaulds171 Oct 12, 2026
c51e94c
add logging to config cache lambda
RossFaulds171 Oct 12, 2026
a52a1eb
fix lint issues
RossFaulds171 Oct 12, 2026
12dae16
amend glue config
RossFaulds171 Oct 12, 2026
acb9ce2
amend return information from populate-supplier-config-cache
RossFaulds171 Oct 12, 2026
41bfa40
alphabetise entity types
RossFaulds171 Oct 12, 2026
a70b984
fix failing test
RossFaulds171 Oct 12, 2026
5fb19f5
CCM-24327: parquet catalog (#734)
masl2 Oct 13, 2026
1010ed9
follow zod schema pattern for env variables in breach config lambda
RossFaulds171 Oct 14, 2026
23ced32
retrieve entity type from internal datastore
RossFaulds171 Oct 14, 2026
e43b854
remove entities from return of config cache lambda
RossFaulds171 Oct 14, 2026
027236f
missing newlines and terraform restructuring
RossFaulds171 Oct 14, 2026
94f18f1
fix lint issue and change S3 bucket used for config cache
RossFaulds171 Oct 14, 2026
f0146df
promote status
RossFaulds171 Oct 14, 2026
fcb95e2
remove unnecessary export
RossFaulds171 Oct 14, 2026
b862888
Create common package
stevebux Oct 14, 2026
89c4dda
add query and flesh out lambda to produce count
RossFaulds171 Oct 15, 2026
d5dd597
fix package-lock.json
RossFaulds171 Oct 15, 2026
2846a0a
setup environment variables
RossFaulds171 Oct 15, 2026
b16c359
Merge branch 'main' into origin/feature/CCM-24327-sameday-letter-brea…
RossFaulds171 Oct 15, 2026
e74a283
fix predicate and sameday parameter
RossFaulds171 Oct 15, 2026
88ef827
fix breach_count error in both breach lambdas
RossFaulds171 Oct 15, 2026
77d773f
use passed in date in breach lambdas
RossFaulds171 Oct 15, 2026
39dbb99
change transformer to extract clientid and campaignid from groupid
RossFaulds171 Oct 16, 2026
c7ad32c
Merge branch 'main' into origin/feature/CCM-24327-sameday-letter-brea…
RossFaulds171 Oct 16, 2026
775394b
fix broken terraform
RossFaulds171 Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/generate-includes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ cp ./lambdas/upsert-letter/README.md ./docs/_includes/components/generated/readm
cp ./lambdas/update-letter-queue/README.md ./docs/_includes/components/generated/readmes/lambda-update-letter-queue.md
cp ./lambdas/letter-updates-transformer/README.md ./docs/_includes/components/generated/readmes/lambda-letter-updates-transformer.md
cp ./lambdas/mi-updates-transformer/README.md ./docs/_includes/components/generated/readmes/lambda-mi-updates-transformer.md
cp ./lambdas/eventpub-parquet-transformer/README.md ./docs/_includes/components/generated/readmes/lambda-eventpub-parquet-transformer.md
cp ./lambdas/supplier-config-ingress/README.md ./docs/_includes/components/generated/readmes/lambda-supplier-config-ingress.md

# Function documentation (internal packages)
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export default defineConfig([
eslintImportResolverTypescript.createTypeScriptImportResolver({
project: [
'lambdas/*/tsconfig.json',
'lambdas/*/*/tsconfig.json',
'tests/tsconfig.json',
'internal/*/tsconfig.json',
],
Expand Down
7 changes: 7 additions & 0 deletions infrastructure/terraform/components/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ No requirements.
| <a name="input_parent_acct_environment"></a> [parent\_acct\_environment](#input\_parent\_acct\_environment) | Name of the environment responsible for the acct resources used, affects things like DNS zone. Useful for named dev environments | `string` | `"main"` | no |
| <a name="input_project"></a> [project](#input\_project) | The name of the tfscaffold project | `string` | n/a | yes |
| <a name="input_region"></a> [region](#input\_region) | The AWS Region | `string` | n/a | yes |
| <a name="input_reports_data_retention_non_current_days"></a> [reports\_data\_retention\_non\_current\_days](#input\_reports\_data\_retention\_non\_current\_days) | Number of days to retain noncurrent reporting bucket object versions | `number` | `14` | no |
| <a name="input_reports_data_retention_policy_days"></a> [reports\_data\_retention\_policy\_days](#input\_reports\_data\_retention\_policy\_days) | Number of days to retain reporting bucket data | `number` | `90` | no |
| <a name="input_shared_infra_account_id"></a> [shared\_infra\_account\_id](#input\_shared\_infra\_account\_id) | The AWS Account ID of the shared infrastructure account | `string` | `"000000000000"` | no |
| <a name="input_sns_success_logging_sample_percent"></a> [sns\_success\_logging\_sample\_percent](#input\_sns\_success\_logging\_sample\_percent) | Enable SNS Delivery Successful Sample Percentage | `number` | `0` | no |
| <a name="input_supplier_mock_schedule_expression"></a> [supplier\_mock\_schedule\_expression](#input\_supplier\_mock\_schedule\_expression) | Schedule expression for supplier mock scheduler | `string` | `"rate(1 minute)"` | no |
Expand All @@ -71,6 +73,7 @@ No requirements.
| <a name="module_ddb_alarms_suppliers"></a> [ddb\_alarms\_suppliers](#module\_ddb\_alarms\_suppliers) | ../../modules/alarms-ddb | n/a |
| <a name="module_domain_truststore"></a> [domain\_truststore](#module\_domain\_truststore) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/3.0.6/terraform-s3bucket.zip | n/a |
| <a name="module_eventpub"></a> [eventpub](#module\_eventpub) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/5.0.6/terraform-eventpub.zip | n/a |
| <a name="module_eventpub_parquet_transformer"></a> [eventpub\_parquet\_transformer](#module\_eventpub\_parquet\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| <a name="module_eventsub"></a> [eventsub](#module\_eventsub) | ../../modules/eventsub | n/a |
| <a name="module_get_letter"></a> [get\_letter](#module\_get\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| <a name="module_get_letter_data"></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 |
Expand All @@ -83,8 +86,12 @@ No requirements.
| <a name="module_letter_updates_transformer"></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 |
| <a name="module_mi_updates_transformer"></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 |
| <a name="module_patch_letter"></a> [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| <a name="module_populate_sameday_breaches"></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 |
| <a name="module_populate_standard_breaches"></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 |
| <a name="module_populate_supplier_config_cache"></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 |
| <a name="module_post_letters"></a> [post\_letters](#module\_post\_letters) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| <a name="module_post_mi"></a> [post\_mi](#module\_post\_mi) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.5/terraform-lambda.zip | n/a |
| <a name="module_s3bucket_reporting"></a> [s3bucket\_reporting](#module\_s3bucket\_reporting) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/6.0.6/terraform-s3bucket.zip | n/a |
| <a name="module_s3bucket_test_letters"></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 |
| <a name="module_sqs_alarms"></a> [sqs\_alarms](#module\_sqs\_alarms) | ../../modules/alarms-sqs | n/a |
| <a name="module_sqs_letter_updates"></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 |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
resource "aws_athena_workgroup" "reporting" {
name = local.csi
description = "Supplier API Athena Workgroup for ${var.environment}"
force_destroy = true

configuration {
enforce_workgroup_configuration = true

result_configuration {
expected_bucket_owner = var.aws_account_id
output_location = "s3://${module.s3bucket_reporting.bucket}/athena-output/"

encryption_configuration {
encryption_option = "SSE_KMS"
kms_key_arn = module.kms.key_arn
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
resource "aws_cloudwatch_log_group" "eventpub_parquet_firehose" {
count = local.eventpub_parquet_enabled ? 1 : 0

name = "/aws/firehose/${local.csi}-eventpub-parquet"
kms_key_id = module.kms.key_arn
retention_in_days = var.log_retention_in_days
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
resource "aws_cloudwatch_log_stream" "eventpub_parquet_firehose" {
count = local.eventpub_parquet_enabled ? 1 : 0

name = "extended_s3"
log_group_name = aws_cloudwatch_log_group.eventpub_parquet_firehose[0].name
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
resource "aws_glue_catalog_table" "eventpub_parquet" {
count = local.eventpub_parquet_enabled ? 1 : 0
name = "${replace(local.csi, "-", "_")}_eventpub"
database_name = aws_glue_catalog_database.supplier.name
table_type = "EXTERNAL_TABLE"

parameters = {
EXTERNAL = "TRUE"
classification = "parquet"
"parquet.compress" = "SNAPPY"

# Athena derives partitions from these properties, so none are registered in Glue
"projection.enabled" = "true"
"projection.__year.type" = "integer"
"projection.__year.range" = "2025,2050"
"projection.__year.digits" = "4"
"projection.__month.type" = "integer"
"projection.__month.range" = "1,12"
"projection.__month.digits" = "2"
"projection.__day.type" = "integer"
"projection.__day.range" = "1,31"
"projection.__day.digits" = "2"
"storage.location.template" = "s3://${module.s3bucket_reporting.bucket}/parquet/eventpub/__year=$${__year}/__month=$${__month}/__day=$${__day}/"
}

storage_descriptor {
location = "s3://${module.s3bucket_reporting.bucket}/parquet/eventpub/"
input_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat"

ser_de_info {
name = "ParquetHiveSerDe"
serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe"
}

dynamic "columns" {
for_each = local.eventpub_parquet_table_columns

content {
name = columns.value.name
type = columns.value.type
}
}
}

partition_keys {
name = "__year"
type = "string"
}

partition_keys {
name = "__month"
type = "string"
}

partition_keys {
name = "__day"
type = "string"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
locals {
supplier_config_cache_table_columns = {
supplier = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "channelType", type = "string" },
{ name = "dailyCapacity", type = "bigint" },
{ name = "id", type = "string" },
{ name = "name", type = "string" },
{ name = "status", type = "string" },
]
volume-group = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "description", type = "string" },
{ name = "endDate", type = "string" },
{ name = "id", type = "string" },
{ name = "name", type = "string" },
{ name = "startDate", type = "string" },
{ name = "status", type = "string" },
]
supplier-pack = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "approval", type = "string" },
{ name = "id", type = "string" },
{ name = "packSpecificationId", type = "string" },
{ name = "status", type = "string" },
{ name = "supplierId", type = "string" },
]
letter-variant = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "campaignIds", type = "array<string>" },
{ name = "clientId", type = "string" },
{ name = "constraints", type = "struct<blackCoveragePercentage:struct<operator:string,value:double>,colourCoveragePercentage:struct<operator:string,value:double>,deliveryDays:struct<operator:string,value:double>,sheets:struct<operator:string,value:double>,sides:struct<operator:string,value:double>>" },
{ name = "description", type = "string" },
{ name = "id", type = "string" },
{ name = "name", type = "string" },
{ name = "packSpecificationIds", type = "array<string>" },
{ name = "priority", type = "bigint" },
{ name = "status", type = "string" },
{ name = "supplierId", type = "string" },
{ name = "type", type = "string" },
{ name = "volumeGroupId", type = "string" },
]
pack-specification = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "assembly", type = "struct<duplex:boolean,envelopeId:string,features:array<string>,insertIds:array<string>,paper:struct<colour:string,id:string,name:string,recycled:boolean,size:string,weightGSM:double>,printColour:string>" },
{ name = "billingId", type = "string" },
{ name = "constraints", type = "struct<blackCoveragePercentage:struct<operator:string,value:double>,colourCoveragePercentage:struct<operator:string,value:double>,deliveryDays:struct<operator:string,value:double>,sheets:struct<operator:string,value:double>,sides:struct<operator:string,value:double>>" },
{ name = "createdAt", type = "string" },
{ name = "description", type = "string" },
{ name = "id", type = "string" },
{ name = "name", type = "string" },
{ name = "postage", type = "struct<deliveryDays:double,id:string,maxThicknessMm:double,maxWeightGrams:double,size:string>" },
{ name = "status", type = "string" },
{ name = "updatedAt", type = "string" },
{ name = "version", type = "bigint" },
]
supplier-allocation = [
{ name = "pk", type = "string" },
{ name = "sk", type = "string" },
{ name = "allocationPercentage", type = "double" },
{ name = "id", type = "string" },
{ name = "status", type = "string" },
{ name = "supplier", type = "string" },
{ name = "volumeGroup", type = "string" },
]
}
}

resource "aws_glue_catalog_table" "supplier_config_cache" {
for_each = local.supplier_config_cache_table_columns
name = "${replace(local.csi, "-", "_")}_${replace(each.key, "-", "_")}"
database_name = aws_glue_catalog_database.supplier.name
table_type = "EXTERNAL_TABLE"

parameters = {
EXTERNAL = "TRUE"
classification = "json"
}

storage_descriptor {
location = "s3://${module.s3bucket_reporting.bucket}/supplier_config_cache/${each.key}/"
input_format = "org.apache.hadoop.mapred.TextInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat"

ser_de_info {
name = "OpenXJsonSerDe"
serialization_library = "org.openx.data.jsonserde.JsonSerDe"

parameters = {
"case.insensitive" = "false"
}
}

dynamic "columns" {
for_each = each.value

content {
name = columns.value.name
type = columns.value.type
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "aws_iam_role" "eventpub_parquet_firehose" {
count = local.eventpub_parquet_enabled ? 1 : 0

name = "${local.csi}-eventpub-parquet-firehose"
assume_role_policy = data.aws_iam_policy_document.eventpub_parquet_firehose_assume_role.json
}

data "aws_iam_policy_document" "eventpub_parquet_firehose_assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["firehose.amazonaws.com"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
resource "aws_iam_role" "eventpub_parquet_sns" {
count = local.eventpub_parquet_enabled ? 1 : 0

name = "${local.csi}-eventpub-parquet-sns"
assume_role_policy = data.aws_iam_policy_document.eventpub_parquet_sns_assume_role.json
}

data "aws_iam_policy_document" "eventpub_parquet_sns_assume_role" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["sns.amazonaws.com"]
}
}
}
Original file line number Diff line number Diff line change
@@ -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"]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
resource "aws_iam_role_policy" "eventpub_parquet_firehose" {
count = local.eventpub_parquet_enabled ? 1 : 0

name = "${local.csi}-eventpub-parquet-firehose"
role = aws_iam_role.eventpub_parquet_firehose[0].id
policy = data.aws_iam_policy_document.eventpub_parquet_firehose[0].json
}

data "aws_iam_policy_document" "eventpub_parquet_firehose" {
count = local.eventpub_parquet_enabled ? 1 : 0

statement {
sid = "AllowDestinationS3Access"
effect = "Allow"

actions = [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject",
]

resources = [
module.s3bucket_reporting.arn,
"${module.s3bucket_reporting.arn}/parquet/*",
]
}

statement {
sid = "AllowKms"
effect = "Allow"

actions = [
"kms:Decrypt",
"kms:DescribeKey",
"kms:GenerateDataKey*",
]

resources = [module.kms.key_arn]
}

statement {
sid = "AllowInvokeTransformer"
effect = "Allow"

actions = [
"lambda:InvokeFunction",
"lambda:GetFunctionConfiguration",
]

resources = ["${module.eventpub_parquet_transformer.function_arn}:$LATEST"]
}

statement {
sid = "AllowGlueSchemaRead"
effect = "Allow"

actions = [
"glue:GetTable",
"glue:GetTableVersion",
"glue:GetTableVersions",
]

resources = [
aws_glue_catalog_table.eventpub_parquet[0].arn,
aws_glue_catalog_database.supplier.arn,
"arn:aws:glue:${var.region}:${var.aws_account_id}:catalog",
]
}

statement {
sid = "AllowLogging"
effect = "Allow"
actions = ["logs:PutLogEvents"]
resources = ["${aws_cloudwatch_log_group.eventpub_parquet_firehose[0].arn}:*"]
}
}
Loading
Loading