Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
2 changes: 2 additions & 0 deletions infrastructure/terraform/components/api/ddb_table_letters.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
resource "aws_dynamodb_table" "letters" {
name = "${local.csi}-letters"
billing_mode = "PAY_PER_REQUEST"
stream_enabled = true
stream_view_type = "NEW_AND_OLD_IMAGES"
Comment thread
stevebux marked this conversation as resolved.
Outdated

hash_key = "supplierId"
range_key = "id"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "aws_lambda_event_source_mapping" "letter_updates_transformer_kinesis" {
event_source_arn = aws_kinesis_stream.letter_change_stream.arn
function_name = module.letter_updates_transformer.function_arn
starting_position = "LATEST"
batch_size = 10
maximum_batching_window_in_seconds = 1

depends_on = [
module.letter_updates_transformer # ensures updates transformer exists
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resource "aws_kinesis_stream" "letter_change_stream" {
name = "${local.csi}-letter-change-stream"
shard_count = 1
retention_period = 24
}

resource "aws_dynamodb_kinesis_streaming_destination" "letter_streaming_destination" {
stream_arn = aws_kinesis_stream.letter_change_stream.arn
table_name = aws_dynamodb_table.letters.name
approximate_creation_date_time_precision = "MILLISECOND"
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,39 @@ module "letter_updates_transformer" {
log_subscription_role_arn = local.acct.log_subscription_role_arn

lambda_env_vars = merge(local.common_lambda_env_vars, {
EVENTPUB_SNS_TOPIC_ARN = module.eventpub.sns_topic.arn
EVENTPUB_SNS_TOPIC_ARN = "${module.eventpub.sns_topic.arn}"
})
}

data "aws_iam_policy_document" "letter_updates_transformer_lambda" {
statement {
sid = "KMSPermissions"
sid = "AllowSNSPublish"
effect = "Allow"

actions = [
"kms:Decrypt",
"kms:GenerateDataKey",
"sns:Publish"
]

resources = [
module.kms.key_arn,
module.eventpub.sns_topic.arn
]
}

statement {
sid = "AllowSNSPublish"
sid = "AllowKinesisGet"
effect = "Allow"

actions = [
"sns:Publish"
"kinesis:GetRecords",
"kinesis:GetShardIterator",
"kinesis:DescribeStream",
"kinesis:DescribeStreamSummary",
"kinesis:ListShards",
"kinesis:ListStreams",
]

resources = [
module.eventpub.sns_topic.arn
aws_kinesis_stream.letter_change_stream.arn
]
}
}
2 changes: 1 addition & 1 deletion internal/events/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@
"typecheck": "tsc --noEmit"
},
"types": "dist/index.d.ts",
"version": "1.0.3"
"version": "1.0.4"
}
6 changes: 6 additions & 0 deletions internal/events/src/domain/letter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ The identifier will be included as the origin domain in the subject of any corre
examples: ["1y3q9v1zzzz"],
}),

supplierId: z.string().meta({
title: "Supplier ID",
description: "Supplier ID allocated to the letter during creation.",
examples: ["supplier-1"],
}),

