diff --git a/.github/workflows/stage-3-build.yaml b/.github/workflows/stage-3-build.yaml index a8441e7ff..766b9bfdd 100644 --- a/.github/workflows/stage-3-build.yaml +++ b/.github/workflows/stage-3-build.yaml @@ -161,7 +161,8 @@ jobs: --targetAccountGroup "nhs-notify-supplier-api-dev" \ --terraformAction "apply" \ --overrideProjectName "nhs" \ - --overrideRoleName "nhs-main-acct-supplier-api-github-deploy" + --overrideRoleName "nhs-main-acct-supplier-api-github-deploy" \ + --internalRef "feature/CCM-15148" artefact-proxies: name: "Build proxies" runs-on: ubuntu-latest diff --git a/infrastructure/terraform/components/api/README.md b/infrastructure/terraform/components/api/README.md index 46f826ce1..dc0d43b4c 100644 --- a/infrastructure/terraform/components/api/README.md +++ b/infrastructure/terraform/components/api/README.md @@ -35,7 +35,7 @@ No requirements. | [kms\_deletion\_window](#input\_kms\_deletion\_window) | When a kms key is deleted, how long should it wait in the pending deletion state? | `string` | `"30"` | no | | [letter\_event\_source](#input\_letter\_event\_source) | Source value to use for the letter status event updates | `string` | `"/data-plane/supplier-api/nhs-supplier-api-prod/main/update-status"` | no | | [letter\_table\_ttl\_hours](#input\_letter\_table\_ttl\_hours) | Number of hours to set as TTL on letters table | `number` | `24` | no | -| [letter\_variant\_map](#input\_letter\_variant\_map) | n/a | `map(object({ supplierId = string, specId = string, priority = number }))` |
{
"lv1": {
"priority": 10,
"specId": "spec1",
"supplierId": "supplier1"
},
"lv2": {
"priority": 10,
"specId": "spec2",
"supplierId": "supplier1"
},
"lv3": {
"priority": 10,
"specId": "spec3",
"supplierId": "supplier2"
}
} | no |
+| [letter\_variant\_map](#input\_letter\_variant\_map) | n/a | `map(object({ supplierId = string, specId = string, priority = number, billingId = string }))` | {
"lv1": {
"billingId": "billing1",
"priority": 10,
"specId": "spec1",
"supplierId": "supplier1"
},
"lv2": {
"billingId": "billing2",
"priority": 10,
"specId": "spec2",
"supplierId": "supplier1"
},
"lv3": {
"billingId": "billing3",
"priority": 10,
"specId": "spec3",
"supplierId": "supplier2"
}
} | no |
| [log\_level](#input\_log\_level) | The log level to be used in lambda functions within the component. Any log with a lower severity than the configured value will not be logged: https://docs.python.org/3/library/logging.html#levels | `string` | `"INFO"` | no |
| [log\_retention\_in\_days](#input\_log\_retention\_in\_days) | The retention period in days for the Cloudwatch Logs events to be retained, default of 0 is indefinite | `number` | `0` | no |
| [manually\_configure\_mtls\_truststore](#input\_manually\_configure\_mtls\_truststore) | Manually manage the truststore used for API Gateway mTLS (e.g. for prod environment) | `bool` | `false` | no |
diff --git a/infrastructure/terraform/components/api/variables.tf b/infrastructure/terraform/components/api/variables.tf
index 51af73e5d..6442d1e8c 100644
--- a/infrastructure/terraform/components/api/variables.tf
+++ b/infrastructure/terraform/components/api/variables.tf
@@ -136,11 +136,11 @@ variable "eventpub_control_plane_bus_arn" {
}
variable "letter_variant_map" {
- type = map(object({ supplierId = string, specId = string, priority = number }))
+ type = map(object({ supplierId = string, specId = string, priority = number, billingId = string }))
default = {
- "lv1" = { supplierId = "supplier1", specId = "spec1", priority = 10 },
- "lv2" = { supplierId = "supplier1", specId = "spec2", priority = 10 },
- "lv3" = { supplierId = "supplier2", specId = "spec3", priority = 10 }
+ "lv1" = { supplierId = "supplier1", specId = "spec1", priority = 10, billingId = "billing1" },
+ "lv2" = { supplierId = "supplier1", specId = "spec2", priority = 10, billingId = "billing2" },
+ "lv3" = { supplierId = "supplier2", specId = "spec3", priority = 10, billingId = "billing3" }
}
}
diff --git a/internal/datastore/src/__test__/letter-repository.test.ts b/internal/datastore/src/__test__/letter-repository.test.ts
index 193c1c077..0f6149717 100644
--- a/internal/datastore/src/__test__/letter-repository.test.ts
+++ b/internal/datastore/src/__test__/letter-repository.test.ts
@@ -30,6 +30,7 @@ function createLetter(
source: "/data-plane/letter-rendering/pdf",
subject: `client/1/letter-request/${letterId}`,
billingRef: "specification1",
+ specificationBillingId: "billing1",
};
}
diff --git a/internal/datastore/src/types.ts b/internal/datastore/src/types.ts
index efeefe345..2eefb9bb0 100644
--- a/internal/datastore/src/types.ts
+++ b/internal/datastore/src/types.ts
@@ -54,6 +54,7 @@ export const LetterSchema = LetterSchemaBase.extend({
source: z.string(),
subject: z.string(),
billingRef: z.string(),
+ specificationBillingId: z.string(),
}).describe("Letter");
/**
diff --git a/internal/events/package.json b/internal/events/package.json
index cf2e4f0e4..3872892d5 100644
--- a/internal/events/package.json
+++ b/internal/events/package.json
@@ -37,5 +37,5 @@
"typecheck": "tsc --noEmit"
},
"types": "dist/index.d.ts",
- "version": "1.0.14"
+ "version": "1.0.15"
}
diff --git a/internal/events/src/domain/letter.ts b/internal/events/src/domain/letter.ts
index 67ed8bbe8..c5aa00597 100644
--- a/internal/events/src/domain/letter.ts
+++ b/internal/events/src/domain/letter.ts
@@ -83,6 +83,13 @@ The identifier will be included as the origin domain in the subject of any corre
examples: ["1y3q9v1zzzz"],
}),
+ specificationBillingId: z.string().meta({
+ title: "Specification Billing ID",
+ description:
+ "The billing ID from the letter specification which was used to produce a letter pack for this request.",
+ examples: ["1y3q9v1zzzz"],
+ }),
+
supplierId: z.string().meta({
title: "Supplier ID",
description: "Supplier ID allocated to the letter during creation.",
diff --git a/internal/events/src/events/__tests__/letter-mapper.test.ts b/internal/events/src/events/__tests__/letter-mapper.test.ts
index c870dc91b..26ea6d00b 100644
--- a/internal/events/src/events/__tests__/letter-mapper.test.ts
+++ b/internal/events/src/events/__tests__/letter-mapper.test.ts
@@ -16,6 +16,7 @@ describe("letter-mapper", () => {
updatedAt: "2025-11-24T15:55:18.000Z",
source: "letter-rendering/source/test",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
} as Letter;
const source = "/data-plane/supplier-api/nhs-supplier-api-dev/main/letters";
const event = mapLetterToCloudEvent(letter, source);
@@ -35,6 +36,7 @@ describe("letter-mapper", () => {
status: "PRINTED",
specificationId: "spec1",
billingRef: "spec1",
+ specificationBillingId: "billing123",
supplierId: "supplier1",
groupId: "group1",
reasonCode: "R02",
diff --git a/internal/events/src/events/__tests__/letter-status-change-events.test.ts b/internal/events/src/events/__tests__/letter-status-change-events.test.ts
index 482155458..6ccd40205 100644
--- a/internal/events/src/events/__tests__/letter-status-change-events.test.ts
+++ b/internal/events/src/events/__tests__/letter-status-change-events.test.ts
@@ -40,6 +40,7 @@ describe("LetterStatus event validations", () => {
billingRef: "1y3q9v1zzzz",
groupId: "client_template",
status,
+ specificationBillingId: "billing123",
}),
}),
);
diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json
index 192ea5e2e..81d4ded6a 100644
--- a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json
+++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-invalid-major-version.json
@@ -9,6 +9,7 @@
"source": "/data-plane/letter-rendering/prod/render-pdf",
"subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5"
},
+ "specificationBillingId": "billing123",
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED",
"supplierId": "supplier1"
diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json
index 54000422a..0f03dc39f 100644
--- a/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json
+++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED-with-missing-fields.json
@@ -8,6 +8,7 @@
"event": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"source": "/data-plane/letter-rendering/prod/render-pdf"
},
+ "specificationBillingId": "billing123",
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED",
"supplierId": "supplier1"
diff --git a/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json b/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json
index 7ffac10f3..8182cc1fe 100644
--- a/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json
+++ b/internal/events/src/events/__tests__/testData/letter.ACCEPTED.json
@@ -9,6 +9,7 @@
"source": "/data-plane/letter-rendering/prod/render-pdf",
"subject": "client/00f3b388-bbe9-41c9-9e76-052d37ee8988/letter-request/0o5Fs0EELR0fUjHjbCnEtdUwQe4_0o5Fs0EELR0fUjHjbCnEtdUwQe5"
},
+ "specificationBillingId": "billing123",
"specificationId": "1y3q9v1zzzz",
"status": "ACCEPTED",
"supplierId": "supplier1"
diff --git a/internal/events/src/events/__tests__/testData/letter.FORWARDED.json b/internal/events/src/events/__tests__/testData/letter.FORWARDED.json
index 28c6111f5..fe53b766e 100644
--- a/internal/events/src/events/__tests__/testData/letter.FORWARDED.json
+++ b/internal/events/src/events/__tests__/testData/letter.FORWARDED.json
@@ -11,6 +11,7 @@
},
"reasonCode": "RNIB",
"reasonText": "RNIB",
+ "specificationBillingId": "billing123",
"specificationId": "1y3q9v1zzzz",
"status": "FORWARDED",
"supplierId": "supplier1"
diff --git a/internal/events/src/events/__tests__/testData/letter.RETURNED.json b/internal/events/src/events/__tests__/testData/letter.RETURNED.json
index 07b28154e..f35440e42 100644
--- a/internal/events/src/events/__tests__/testData/letter.RETURNED.json
+++ b/internal/events/src/events/__tests__/testData/letter.RETURNED.json
@@ -11,6 +11,7 @@
},
"reasonCode": "R07",
"reasonText": "No such address",
+ "specificationBillingId": "billing123",
"specificationId": "1y3q9v1zzzz",
"status": "RETURNED",
"supplierId": "supplier1"
diff --git a/internal/events/src/events/letter-mapper.ts b/internal/events/src/events/letter-mapper.ts
index 91f72988a..4b6781e17 100644
--- a/internal/events/src/events/letter-mapper.ts
+++ b/internal/events/src/events/letter-mapper.ts
@@ -25,6 +25,7 @@ export function mapLetterToCloudEvent(
status: letter.status,
specificationId: letter.specificationId,
billingRef: letter.billingRef,
+ specificationBillingId: letter.specificationBillingId,
supplierId: letter.supplierId,
groupId: letter.groupId,
reasonCode: letter.reasonCode,
diff --git a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts
index fa7f9f81e..d6440eee5 100644
--- a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts
+++ b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts
@@ -28,6 +28,7 @@ describe("letter-mapper", () => {
ttl: 123,
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
const result: PatchLetterResponse = mapToPatchLetterResponse(letter);
@@ -64,6 +65,7 @@ describe("letter-mapper", () => {
reasonText: "Reason text",
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
const result: PatchLetterResponse = mapToPatchLetterResponse(letter);
@@ -100,6 +102,7 @@ describe("letter-mapper", () => {
ttl: 123,
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
const result: GetLetterResponse = mapToGetLetterResponse(letter);
@@ -136,6 +139,7 @@ describe("letter-mapper", () => {
reasonText: "Reason text",
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
const result: GetLetterResponse = mapToGetLetterResponse(letter);
@@ -174,6 +178,7 @@ describe("letter-mapper", () => {
reasonText: "Reason text",
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
const result: GetLettersResponse = mapToGetLettersResponse([
diff --git a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts
index 08e103ab0..be69387e1 100644
--- a/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts
+++ b/lambdas/api-handler/src/services/__tests__/letter-operations.test.ts
@@ -41,6 +41,7 @@ function makeLetter(id: string, status: Letter["status"]): Letter {
reasonText: "Reason text",
source: "/data-plane/letter-rendering/pdf",
subject: "letter-rendering/source/letter/letter-id",
+ specificationBillingId: "billing123",
};
}
diff --git a/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts
index fd11de133..9a805f44e 100644
--- a/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts
+++ b/lambdas/letter-updates-transformer/src/__tests__/letter-updates-transformer.test.ts
@@ -339,6 +339,7 @@ function generateLetter(status: LetterStatus, id?: string): Letter {
supplierStatus: `supplier1#${status}`,
supplierStatusSk: "2025-12-10T11:12:54Z#1",
ttl: 1_234_567_890,
+ specificationBillingId: "billing1",
};
}
diff --git a/lambdas/supplier-allocator/src/config/__tests__/deps.test.ts b/lambdas/supplier-allocator/src/config/__tests__/deps.test.ts
index b8b7b736e..64d41a1cb 100644
--- a/lambdas/supplier-allocator/src/config/__tests__/deps.test.ts
+++ b/lambdas/supplier-allocator/src/config/__tests__/deps.test.ts
@@ -6,6 +6,7 @@ describe("createDependenciesContainer", () => {
lv1: {
supplierId: "supplier1",
specId: "spec1",
+ billingId: "billing1",
},
},
};
diff --git a/lambdas/supplier-allocator/src/config/__tests__/env.test.ts b/lambdas/supplier-allocator/src/config/__tests__/env.test.ts
index 3ad3991ac..04985c62b 100644
--- a/lambdas/supplier-allocator/src/config/__tests__/env.test.ts
+++ b/lambdas/supplier-allocator/src/config/__tests__/env.test.ts
@@ -19,6 +19,7 @@ describe("lambdaEnv", () => {
"lv1": {
"supplierId": "supplier1",
"specId": "spec1",
+ "billingId": "billing1",
"priority": 10
}
}`;
@@ -30,6 +31,7 @@ describe("lambdaEnv", () => {
lv1: {
supplierId: "supplier1",
specId: "spec1",
+ billingId: "billing1",
priority: 10,
},
},
diff --git a/lambdas/supplier-allocator/src/config/env.ts b/lambdas/supplier-allocator/src/config/env.ts
index d1e07403f..8f5601c76 100644
--- a/lambdas/supplier-allocator/src/config/env.ts
+++ b/lambdas/supplier-allocator/src/config/env.ts
@@ -5,6 +5,7 @@ const LetterVariantSchema = z.record(
z.object({
supplierId: z.string(),
specId: z.string(),
+ billingId: z.string(),
priority: z.int().min(0).max(99), // Lower number represents a higher priority
}),
);
diff --git a/lambdas/supplier-allocator/src/handler/__tests__/allocate-handler.test.ts b/lambdas/supplier-allocator/src/handler/__tests__/allocate-handler.test.ts
index dc9524e5e..1e29a6d0d 100644
--- a/lambdas/supplier-allocator/src/handler/__tests__/allocate-handler.test.ts
+++ b/lambdas/supplier-allocator/src/handler/__tests__/allocate-handler.test.ts
@@ -138,6 +138,7 @@ function createSupplierStatusChangeEvent(
billingRef: "1y3q9v1zzzz",
status: "RETURNED",
supplierId: "supplier1",
+ specificationBillingId: "billing1",
},
datacontenttype: "application/json",
dataschema:
@@ -174,6 +175,7 @@ describe("createSupplierAllocatorHandler", () => {
lv1: {
supplierId: "supplier1",
specId: "spec1",
+ billingId: "billing1",
priority: 10,
},
},
@@ -209,16 +211,9 @@ describe("createSupplierAllocatorHandler", () => {
expect(messageBody.supplierSpec).toEqual({
supplierId: "supplier1",
specId: "spec1",
+ billingId: "billing1",
priority: 10,
});
-
- assertMetricLogged(
- mockedDeps.logger,
- "supplier1",
- "10",
- MetricStatus.Success,
- 1,
- );
});
test("parses SNS notification and sends message to SQS queue for v1 event", async () => {
@@ -244,6 +239,7 @@ describe("createSupplierAllocatorHandler", () => {
expect(messageBody.supplierSpec).toEqual({
supplierId: "supplier1",
specId: "spec1",
+ billingId: "billing1",
priority: 10,
});
});
@@ -307,11 +303,10 @@ describe("createSupplierAllocatorHandler", () => {
const sendCall = (mockSqsClient.send as jest.Mock).mock.calls[0][0];
const messageBody = JSON.parse(sendCall.input.MessageBody);
- expect(messageBody.supplierSpec).toEqual({
- supplierId: "supplier1",
- specId: "spec1",
- priority: 10,
- });
+ expect(messageBody.supplierSpec.supplierId).toBe("supplier1");
+ expect(messageBody.supplierSpec.specId).toBe("spec1");
+ expect(messageBody.supplierSpec.billingId).toBe("billing1");
+ expect(messageBody.supplierSpec.priority).toBe(10);
});
test("processes multiple messages in batch", async () => {
diff --git a/lambdas/supplier-allocator/src/handler/allocate-handler.ts b/lambdas/supplier-allocator/src/handler/allocate-handler.ts
index 3fd4f3be5..d0b3b6ab2 100644
--- a/lambdas/supplier-allocator/src/handler/allocate-handler.ts
+++ b/lambdas/supplier-allocator/src/handler/allocate-handler.ts
@@ -8,7 +8,12 @@ import { Unit } from "aws-embedded-metrics";
import { MetricEntry, MetricStatus, buildEMFObject } from "@internal/helpers";
import { Deps } from "../config/deps";
-type SupplierSpec = { supplierId: string; specId: string; priority: number };
+type SupplierSpec = {
+ supplierId: string;
+ specId: string;
+ priority: number;
+ billingId: string;
+};
type PreparedEvents = LetterRequestPreparedEventV2 | LetterRequestPreparedEvent;
// small envelope that must exist in all inputs
diff --git a/lambdas/update-letter-queue/src/__tests__/update-letter-queue.test.ts b/lambdas/update-letter-queue/src/__tests__/update-letter-queue.test.ts
index 6af007ab9..577fc2be4 100644
--- a/lambdas/update-letter-queue/src/__tests__/update-letter-queue.test.ts
+++ b/lambdas/update-letter-queue/src/__tests__/update-letter-queue.test.ts
@@ -48,6 +48,7 @@ function generateLetter(status: LetterStatus, id?: string): Letter {
source: "test-source",
subject: "test-subject",
billingRef: "billing-ref-1",
+ specificationBillingId: "billing1",
priority: 2,
};
}
diff --git a/lambdas/upsert-letter/src/handler/__tests__/upsert-handler.test.ts b/lambdas/upsert-letter/src/handler/__tests__/upsert-handler.test.ts
index 9f855d176..65e3bd9f4 100644
--- a/lambdas/upsert-letter/src/handler/__tests__/upsert-handler.test.ts
+++ b/lambdas/upsert-letter/src/handler/__tests__/upsert-handler.test.ts
@@ -97,6 +97,7 @@ function createSupplierStatusChangeEventWithoutSupplier(
billingRef: "1y3q9v1zzzz",
status: "RETURNED",
supplierId: "",
+ specificationBillingId: "billing1",
},
datacontenttype: "application/json",
dataschema:
@@ -151,6 +152,7 @@ function createSupplierStatusChangeEvent(
billingRef: "1y3q9v1zzzz",
status: "RETURNED",
supplierId: "supplier1",
+ specificationBillingId: "billing1",
},
datacontenttype: "application/json",
dataschema:
@@ -211,11 +213,21 @@ describe("createUpsertLetterHandler", () => {
test("processes all records successfully and returns no batch failures", async () => {
const v2message = {
letterEvent: createPreparedV2Event(),
- supplierSpec: { supplierId: "supplier1", specId: "spec1", priority: 10 },
+ supplierSpec: {
+ supplierId: "supplier1",
+ specId: "spec1",
+ billingId: "billing1",
+ priority: 10,
+ },
};
const v1message = {
letterEvent: createPreparedV1Event(),
- supplierSpec: { supplierId: "supplier1", specId: "spec1", priority: 10 },
+ supplierSpec: {
+ supplierId: "supplier2",
+ specId: "spec2",
+ billingId: "billing2",
+ priority: 10,
+ },
};
const evt: SQSEvent = createSQSEvent([
@@ -249,18 +261,20 @@ describe("createUpsertLetterHandler", () => {
expect(insertedV2Letter.status).toBe("PENDING");
expect(insertedV2Letter.groupId).toBe("client1campaign1template1");
expect(insertedV2Letter.source).toBe("/data-plane/letter-rendering/test");
+ expect(insertedV2Letter.specificationBillingId).toBe("billing1");
expect(insertedV2Letter.priority).toBe(10);
const insertedV1Letter = (mockedDeps.letterRepo.putLetter as jest.Mock).mock
.calls[1][0];
expect(insertedV1Letter.id).toBe("letter1");
- expect(insertedV1Letter.supplierId).toBe("supplier1");
- expect(insertedV1Letter.specificationId).toBe("spec1");
- expect(insertedV1Letter.billingRef).toBe("spec1");
+ expect(insertedV1Letter.supplierId).toBe("supplier2");
+ expect(insertedV1Letter.specificationId).toBe("spec2");
+ expect(insertedV1Letter.billingRef).toBe("spec2");
expect(insertedV1Letter.url).toBe("s3://letterDataBucket/letter1.pdf");
expect(insertedV1Letter.status).toBe("PENDING");
expect(insertedV1Letter.groupId).toBe("client1campaign1template1");
expect(insertedV1Letter.source).toBe("/data-plane/letter-rendering/test");
+ expect(insertedV1Letter.specificationBillingId).toBe("billing2");
expect(insertedV1Letter.priority).toBe(10);
const updatedLetter = (
@@ -277,7 +291,12 @@ describe("createUpsertLetterHandler", () => {
});
expect(mockMetrics.putMetric).toHaveBeenCalledWith(
"MessagesProcessed",
- 3,
+ 2,
+ "Count",
+ );
+ expect(mockMetrics.putMetric).toHaveBeenCalledWith(
+ "MessagesProcessed",
+ 1,
"Count",
);
});
@@ -474,14 +493,24 @@ describe("createUpsertLetterHandler", () => {
id: "7b9a03ca-342a-4150-b56b-989109c45615",
domainId: "ok",
}),
- supplierSpec: { supplierId: "supplier1", specId: "spec1", priority: 10 },
+ supplierSpec: {
+ supplierId: "supplier1",
+ specId: "spec1",
+ billingId: "billing1",
+ priority: 10,
+ },
};
const message2 = {
letterEvent: createPreparedV2Event({
id: "7b9a03ca-342a-4150-b56b-989109c45616",
domainId: "fail",
}),
- supplierSpec: { supplierId: "supplier1", specId: "spec1", priority: 10 },
+ supplierSpec: {
+ supplierId: "supplier1",
+ specId: "spec1",
+ billingId: "billing1",
+ priority: 10,
+ },
};
const evt: SQSEvent = createSQSEvent([
createSqsRecord("ok-msg", JSON.stringify(message1)),
diff --git a/lambdas/upsert-letter/src/handler/upsert-handler.ts b/lambdas/upsert-letter/src/handler/upsert-handler.ts
index ab9bd9877..e384d544b 100644
--- a/lambdas/upsert-letter/src/handler/upsert-handler.ts
+++ b/lambdas/upsert-letter/src/handler/upsert-handler.ts
@@ -21,6 +21,7 @@ type PreparedEvents = LetterRequestPreparedEventV2 | LetterRequestPreparedEvent;
const SupplierSpecSchema = z.object({
supplierId: z.string().min(1),
specId: z.string().min(1),
+ billingId: z.string().min(1),
priority: z.int().min(0).max(99).default(10),
});
@@ -64,7 +65,8 @@ function getOperationFromType(type: string): UpsertOperation {
preparedRequest,
supplierSpec.supplierId,
supplierSpec.specId,
- supplierSpec.specId, // use specId for now
+ supplierSpec.specId, // use specId for now billingRef
+ supplierSpec.billingId, // use billingId for specificationBillingId,
supplierSpec.priority,
);
await deps.letterRepo.putLetter(letterToInsert);
@@ -102,6 +104,7 @@ function mapToInsertLetter(
supplier: string,
spec: string,
billingRef: string,
+ billingId: string,
priority: number,
): InsertLetter {
const now = new Date().toISOString();
@@ -122,6 +125,7 @@ function mapToInsertLetter(
createdAt: now,
updatedAt: now,
billingRef,
+ specificationBillingId: billingId,
};
}
@@ -243,6 +247,7 @@ export default function createUpsertLetterHandler(deps: Deps): SQSHandler {
supplierSpec ?? {
supplierId: "unknown",
specId: "unknown",
+ billingId: "unknown",
priority: 10,
},
deps,
diff --git a/scripts/utilities/letter-test-data/README.md b/scripts/utilities/letter-test-data/README.md
index 284268885..52ed66166 100644
--- a/scripts/utilities/letter-test-data/README.md
+++ b/scripts/utilities/letter-test-data/README.md
@@ -16,6 +16,7 @@ npm run cli -- create-letter \
--letter-id letter-id \
--group-id group-id \
--specification-id specification-id \
+ --billing-id billing-id \
--status PENDING
```
@@ -26,6 +27,7 @@ npm run cli -- create-letter-batch \
--awsAccountId 820178564574 \
--group-id group-id \
--specification-id specification-id \
+ --billing-id billing-id \
--status PENDING \
--count 10
```
diff --git a/scripts/utilities/letter-test-data/src/__test__/helpers/create-letter-helpers.test.ts b/scripts/utilities/letter-test-data/src/__test__/helpers/create-letter-helpers.test.ts
index 9f366d2dd..66af9c01a 100644
--- a/scripts/utilities/letter-test-data/src/__test__/helpers/create-letter-helpers.test.ts
+++ b/scripts/utilities/letter-test-data/src/__test__/helpers/create-letter-helpers.test.ts
@@ -29,6 +29,7 @@ describe("Create letter helpers", () => {
const targetFilename = "targetFilename";
const groupId = "groupId";
const specificationId = "specificationId";
+ const billingId = "billingId";
const status = "PENDING" as LetterStatusType;
const testLetter = "test-letter-standard";
@@ -39,6 +40,7 @@ describe("Create letter helpers", () => {
targetFilename,
groupId,
specificationId,
+ billingId,
status,
letterRepository: mockedLetterRepository,
testLetter,
@@ -62,6 +64,7 @@ describe("Create letter helpers", () => {
source: "/data-plane/letter-rendering/letter-test-data",
subject: "supplier-api/letter-test-data/letterId",
billingRef: "specificationId",
+ specificationBillingId: "billingId",
});
});
@@ -81,6 +84,7 @@ describe("Create letter helpers", () => {
const targetFilename = "targetFilename";
const groupId = "groupId";
const specificationId = "specificationId";
+ const billingId = "billingId";
const status = "PENDING" as LetterStatusType;
const testLetter = "none";
@@ -91,6 +95,7 @@ describe("Create letter helpers", () => {
targetFilename,
groupId,
specificationId,
+ billingId,
status,
letterRepository: mockedLetterRepository,
testLetter,
@@ -110,6 +115,7 @@ describe("Create letter helpers", () => {
billingRef: "specificationId",
source: "/data-plane/letter-rendering/letter-test-data",
subject: "supplier-api/letter-test-data/letterId",
+ specificationBillingId: "billingId",
});
});
@@ -121,6 +127,7 @@ describe("Create letter helpers", () => {
letterId: "testLetterId",
supplierId: "testSupplierId",
specificationId: "testSpecId",
+ billingId: "testBillingId",
groupId: "testGroupId",
status: "PENDING" as LetterStatusType,
url: "s3://bucket/testSupplierId/testLetter.pdf",
@@ -140,6 +147,7 @@ describe("Create letter helpers", () => {
source: "/data-plane/letter-rendering/letter-test-data",
subject: "supplier-api/letter-test-data/testLetterId",
billingRef: "testSpecId",
+ specificationBillingId: "testBillingId",
});
});
});
diff --git a/scripts/utilities/letter-test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts
index 131fe224e..982e9e4ee 100644
--- a/scripts/utilities/letter-test-data/src/cli/index.ts
+++ b/scripts/utilities/letter-test-data/src/cli/index.ts
@@ -44,6 +44,10 @@ async function main() {
type: "string",
demandOption: false,
},
+ "billing-id": {
+ type: "string",
+ demandOption: false,
+ },
"ttl-hours": {
type: "number",
demandOption: false,
@@ -83,6 +87,7 @@ async function main() {
const targetFilename = `${letterId}.pdf`;
const groupId = argv.groupId ?? randomUUID();
const specificationId = argv.specificationId ?? randomUUID();
+ const billingId = argv.billingId ?? randomUUID();
const { status } = argv;
const { environment } = argv;
const { ttlHours } = argv;
@@ -96,6 +101,7 @@ async function main() {
targetFilename,
groupId,
specificationId,
+ billingId,
status: status as LetterStatusType,
letterRepository,
testLetter,
@@ -130,6 +136,10 @@ async function main() {
type: "string",
demandOption: false,
},
+ "billing-id": {
+ type: "string",
+ demandOption: false,
+ },
"ttl-hours": {
type: "number",
demandOption: false,
@@ -174,6 +184,7 @@ async function main() {
const { supplierId } = argv;
const groupId = argv.groupId ?? randomUUID();
const specificationId = argv.specificationId ?? randomUUID();
+ const billingId = argv.billingId ?? randomUUID();
const { status } = argv;
const { environment } = argv;
const { ttlHours } = argv;
@@ -206,6 +217,7 @@ async function main() {
supplierId,
groupId,
specificationId,
+ billingId,
status: status as LetterStatusType,
url,
}),
diff --git a/scripts/utilities/letter-test-data/src/helpers/create-letter-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/create-letter-helpers.ts
index 20131b56c..6278dbbe7 100644
--- a/scripts/utilities/letter-test-data/src/helpers/create-letter-helpers.ts
+++ b/scripts/utilities/letter-test-data/src/helpers/create-letter-helpers.ts
@@ -11,12 +11,14 @@ export async function createLetter(params: {
supplierId: string;
targetFilename: string;
specificationId: string;
+ billingId: string;
groupId: string;
status: LetterStatusType;
letterRepository: LetterRepository;
testLetter: string;
}) {
const {
+ billingId,
bucketName,
groupId,
letterId,
@@ -49,6 +51,7 @@ export async function createLetter(params: {
source: "/data-plane/letter-rendering/letter-test-data",
subject: `supplier-api/letter-test-data/${letterId}`,
billingRef: specificationId,
+ specificationBillingId: billingId,
};
const letterRecord = await letterRepository.putLetter(letter);
@@ -59,12 +62,20 @@ export function createLetterDto(params: {
letterId: string;
supplierId: string;
specificationId: string;
+ billingId: string;
groupId: string;
status: LetterStatusType;
url: string;
}) {
- const { groupId, letterId, specificationId, status, supplierId, url } =
- params;
+ const {
+ billingId,
+ groupId,
+ letterId,
+ specificationId,
+ status,
+ supplierId,
+ url,
+ } = params;
const letter: Omit