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 = { id: letterId, @@ -78,6 +89,7 @@ export function createLetterDto(params: { source: "/data-plane/letter-rendering/letter-test-data", subject: `supplier-api/letter-test-data/${letterId}`, billingRef: specificationId, + specificationBillingId: billingId, }; return letter;