groupId: z.string().meta({
title: "Group ID",
description:
Expand Down
10 changes: 10 additions & 0 deletions internal/events/src/events/__tests__/event-envelope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ describe("EventEnvelope schema validation", () => {
const baseValidEnvelope: Envelope = {
dataschema:
"https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.READ.1.0.0.schema.json",
dataschemaversion: "1.0.0",
specversion: "1.0",
id: "6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111",
source: "/data-plane/supplier-api/ordering",
subject: "order/769acdd4",
type: "uk.nhs.notify.supplier-api.order.READ.v1",
plane: "data",
time: "2025-10-01T10:15:30.000Z",
datacontenttype: "application/json",
data: {
"notify-payload": {
"notify-data": { nhsNumber: "9434765919" },
Expand Down Expand Up @@ -241,10 +244,13 @@ describe("EventEnvelope schema validation", () => {
specversion: "1.0" as const,
id: "6f1c2a53-3d54-4a0a-9a0b-0e02b2c3d479",
type: "uk.nhs.notify.supplier-api.letter.CREATED.v1" as const,
plane: "data",
dataschema:
"https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.CREATED.1.0.0.schema.json",
dataschemaversion: "1.0.0",
source: "/data-plane/supplier-api/letters",
time: "2025-10-01T10:15:30.000Z",
datacontenttype: "application/json",
data: { status: "CREATED" },
traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
recordedtime: "2025-10-01T10:15:30.250Z",
Expand Down Expand Up @@ -294,11 +300,14 @@ describe("EventEnvelope schema validation", () => {
specversion: "1.0" as const,
id: "6f1c2a53-3d54-4a0a-9a0b-0e9ae2d4c111",
type: "uk.nhs.notify.supplier-api.order.READ.v1" as const,
plane: "data",
dataschema:
"https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.READ.1.0.0.schema.json",
dataschemaversion: "1.0.0",
source: "/data-plane/supplier-api/ordering",
subject: "order/769acdd4",
time: "2025-10-01T10:15:30.000Z",
datacontenttype: "application/json",
data: { status: "READ" },
traceparent: "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01",
recordedtime: "2025-10-01T10:15:30.250Z",
Expand Down Expand Up @@ -341,6 +350,7 @@ describe("EventEnvelope schema validation", () => {
type: "uk.nhs.notify.supplier-api.order.read.v1" as const,
dataschema:
"https://notify.nhs.uk/cloudevents/schemas/supplier-api/order.read.1.0.0.schema.json",
dataschemaversion: "1.0.0",
source: "/data-plane/supplier-api/ordering",
subject: "prefix/letter-rendering/order/769acdd4",
time: "2025-10-01T10:15:30.000Z",
Expand Down
1 change: 1 addition & 0 deletions internal/events/src/events/__tests__/mi-events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("MI event validations", () => {
expect(event).toEqual(
expect.objectContaining({
type: "uk.nhs.notify.supplier-api.mi.SUBMITTED.v1",
plane: "data",
specversion: "1.0",
source: "/data-plane/supplier-api/prod/submit-mi",
id: "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
"subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5"
},
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED"
"status": "ACCEPTED",
"supplierId": "supplier1"
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.0.1.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "23f1f09c-a555-4d9b-8405-0b33490bc920",
"plane": "data",
"recordedtime": "2025-08-28T08:45:00.000Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@
"source": "/data-plane/letter-rendering/prod/render-pdf"
},
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED"
"status": "ACCEPTED",
"supplierId": "supplier1"
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "23f1f09c-a555-4d9b-8405-0b33490bc920",
"plane": "data",
"recordedtime": "2025-08-28T08:45:00.000Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
"subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5"
},
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED"
Comment thread
m-houston marked this conversation as resolved.
"status": "ACCEPTED",
"supplierId": "supplier1"
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.ACCEPTED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "23f1f09c-a555-4d9b-8405-0b33490bc920",
"plane": "data",
"recordedtime": "2025-08-28T08:45:00.000Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
"reasonCode": "RNIB",
"reasonText": "RNIB",
"specificationId": "1y3q9v1zzzz",
"status": "FORWARDED"
"status": "FORWARDED",
"supplierId": "supplier1"
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.FORWARDED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "23f1f09c-a555-4d9b-8405-0b33490bc920",
"plane": "data",
"recordedtime": "2025-08-28T08:45:00.000Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
"reasonCode": "R07",
"reasonText": "No such address",
"specificationId": "1y3q9v1zzzz",
"status": "RETURNED"
"status": "RETURNED",
"supplierId": "supplier1"
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/letter.RETURNED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "23f1f09c-a555-4d9b-8405-0b33490bc920",
"plane": "data",
"recordedtime": "2025-08-28T08:45:00.000Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "9a3d4c55-5f76-6c2c-b789-2f1cf4e5e333",
"plane": "data",
"recordedtime": "2025-11-16T15:00:00.250Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.2.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222",
"plane": "data",
"recordedtime": "2025-11-16T10:30:00.250Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222",
"plane": "data",
"recordedtime": "2025-11-16T10:30:00.250Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
},
"datacontenttype": "application/json",
"dataschema": "https://notify.nhs.uk/cloudevents/schemas/supplier-api/mi.SUBMITTED.1.0.0.schema.json",
"dataschemaversion": "1.0.0",
"id": "8f2c3b44-4e65-5b1b-a678-1f0bf3d4d222",
"plane": "data",
"recordedtime": "2025-11-16T10:30:00.250Z",
"severitynumber": 2,
"severitytext": "INFO",
Expand Down
29 changes: 21 additions & 8 deletions internal/events/src/events/event-envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
examples: typeStrings,
}),

plane: z.literal("data").meta({
title: "plane",
description: "The event bus that this event will be published to",
examples: ["data"],
}),

dataschema: z
.string()
.regex(
Expand All @@ -64,6 +70,15 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
examples: schemaExamples,
}),

dataschemaversion: z
.string()
.regex(/^1\.\d+\.\d+$/)
.meta({
title: "Data Schema URI",
description: `Version of the schema that describes the event data\n\nMust match the version in dataschema`,
examples: ["1.0.0"],
}),

source: z
.string()
.regex(/^\/data-plane\/supplier-api(?:\/.*)?$/)
Expand Down Expand Up @@ -94,14 +109,12 @@ export function EventEnvelope<TData extends z.ZodTypeAny>(
examples: ["2025-10-01T10:15:30.000Z"],
}),

datacontenttype: z.optional(
z.literal("application/json").meta({
title: "Data Content Type",
description:
"Media type for the data field (fixed to application/json).",
examples: ["application/json"],
}),
),
datacontenttype: z.literal("application/json").meta({
title: "Data Content Type",
description:
"Media type for the data field (fixed to application/json).",
examples: ["application/json"],
}),

traceparent: z
.string()
Expand Down
6 changes: 3 additions & 3 deletions internal/events/src/events/letter-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const $LetterEvent = EventEnvelope(
"letter",
"letter",
$Letter,
$LetterStatus.options,
$LetterStatus.options.map((status) => status.toLowerCase()),
Comment thread
m-houston marked this conversation as resolved.
Outdated
"letter-origin",
).meta({
title: `letter.* Event`,
Expand All @@ -27,13 +27,13 @@ export type LetterEvent = z.infer<typeof $LetterEvent>;
*/
const eventSchema = (status: LetterStatus) =>
EventEnvelope(
`letter.${status}`,
`letter.${status.toLowerCase()}`,
"letter",
$Letter,
[status],
"letter-origin",
).meta({
title: `letter.${status} Event`,
title: `letter.${status.toLowerCase()} Event`,
description: `Event schema for letter status change to ${status}`,
});

Expand Down
11 changes: 9 additions & 2 deletions lambdas/letter-updates-transformer/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
{
"dependencies": {
"@types/aws-lambda": "^8.10.148",
"esbuild": "^0.24.0"
"@aws-sdk/client-sns": "^3.943.0",
"@aws-sdk/util-dynamodb": "^3.943.0",
"@internal/datastore": "^0.1.0",
"@internal/helpers": "^0.1.0",
"@nhsdigital/nhs-notify-event-schemas-supplier-api": "*",
"aws-lambda": "^1.0.7",
"esbuild": "^0.24.0",
"pino": "^10.1.0",
"zod": "^4.1.13"
},
"devDependencies": {
"@tsconfig/node22": "^22.0.2",
Expand Down
17 changes: 0 additions & 17 deletions lambdas/letter-updates-transformer/src/__tests__/index.test.ts

This file was deleted.

Loading
Loading