Skip to content

Commit df10891

Browse files
committed
CCM-12937 Letter updates transformer lambda
1 parent 089b325 commit df10891

28 files changed

+2484
-364
lines changed

infrastructure/terraform/components/api/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ No requirements.
4545
| <a name="module_kms"></a> [kms](#module\_kms) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-kms.zip | n/a |
4646
| <a name="module_letter_status_update"></a> [letter\_status\_update](#module\_letter\_status\_update) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-lambda.zip | n/a |
4747
| <a name="module_letter_status_updates_queue"></a> [letter\_status\_updates\_queue](#module\_letter\_status\_updates\_queue) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.24/terraform-sqs.zip | n/a |
48+
| <a name="module_letter_stream_forwarder"></a> [letter\_stream\_forwarder](#module\_letter\_stream\_forwarder) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a |
4849
| <a name="module_letter_updates_transformer"></a> [letter\_updates\_transformer](#module\_letter\_updates\_transformer) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a |
4950
| <a name="module_logging_bucket"></a> [logging\_bucket](#module\_logging\_bucket) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-s3bucket.zip | n/a |
5051
| <a name="module_patch_letter"></a> [patch\_letter](#module\_patch\_letter) | https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip | n/a |

infrastructure/terraform/components/api/ddb_table_letters.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
resource "aws_dynamodb_table" "letters" {
22
name = "${local.csi}-letters"
33
billing_mode = "PAY_PER_REQUEST"
4+
stream_enabled = true
5+
stream_view_type = "NEW_AND_OLD_IMAGES"
46

57
hash_key = "supplierId"
68
range_key = "id"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
resource "aws_lambda_event_source_mapping" "letter_stream_forwarder_dynamodb" {
2+
event_source_arn = aws_dynamodb_table.letters.stream_arn
3+
function_name = module.letter_stream_forwarder.function_arn
4+
starting_position = "LATEST"
5+
batch_size = 10
6+
maximum_batching_window_in_seconds = 1
7+
8+
depends_on = [
9+
module.letter_stream_forwarder # ensures stream forwarder exists
10+
]
11+
}
12+
13+
resource "aws_lambda_event_source_mapping" "letter_updates_transformer_kinesis" {
14+
event_source_arn = aws_kinesis_stream.letter_change_stream.arn
15+
function_name = module.letter_updates_transformer.function_arn
16+
starting_position = "LATEST"
17+
batch_size = 10
18+
maximum_batching_window_in_seconds = 1
19+
20+
depends_on = [
21+
module.letter_updates_transformer # ensures updates transformer exists
22+
]
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "aws_kinesis_stream" "letter_change_stream" {
2+
name = "${local.csi}-letter-change-stream"
3+
shard_count = 1
4+
retention_period = 24
5+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
module "letter_stream_forwarder" {
2+
source = "https://github.com/NHSDigital/nhs-notify-shared-modules/releases/download/v2.0.26/terraform-lambda.zip"
3+
4+
function_name = "letter-stream-forwarder"
5+
description = "Kinesis stream forwarder for DDB letter status updates"
6+
7+
aws_account_id = var.aws_account_id
8+
component = var.component
9+
environment = var.environment
10+
project = var.project
11+
region = var.region
12+
group = var.group
13+
14+
log_retention_in_days = var.log_retention_in_days
15+
kms_key_arn = module.kms.key_arn
16+
17+
iam_policy_document = {
18+
body = data.aws_iam_policy_document.letter_stream_forwarder_lambda.json
19+
}
20+
21+
function_s3_bucket = local.acct.s3_buckets["lambda_function_artefacts"]["id"]
22+
function_code_base_path = local.aws_lambda_functions_dir_path
23+
function_code_dir = "letter-stream-forwarder/dist"
24+
function_include_common = true
25+
handler_function_name = "handler"
26+
runtime = "nodejs22.x"
27+
memory = 128
28+
timeout = 5
29+
log_level = var.log_level
30+
31+
force_lambda_code_deploy = var.force_lambda_code_deploy
32+
enable_lambda_insights = false
33+
34+
send_to_firehose = true
35+
log_destination_arn = local.destination_arn
36+
log_subscription_role_arn = local.acct.log_subscription_role_arn
37+
38+
lambda_env_vars = merge(local.common_lambda_env_vars, {
39+
LETTER_CHANGE_STREAM_ARN = "${aws_kinesis_stream.letter_change_stream.arn}"
40+
})
41+
}
42+
43+
data "aws_iam_policy_document" "letter_stream_forwarder_lambda" {
44+
45+
statement {
46+
sid = "AllowDynamoDBStream"
47+
effect = "Allow"
48+
49+
actions = [
50+
"dynamodb:GetRecords",
51+
"dynamodb:GetShardIterator",
52+
"dynamodb:DescribeStream",
53+
"dynamodb:ListStreams",
54+
]
55+
56+
resources = [
57+
"${aws_dynamodb_table.letters.arn}/stream/*"
58+
]
59+
}
60+
61+
statement {
62+
sid = "AllowKinesisPut"
63+
effect = "Allow"
64+
65+
actions = [
66+
"kinesis:DescribeStream",
67+
"kinesis:PutRecord",
68+
]
69+
70+
resources = [
71+
aws_kinesis_stream.letter_change_stream.arn
72+
]
73+
}
74+
}

infrastructure/terraform/components/api/module_lambda_letter_updates_transformer.tf

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,39 @@ module "letter_updates_transformer" {
3636
log_subscription_role_arn = local.acct.log_subscription_role_arn
3737

3838
lambda_env_vars = merge(local.common_lambda_env_vars, {
39-
EVENTPUB_SNS_TOPIC_ARN = module.eventpub.sns_topic.arn
39+
EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}"
4040
})
4141
}
4242

4343
data "aws_iam_policy_document" "letter_updates_transformer_lambda" {
4444
statement {
45-
sid = "KMSPermissions"
45+
sid = "AllowSNSPublish"
4646
effect = "Allow"
4747

4848
actions = [
49-
"kms:Decrypt",
50-
"kms:GenerateDataKey",
49+
"sns:Publish"
5150
]
5251

5352
resources = [
54-
module.kms.key_arn,
53+
module.eventpub.sns_topic.arn
5554
]
5655
}
5756

5857
statement {
59-
sid = "AllowSNSPublish"
58+
sid = "AllowKinesisGet"
6059
effect = "Allow"
6160

6261
actions = [
63-
"sns:Publish"
62+
"kinesis:GetRecords",
63+
"kinesis:GetShardIterator",
64+
"kinesis:DescribeStream",
65+
"kinesis:DescribeStreamSummary",
66+
"kinesis:ListShards",
67+
"kinesis:ListStreams",
6468
]
6569

6670
resources = [
67-
module.eventpub.sns_topic.arn
71+
aws_kinesis_stream.letter_change_stream.arn
6872
]
6973
}
7074
}

internal/events/src/events/event-envelope.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
5050
examples: typeStrings,
5151
}),
5252

53+
plane: z.literal("data-plane").meta({
54+
title: "plane",
55+
description: "Fixed as data-plane",
56+
examples: ["data-plane"],
57+
}),
58+
5359
dataschema: z
5460
.string()
5561
.regex(
@@ -64,6 +70,15 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
6470
examples: schemaExamples,
6571
}),
6672

73+
dataschemaversion: z
74+
.string()
75+
.regex(/^1.\d+.\d+$/)
76+
.meta({
77+
title: "Data Schema URI",
78+
description: `Version of the schema that describes the event data\n\nMust match the version in dataschema`,
79+
examples:["1.0.0"],
80+
}),
81+
6782
source: z
6883
.string()
6984
.regex(/^\/data-plane\/supplier-api(?:\/.*)?$/)
@@ -94,13 +109,13 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
94109
examples: ["2025-10-01T10:15:30.000Z"],
95110
}),
96111

97-
datacontenttype: z.optional(
112+
datacontenttype:
98113
z.literal("application/json").meta({
99114
title: "Data Content Type",
100115
description:
101116
"Media type for the data field (fixed to application/json).",
102117
examples: ["application/json"],
103-
}),
118+
},
104119
),
105120

106121
traceparent: z
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
coverage
2+
node_modules
3+
dist
4+
.reports
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { Config } from 'jest';
2+
3+
export const baseJestConfig: Config = {
4+
preset: 'ts-jest',
5+
6+
// Automatically clear mock calls, instances, contexts and results before every test
7+
clearMocks: true,
8+
9+
// Indicates whether the coverage information should be collected while executing the test
10+
collectCoverage: true,
11+
12+
// The directory where Jest should output its coverage files
13+
coverageDirectory: './.reports/unit/coverage',
14+
15+
// Indicates which provider should be used to instrument code for coverage
16+
coverageProvider: 'babel',
17+
18+
coverageThreshold: {
19+
global: {
20+
branches: 100,
21+
functions: 100,
22+
lines: 100,
23+
statements: -10,
24+
},
25+
},
26+
27+
coveragePathIgnorePatterns: ['/__tests__/'],
28+
transform: { '^.+\\.ts$': 'ts-jest' },
29+
testPathIgnorePatterns: ['.build'],
30+
testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
31+
32+
// Use this configuration option to add custom reporters to Jest
33+
reporters: [
34+
'default',
35+
[
36+
'jest-html-reporter',
37+
{
38+
pageTitle: 'Test Report',
39+
outputPath: './.reports/unit/test-report.html',
40+
includeFailureMsg: true,
41+
},
42+
],
43+
],
44+
45+
// The test environment that will be used for testing
46+
testEnvironment: 'jsdom',
47+
};
48+
49+
const utilsJestConfig = {
50+
...baseJestConfig,
51+
52+
testEnvironment: 'node',
53+
54+
coveragePathIgnorePatterns: [
55+
...(baseJestConfig.coveragePathIgnorePatterns ?? []),
56+
'zod-validators.ts',
57+
],
58+
};
59+
60+
export default utilsJestConfig;

0 commit comments

Comments
 (0)