From 3f77980d887113ae20362eaef26bb35162ffa9c0 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 4 Jun 2026 11:05:29 +0100 Subject: [PATCH 01/28] inital changes for sha256 --- internal/datastore/src/types.ts | 1 + lambdas/api-handler/src/contracts/letters.ts | 1 + lambdas/api-handler/src/mappers/letter-mapper.ts | 1 + lambdas/upsert-letter/src/handler/upsert-handler.ts | 1 + .../letter-test-data/src/__test__/helpers/s3-helpers.test.ts | 4 ++++ scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts | 5 +++++ 6 files changed, 13 insertions(+) diff --git a/internal/datastore/src/types.ts b/internal/datastore/src/types.ts index 53708cedf..39ece3492 100644 --- a/internal/datastore/src/types.ts +++ b/internal/datastore/src/types.ts @@ -42,6 +42,7 @@ export const LetterSchemaBase = z.object({ groupId: z.string(), reasonCode: z.string().optional(), reasonText: z.string().optional(), + sha256Hash: z.string().optional(), }); export const LetterSchema = LetterSchemaBase.extend({ diff --git a/lambdas/api-handler/src/contracts/letters.ts b/lambdas/api-handler/src/contracts/letters.ts index 0c3d14182..ad906b6f2 100644 --- a/lambdas/api-handler/src/contracts/letters.ts +++ b/lambdas/api-handler/src/contracts/letters.ts @@ -52,6 +52,7 @@ export const GetLetterResponseResourceSchema = z groupId: z.string().optional(), reasonCode: z.string().optional(), reasonText: z.string().optional(), + sha256Hash: z.string().optional(), }) .strict(), }) diff --git a/lambdas/api-handler/src/mappers/letter-mapper.ts b/lambdas/api-handler/src/mappers/letter-mapper.ts index c31d61b34..0db9bcca6 100644 --- a/lambdas/api-handler/src/mappers/letter-mapper.ts +++ b/lambdas/api-handler/src/mappers/letter-mapper.ts @@ -22,6 +22,7 @@ function letterToResourceResponse(letter: LetterBase) { groupId: letter.groupId, ...(letter.reasonCode != null && { reasonCode: letter.reasonCode }), ...(letter.reasonText != null && { reasonText: letter.reasonText }), + ...(letter.sha256Hash != null && { sha256Hash: letter.sha256Hash }), }, }; } diff --git a/lambdas/upsert-letter/src/handler/upsert-handler.ts b/lambdas/upsert-letter/src/handler/upsert-handler.ts index a38bbf013..fa49adb05 100644 --- a/lambdas/upsert-letter/src/handler/upsert-handler.ts +++ b/lambdas/upsert-letter/src/handler/upsert-handler.ts @@ -132,6 +132,7 @@ function mapToInsertLetter( upsertRequest.data.templateId, ), url: upsertRequest.data.url, + sha256Hash: upsertRequest.data.sha256Hash, source: upsertRequest.source, subject: upsertRequest.subject, createdAt: now, diff --git a/scripts/utilities/letter-test-data/src/__test__/helpers/s3-helpers.test.ts b/scripts/utilities/letter-test-data/src/__test__/helpers/s3-helpers.test.ts index 22e56cd54..93e996fbf 100644 --- a/scripts/utilities/letter-test-data/src/__test__/helpers/s3-helpers.test.ts +++ b/scripts/utilities/letter-test-data/src/__test__/helpers/s3-helpers.test.ts @@ -64,6 +64,10 @@ describe("uploadFile", () => { Key: `${supplierId}/${targetFilename}`, Body: Buffer.from("fake-pdf-bytes"), ContentType: "application/pdf", + Metadata: { + sha256Hash: + "50af8d443ccf8b2777b72a9169cd0665ef4be5335b8f53543556fa0d320b135b", + }, }); }); diff --git a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts index 3f25a5c79..58024ba76 100644 --- a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts @@ -1,4 +1,5 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; +import { createHash } from "crypto"; import { readFileSync } from "node:fs"; import path from "node:path"; @@ -12,12 +13,16 @@ export default async function uploadFile( const s3 = new S3Client(); const filePath = path.join(__dirname, "..", "test-letters", sourceFilename); const fileContent = readFileSync(filePath); + const hash = createHash("sha256").update(fileContent).digest("hex"); const uploadParams = { Bucket: bucketName, Key: `${folder}/${targetFilename}`, Body: fileContent, ContentType: "application/pdf", + Metadata: { + sha256Hash: hash, + }, }; const command = new PutObjectCommand(uploadParams); From b95e224bbb06303a1d8ac6203726b6ce53131187 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 4 Jun 2026 12:22:05 +0100 Subject: [PATCH 02/28] use node:crypto instead of crypto --- scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts index 58024ba76..33f7104e1 100644 --- a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts @@ -1,5 +1,5 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; -import { createHash } from "crypto"; +import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import path from "node:path"; From 6e0b2efaf7915fc08dd56c29f991efa182e9afe9 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 4 Jun 2026 12:35:30 +0100 Subject: [PATCH 03/28] remove blank line in object --- internal/event-builders/src/letter-mapper.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/event-builders/src/letter-mapper.ts b/internal/event-builders/src/letter-mapper.ts index 27083b1b3..2367045c7 100644 --- a/internal/event-builders/src/letter-mapper.ts +++ b/internal/event-builders/src/letter-mapper.ts @@ -19,7 +19,6 @@ export function mapLetterToCloudEvent( dataschemaversion, source, subject: `letter-origin/letter-rendering/letter/${letter.id}`, - data: { domainId: letter.id as LetterStatusChangeEvent["data"]["domainId"], status: letter.status, From 1a2361baf989072b557b84c899ee39646d288e34 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 4 Jun 2026 12:44:59 +0100 Subject: [PATCH 04/28] temporarily see if Omit fixes test --- internal/event-builders/src/letter-mapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/event-builders/src/letter-mapper.ts b/internal/event-builders/src/letter-mapper.ts index 2367045c7..5925863b7 100644 --- a/internal/event-builders/src/letter-mapper.ts +++ b/internal/event-builders/src/letter-mapper.ts @@ -5,7 +5,7 @@ import { LetterStatusChangeEvent } from "@nhsdigital/nhs-notify-event-schemas-su // eslint-disable-next-line import-x/prefer-default-export export function mapLetterToCloudEvent( - letter: Letter, + letter: Omit, // check to see if need to change schema outside of this repo source: string, ): LetterStatusChangeEvent { const eventId = randomUUID(); From d353991045bd7d10b6da32aecb6f66728c3dbd7f Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 4 Jun 2026 13:50:24 +0100 Subject: [PATCH 05/28] removed temporary omit --- internal/event-builders/src/letter-mapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/event-builders/src/letter-mapper.ts b/internal/event-builders/src/letter-mapper.ts index 5925863b7..2367045c7 100644 --- a/internal/event-builders/src/letter-mapper.ts +++ b/internal/event-builders/src/letter-mapper.ts @@ -5,7 +5,7 @@ import { LetterStatusChangeEvent } from "@nhsdigital/nhs-notify-event-schemas-su // eslint-disable-next-line import-x/prefer-default-export export function mapLetterToCloudEvent( - letter: Omit, // check to see if need to change schema outside of this repo + letter: Letter, source: string, ): LetterStatusChangeEvent { const eventId = randomUUID(); From a76506a3dc6318f280a8ca9d0f133110db68bb36 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Fri, 5 Jun 2026 09:50:21 +0100 Subject: [PATCH 06/28] add sha256 test --- .../mappers/__tests__/letter-mapper.test.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) 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 d6440eee5..a71841313 100644 --- a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts @@ -120,6 +120,41 @@ describe("letter-mapper", () => { }); }); + it("maps an internal Letter to a GetLetterResponse with sha256Hash when present", () => { + const date = new Date().toISOString(); + const letter: Letter = { + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + billingRef: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", + createdAt: date, + updatedAt: date, + supplierStatus: "supplier1#PENDING", + supplierStatusSk: date, + ttl: 123, + source: "/data-plane/letter-rendering/pdf", + subject: "letter-rendering/source/letter/letter-id", + specificationBillingId: "billing123", + }; + + const result: GetLetterResponse = mapToGetLetterResponse(letter); + + expect(result).toEqual({ + data: { + id: "abc123", + type: "Letter", + attributes: { + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + }, + }, + }); + }); + it("maps an internal Letter to a GetLetterResponse with reasonCode and reasonText when present", () => { const date = new Date().toISOString(); const letter: Letter = { From 9253cab0bd337361d11efe8c7885008990c2930a Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Fri, 5 Jun 2026 10:14:23 +0100 Subject: [PATCH 07/28] add sha256hash to test --- lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts | 2 ++ 1 file changed, 2 insertions(+) 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 a71841313..4be82c53e 100644 --- a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts @@ -135,6 +135,7 @@ describe("letter-mapper", () => { supplierStatus: "supplier1#PENDING", supplierStatusSk: date, ttl: 123, + sha256Hash: "abc123hash", source: "/data-plane/letter-rendering/pdf", subject: "letter-rendering/source/letter/letter-id", specificationBillingId: "billing123", @@ -150,6 +151,7 @@ describe("letter-mapper", () => { specificationId: "spec123", status: "PENDING", groupId: "group123", + sha256Hash: "abc123hash", }, }, }); From 27c745fc61bef18f3c26dadc17376e40c4a7bf3e Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Fri, 5 Jun 2026 11:38:08 +0100 Subject: [PATCH 08/28] add sha256Hash to the getLetters endpoint --- lambdas/api-handler/src/contracts/letters.ts | 1 + .../mappers/__tests__/letter-mapper.test.ts | 53 +++++++++++++++++++ .../api-handler/src/mappers/letter-mapper.ts | 1 + 3 files changed, 55 insertions(+) diff --git a/lambdas/api-handler/src/contracts/letters.ts b/lambdas/api-handler/src/contracts/letters.ts index ad906b6f2..e53dd935f 100644 --- a/lambdas/api-handler/src/contracts/letters.ts +++ b/lambdas/api-handler/src/contracts/letters.ts @@ -67,6 +67,7 @@ export const GetLettersResponseResourceSchema = z status: LetterStatusSchema, specificationId: z.string(), groupId: z.string().optional(), + sha256Hash: z.string().optional(), }) .strict(), }) 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 4be82c53e..758e039d0 100644 --- a/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts +++ b/lambdas/api-handler/src/mappers/__tests__/letter-mapper.test.ts @@ -246,4 +246,57 @@ describe("letter-mapper", () => { ], }); }); + it("maps an internal Letter collection to a GetLettersResponse with sha256Hash when present", () => { + const date = new Date().toISOString(); + const letter: Letter = { + id: "abc123", + status: "PENDING", + supplierId: "supplier1", + specificationId: "spec123", + billingRef: "spec123", + groupId: "group123", + url: "https://example.com/letter/abc123", + createdAt: date, + updatedAt: date, + supplierStatus: "supplier1#PENDING", + supplierStatusSk: date, + ttl: 123, + reasonCode: "R01", + reasonText: "Reason text", + sha256Hash: "abc123hash", + source: "/data-plane/letter-rendering/pdf", + subject: "letter-rendering/source/letter/letter-id", + specificationBillingId: "billing123", + }; + + const result: GetLettersResponse = mapToGetLettersResponse([ + letter, + letter, + ]); + + expect(result).toEqual({ + data: [ + { + id: "abc123", + type: "Letter", + attributes: { + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + sha256Hash: "abc123hash", + }, + }, + { + id: "abc123", + type: "Letter", + attributes: { + specificationId: "spec123", + status: "PENDING", + groupId: "group123", + sha256Hash: "abc123hash", + }, + }, + ], + }); + }); }); diff --git a/lambdas/api-handler/src/mappers/letter-mapper.ts b/lambdas/api-handler/src/mappers/letter-mapper.ts index 0db9bcca6..81403d6dd 100644 --- a/lambdas/api-handler/src/mappers/letter-mapper.ts +++ b/lambdas/api-handler/src/mappers/letter-mapper.ts @@ -35,6 +35,7 @@ function letterToGetLettersResourceResponse(letter: LetterBase) { status: letter.status, specificationId: letter.specificationId, groupId: letter.groupId, + ...(letter.sha256Hash != null && { sha256Hash: letter.sha256Hash }), }, }; } From b4729556cc4fc77db0181f81540ecb9704a2874e Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Fri, 5 Jun 2026 12:07:10 +0100 Subject: [PATCH 09/28] make sure sha256 passed across --- internal/datastore/src/types.ts | 1 + lambdas/api-handler/src/services/letter-operations.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/internal/datastore/src/types.ts b/internal/datastore/src/types.ts index 39ece3492..26739c6c7 100644 --- a/internal/datastore/src/types.ts +++ b/internal/datastore/src/types.ts @@ -88,6 +88,7 @@ export const PendingLetterSchemaBase = z.object({ letterId: idRef(LetterSchema, "id"), specificationId: z.string(), groupId: z.string(), + sha256Hash: z.string().optional(), }); export const PendingLetterSchema = PendingLetterSchemaBase.extend({ diff --git a/lambdas/api-handler/src/services/letter-operations.ts b/lambdas/api-handler/src/services/letter-operations.ts index a29017f65..322bd463e 100644 --- a/lambdas/api-handler/src/services/letter-operations.ts +++ b/lambdas/api-handler/src/services/letter-operations.ts @@ -36,6 +36,7 @@ function mapPendingLetterToLetterBase(pending: PendingLetterBase): LetterBase { status: "PENDING", specificationId: pending.specificationId, groupId: pending.groupId, + sha256Hash: pending.sha256Hash, }; } From 18736bd43075a0d172103a0b367056f9ffba8128 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 11:40:59 +0100 Subject: [PATCH 10/28] add sha256Hash generation in tests --- .../src/__test__/helpers/create-letter-helpers.test.ts | 2 ++ .../letter-test-data/src/helpers/create-letter-helpers.ts | 8 +++++++- .../utilities/letter-test-data/src/helpers/s3-helpers.ts | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) 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 66af9c01a..fcf69b5bc 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 @@ -131,6 +131,7 @@ describe("Create letter helpers", () => { groupId: "testGroupId", status: "PENDING" as LetterStatusType, url: "s3://bucket/testSupplierId/testLetter.pdf", + sha256Hash: "testHash", }; const result = createLetterDto(params); @@ -148,6 +149,7 @@ describe("Create letter helpers", () => { subject: "supplier-api/letter-test-data/testLetterId", billingRef: "testSpecId", specificationBillingId: "testBillingId", + sha256Hash: "testHash", }); }); }); 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 6278dbbe7..86ea880fe 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 @@ -17,6 +17,7 @@ export async function createLetter(params: { letterRepository: LetterRepository; testLetter: string; }) { + let hash: string | undefined = undefined; const { billingId, bucketName, @@ -31,12 +32,13 @@ export async function createLetter(params: { } = params; if (testLetter !== "none") { - await uploadFile( + const result = await uploadFile( bucketName, supplierId, `${testLetter}.pdf`, targetFilename, ); + hash = result.hash; } const letter: Omit = { @@ -52,6 +54,7 @@ export async function createLetter(params: { subject: `supplier-api/letter-test-data/${letterId}`, billingRef: specificationId, specificationBillingId: billingId, + sha256Hash: hash, }; const letterRecord = await letterRepository.putLetter(letter); @@ -66,6 +69,7 @@ export function createLetterDto(params: { groupId: string; status: LetterStatusType; url: string; + sha256Hash?: string; }) { const { billingId, @@ -75,6 +79,7 @@ export function createLetterDto(params: { status, supplierId, url, + sha256Hash, } = params; const letter: Omit = { @@ -90,6 +95,7 @@ export function createLetterDto(params: { subject: `supplier-api/letter-test-data/${letterId}`, billingRef: specificationId, specificationBillingId: billingId, + sha256Hash, }; return letter; diff --git a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts index 33f7104e1..023202031 100644 --- a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts @@ -26,7 +26,8 @@ export default async function uploadFile( }; const command = new PutObjectCommand(uploadParams); - return await s3.send(command); + const commandResult = await s3.send(command); + return {commandResult, hash}; } catch (error) { console.error("Error uploading file:", error); throw error; From d65c5a860057d714aec4d24488bb11e45525affa Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 11:58:50 +0100 Subject: [PATCH 11/28] fix test and lint issue --- .../src/__test__/helpers/create-letter-helpers.test.ts | 6 ++++-- .../letter-test-data/src/helpers/create-letter-helpers.ts | 3 --- .../utilities/letter-test-data/src/helpers/s3-helpers.ts | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) 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 fcf69b5bc..66bedc1b9 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 @@ -33,6 +33,8 @@ describe("Create letter helpers", () => { const status = "PENDING" as LetterStatusType; const testLetter = "test-letter-standard"; + (uploadFile as jest.Mock).mockResolvedValue({ hash: "abc123" }); + await createLetter({ letterId, bucketName, @@ -65,6 +67,7 @@ describe("Create letter helpers", () => { subject: "supplier-api/letter-test-data/letterId", billingRef: "specificationId", specificationBillingId: "billingId", + sha256Hash: "abc123", }); }); @@ -116,6 +119,7 @@ describe("Create letter helpers", () => { source: "/data-plane/letter-rendering/letter-test-data", subject: "supplier-api/letter-test-data/letterId", specificationBillingId: "billingId", + sha256Hash: undefined, }); }); @@ -131,7 +135,6 @@ describe("Create letter helpers", () => { groupId: "testGroupId", status: "PENDING" as LetterStatusType, url: "s3://bucket/testSupplierId/testLetter.pdf", - sha256Hash: "testHash", }; const result = createLetterDto(params); @@ -149,7 +152,6 @@ describe("Create letter helpers", () => { subject: "supplier-api/letter-test-data/testLetterId", billingRef: "testSpecId", specificationBillingId: "testBillingId", - sha256Hash: "testHash", }); }); }); 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 86ea880fe..4beaf263f 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 @@ -69,7 +69,6 @@ export function createLetterDto(params: { groupId: string; status: LetterStatusType; url: string; - sha256Hash?: string; }) { const { billingId, @@ -79,7 +78,6 @@ export function createLetterDto(params: { status, supplierId, url, - sha256Hash, } = params; const letter: Omit = { @@ -95,7 +93,6 @@ export function createLetterDto(params: { subject: `supplier-api/letter-test-data/${letterId}`, billingRef: specificationId, specificationBillingId: billingId, - sha256Hash, }; return letter; diff --git a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts index 023202031..6da89b4a2 100644 --- a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts @@ -27,7 +27,7 @@ export default async function uploadFile( const command = new PutObjectCommand(uploadParams); const commandResult = await s3.send(command); - return {commandResult, hash}; + return { commandResult, hash}; } catch (error) { console.error("Error uploading file:", error); throw error; From ed10eb350e3c134af4f584c17e3c8c8c3715d3f1 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 12:07:45 +0100 Subject: [PATCH 12/28] fix lint issue --- .../letter-test-data/src/helpers/create-letter-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4beaf263f..618ee4652 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 @@ -17,7 +17,7 @@ export async function createLetter(params: { letterRepository: LetterRepository; testLetter: string; }) { - let hash: string | undefined = undefined; + let hash: string | undefined; const { billingId, bucketName, From ea726eca78862afad52298d789bbf0b6e27c570b Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 12:16:44 +0100 Subject: [PATCH 13/28] fix another lint issue --- scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts index 6da89b4a2..735d47e55 100644 --- a/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts +++ b/scripts/utilities/letter-test-data/src/helpers/s3-helpers.ts @@ -27,7 +27,7 @@ export default async function uploadFile( const command = new PutObjectCommand(uploadParams); const commandResult = await s3.send(command); - return { commandResult, hash}; + return { commandResult, hash }; } catch (error) { console.error("Error uploading file:", error); throw error; From 3db34c756de02b45e6871189fcaba8e2c03a895f Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 13:35:40 +0100 Subject: [PATCH 14/28] add hash to tests --- internal/datastore/src/__test__/letter-queue-repository.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/datastore/src/__test__/letter-queue-repository.test.ts b/internal/datastore/src/__test__/letter-queue-repository.test.ts index 74812b010..bcac9c20b 100644 --- a/internal/datastore/src/__test__/letter-queue-repository.test.ts +++ b/internal/datastore/src/__test__/letter-queue-repository.test.ts @@ -21,6 +21,7 @@ function createLetter( specificationId: "specification1", groupId: "group1", priority: 10, + sha256Hash: "hash1", ...overrides, }; } From 85efcec166b2f5e3e6cebfb9165b71b10176ff00 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 14:39:29 +0100 Subject: [PATCH 15/28] add hash in more test data --- scripts/utilities/letter-test-data/src/cli/index.ts | 8 +++++--- .../letter-test-data/src/helpers/create-letter-helpers.ts | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/utilities/letter-test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts index 0b6c32371..6a7f3cc6c 100644 --- a/scripts/utilities/letter-test-data/src/cli/index.ts +++ b/scripts/utilities/letter-test-data/src/cli/index.ts @@ -192,21 +192,22 @@ async function main() { const letterRepository = createLetterRepository(environment, ttlHours); const { count } = argv; const { testLetter } = argv; - // Setup file attributes const bucketName = `nhs-${argv.awsAccountId}-eu-west-2-${argv.environment}-supapi-test-letters`; const targetFilename = `${batchId}-${status}.pdf`; const folder = `${supplierId}/${batchId}`; const url = `s3://${bucketName}/${folder}/${targetFilename}`; + let hash: string | undefined; // Upload a test file for this batch if it is not an 'none' batch if (testLetter !== "none") { - await uploadFile( + const result = await uploadFile( bucketName, - folder, + supplierId, `${testLetter}.pdf`, targetFilename, ); + hash = result.hash; } // Create letter DTOs @@ -221,6 +222,7 @@ async function main() { billingId, status: status as LetterStatusType, url, + sha256Hash: hash, }), ); } 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 618ee4652..ff5fb4f18 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 @@ -69,6 +69,7 @@ export function createLetterDto(params: { groupId: string; status: LetterStatusType; url: string; + sha256Hash?: string; }) { const { billingId, @@ -78,6 +79,7 @@ export function createLetterDto(params: { status, supplierId, url, + sha256Hash, } = params; const letter: Omit = { From 3268427a46d60bb01640a45af6def9dd309efa5a Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Mon, 8 Jun 2026 14:47:19 +0100 Subject: [PATCH 16/28] fix dto issue --- .../letter-test-data/src/helpers/create-letter-helpers.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 ff5fb4f18..c078052de 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 @@ -75,11 +75,11 @@ export function createLetterDto(params: { billingId, groupId, letterId, + sha256Hash, specificationId, status, supplierId, url, - sha256Hash, } = params; const letter: Omit = { @@ -88,6 +88,7 @@ export function createLetterDto(params: { specificationId, groupId, url, + sha256Hash, status, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), From d6f65f060a9b3dc847f7c5f8e11c42d7f6a97d91 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 10:23:48 +0100 Subject: [PATCH 17/28] fix uploadFile issue --- scripts/utilities/letter-test-data/src/cli/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/utilities/letter-test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts index 6a7f3cc6c..0d8f01910 100644 --- a/scripts/utilities/letter-test-data/src/cli/index.ts +++ b/scripts/utilities/letter-test-data/src/cli/index.ts @@ -203,7 +203,7 @@ async function main() { if (testLetter !== "none") { const result = await uploadFile( bucketName, - supplierId, + folder, `${testLetter}.pdf`, targetFilename, ); From b03d21eacecba7c50e972c796d914b2df840f5e8 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 11:16:39 +0100 Subject: [PATCH 18/28] add sha256 to projection --- tests/helpers/generate-fetch-test-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/helpers/generate-fetch-test-data.ts b/tests/helpers/generate-fetch-test-data.ts index a3fdc75e3..0a1ef281e 100644 --- a/tests/helpers/generate-fetch-test-data.ts +++ b/tests/helpers/generate-fetch-test-data.ts @@ -208,7 +208,7 @@ export async function waitForLetterStatus( TableName: LETTERSTABLENAME, Key: { id, supplierId }, ProjectionExpression: - "id, #status, supplierId, specificationId, groupId, reasonCode, reasonText", + "id, #status, supplierId, specificationId, groupId, reasonCode, reasonText, sha256Hash", ExpressionAttributeNames: { "#status": "status", }, From da529acc3936b5c5ceb5a18dc33151c0e1ccbec3 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 12:35:07 +0100 Subject: [PATCH 19/28] amend sandbox and oas --- ...getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + ...getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json | 1 + .../responses/getLetters_pending.json | 23 +++++++++++++++++++ .../api/components/schemas/letterItem.yml | 2 ++ .../api/components/schemas/sha256Hash.yml | 3 +++ 13 files changed, 38 insertions(+) create mode 100644 specification/api/components/schemas/sha256Hash.yml diff --git a/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json index f72687b47..394d052ff 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json index 905df7328..b8ab0f83a 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "ACCEPTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json index ae4f8d46f..cc410c0d0 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PRINTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json index afa79b734..48b5b6531 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "ENCLOSED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json index 9f5223d70..c1f000225 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "DISPATCHED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json index ded23e7a1..14dfae3f6 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,6 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "DELIVERED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json index ec685a98b..bf480bebc 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,6 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "REJECTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json index d37729d62..f932e9859 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -3,6 +3,7 @@ "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "CANCELLED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json index 945e8270b..ce06ba4a8 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,6 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "FAILED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json index fff8bf1dd..34e794fc6 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,6 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "RETURNED" }, diff --git a/sandbox/data/examples/getLetters/responses/getLetters_pending.json b/sandbox/data/examples/getLetters/responses/getLetters_pending.json index c3e72d3b6..16b44ac30 100644 --- a/sandbox/data/examples/getLetters/responses/getLetters_pending.json +++ b/sandbox/data/examples/getLetters/responses/getLetters_pending.json @@ -3,6 +3,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -12,6 +13,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -21,6 +23,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -30,6 +33,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -39,6 +43,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -48,6 +53,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -57,6 +63,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -66,6 +73,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "853817f0-d797-4eb2-bf40-2879a9b58117", "status": "PENDING" }, @@ -75,6 +83,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "9e5d5da7-5991-4ac9-9cee-559769912ca5", "status": "PENDING" }, @@ -84,6 +93,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -93,6 +103,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -102,6 +113,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -111,6 +123,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -120,6 +133,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -129,6 +143,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -138,6 +153,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -147,6 +163,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -156,6 +173,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -165,6 +183,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -174,6 +193,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -183,6 +203,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -192,6 +213,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -201,6 +223,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", + "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, diff --git a/specification/api/components/schemas/letterItem.yml b/specification/api/components/schemas/letterItem.yml index a62bd6fdb..a58037b6c 100644 --- a/specification/api/components/schemas/letterItem.yml +++ b/specification/api/components/schemas/letterItem.yml @@ -24,3 +24,5 @@ properties: $ref: "./reasonCode.yml" reasonText: $ref: "./reasonText.yml" + sha256Hash: + $ref: "./sha256Hash.yml" diff --git a/specification/api/components/schemas/sha256Hash.yml b/specification/api/components/schemas/sha256Hash.yml new file mode 100644 index 000000000..a70c5e3c8 --- /dev/null +++ b/specification/api/components/schemas/sha256Hash.yml @@ -0,0 +1,3 @@ +type: string +description: Sha 256 hash of the letter content +example: 2WL5eYSWGzCHlGmzNxuqVusPxDg From d9bb54f3e19625d402d65caf5abfaf784d40a2cf Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 14:00:40 +0100 Subject: [PATCH 20/28] amend update letter queue lambda for sha256Hash --- lambdas/update-letter-queue/src/update-letter-queue.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lambdas/update-letter-queue/src/update-letter-queue.ts b/lambdas/update-letter-queue/src/update-letter-queue.ts index 04ed963ff..cc91a0079 100644 --- a/lambdas/update-letter-queue/src/update-letter-queue.ts +++ b/lambdas/update-letter-queue/src/update-letter-queue.ts @@ -191,6 +191,7 @@ function mapLetterToPendingLetter(letter: Letter): InsertPendingLetter { specificationId: letter.specificationId, groupId: letter.groupId, priority: letter.priority, + sha256Hash: letter.sha256Hash, }; } From 37c16a68c80858c74d24ae23460e8d8be4c173b3 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 14:49:55 +0100 Subject: [PATCH 21/28] use correct hash exmaple --- .../responses/getLetters_pending.json | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/sandbox/data/examples/getLetters/responses/getLetters_pending.json b/sandbox/data/examples/getLetters/responses/getLetters_pending.json index 16b44ac30..20a3ee480 100644 --- a/sandbox/data/examples/getLetters/responses/getLetters_pending.json +++ b/sandbox/data/examples/getLetters/responses/getLetters_pending.json @@ -3,7 +3,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -13,7 +13,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -23,7 +23,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -33,7 +33,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -43,7 +43,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -53,7 +53,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -63,7 +63,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, @@ -73,7 +73,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "853817f0-d797-4eb2-bf40-2879a9b58117", "status": "PENDING" }, @@ -83,7 +83,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "9e5d5da7-5991-4ac9-9cee-559769912ca5", "status": "PENDING" }, @@ -93,7 +93,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -103,7 +103,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -113,7 +113,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -123,7 +123,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -133,7 +133,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -143,7 +143,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -153,7 +153,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -163,7 +163,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -173,7 +173,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -183,7 +183,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -193,7 +193,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -203,7 +203,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -213,7 +213,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, @@ -223,7 +223,7 @@ { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "634bcc87-fcc3-41fe-b6f9-c6a2125758e6", "status": "PENDING" }, From 76b53b4963ee4b40e0a3c1630b393130fc325acc Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 14:55:04 +0100 Subject: [PATCH 22/28] amend sandbox examples to use proper sha256 example --- .../responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- .../responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json index 394d052ff..92bb25f1f 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-24L5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PENDING" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json index b8ab0f83a..bbc41e7b9 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2AL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "ACCEPTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json index cc410c0d0..ae3d1356b 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2BL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "PRINTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json index 48b5b6531..401df416a 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2CL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "ENCLOSED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json index c1f000225..bdd71096d 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2DL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "DISPATCHED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json index 14dfae3f6..672e13cad 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2EL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -2,7 +2,7 @@ "data": { "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "DELIVERED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json index bf480bebc..c9184b3cd 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2WL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,7 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "REJECTED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json index f932e9859..461303361 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2XL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -3,7 +3,7 @@ "attributes": { "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "CANCELLED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json index ce06ba4a8..631871f95 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2YL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,7 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "FAILED" }, diff --git a/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json b/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json index 34e794fc6..d12605718 100644 --- a/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json +++ b/sandbox/data/examples/getLetter/responses/getLetter-2ZL5eYSWGzCHlGmzNxuqVusPxDg.json @@ -4,7 +4,7 @@ "groupId": "c5d93f917f5546d08beccf770a915d96", "reasonCode": "R01", "reasonText": "failed validation", - "sha256Hash": "2WL5eYSWGzCHlGmzNxuqVusPxDg", + "sha256Hash": "3a7bd3e2360a3d29eea436fcfb7e44c735d117c8f2f1d2d1e4f6e8f7e6e8f7e6", "specificationId": "2WL5eYSWGzCHlGmzNxuqVusPxDg", "status": "RETURNED" }, From 6d3e4bfd0bc179a7e0ef18d3b457135c0fd6708d Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 15:52:16 +0100 Subject: [PATCH 23/28] add component tests --- .../get-letter-status.spec.ts | 1 + .../apiGateway-tests/get-letters.spec.ts | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts b/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts index 1e893ef24..33faf7eed 100644 --- a/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts +++ b/tests/component-tests/apiGateway-tests/get-letter-status.spec.ts @@ -39,6 +39,7 @@ test.describe("API Gateway Tests to Verify Get Letter Status Endpoint", () => { expect(responseBody).toMatchObject({ data: { attributes: { + sha256Hash: createdLetter.sha256Hash, status: "PENDING", specificationId: createdLetter.specificationId, groupId: createdLetter.groupId, diff --git a/tests/component-tests/apiGateway-tests/get-letters.spec.ts b/tests/component-tests/apiGateway-tests/get-letters.spec.ts index b48544916..84cd492c5 100644 --- a/tests/component-tests/apiGateway-tests/get-letters.spec.ts +++ b/tests/component-tests/apiGateway-tests/get-letters.spec.ts @@ -10,6 +10,7 @@ import { isErrorResponse, isGetLettersResponse, } from "../../helpers/generate-fetch-test-data"; +import { SUPPLIER_LETTERS } from "../../constants/api-constants"; let baseUrl: string; @@ -34,6 +35,37 @@ test.describe("API Gateway Tests To Get List Of Pending Letters", () => { throw new Error("Expected GetLettersResponse body for 200 status"); } expect(responseBody.data.length).toBeGreaterThanOrEqual(1); + + responseBody.data.forEach((letter) => { + expect(letter.attributes.sha256Hash).toBeDefined(); + expect(letter.attributes.sha256Hash).not.toBeNull(); + }); + }); + + test("GET /letters retrieve letter should match SHA256 from GET /letter{id} of the same letter", async ({ request }) => { + const headers = createValidRequestHeaders(); + const { responseBody, statusCode } = await getLettersWithRetry( + request, + baseUrl, + headers, + { + lettersLimit: "2", + }, + ); + + expect(statusCode).toBe(200); + if (!isGetLettersResponse(responseBody)) { + throw new Error("Expected GetLettersResponse body for 200 status"); + } + expect(responseBody.data.length).toBeGreaterThanOrEqual(1); + const getLettersSha = responseBody.data[0].attributes.sha256Hash; + const id = responseBody.data[0].id; + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { + headers, + }); + const letterResponseBody = await response.json(); + expect(response.status()).toBe(200); + expect(letterResponseBody.data.attributes.sha256Hash).toBe(getLettersSha); }); test("GET /letters with invalid authentication should return 403", async ({ From 0076f5da01d9deceaba666a2bedc48a7c2d98638 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 16:04:02 +0100 Subject: [PATCH 24/28] lint fix --- tests/component-tests/apiGateway-tests/get-letters.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/get-letters.spec.ts b/tests/component-tests/apiGateway-tests/get-letters.spec.ts index 84cd492c5..91e43e5d6 100644 --- a/tests/component-tests/apiGateway-tests/get-letters.spec.ts +++ b/tests/component-tests/apiGateway-tests/get-letters.spec.ts @@ -36,10 +36,10 @@ test.describe("API Gateway Tests To Get List Of Pending Letters", () => { } expect(responseBody.data.length).toBeGreaterThanOrEqual(1); - responseBody.data.forEach((letter) => { + for (const letter of responseBody.data) { expect(letter.attributes.sha256Hash).toBeDefined(); expect(letter.attributes.sha256Hash).not.toBeNull(); - }); + } }); test("GET /letters retrieve letter should match SHA256 from GET /letter{id} of the same letter", async ({ request }) => { From a60cf8159c2a86891c3ad592e2aa1738f21170e3 Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 16:28:56 +0100 Subject: [PATCH 25/28] more lint fixes --- .../component-tests/apiGateway-tests/get-letters.spec.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/get-letters.spec.ts b/tests/component-tests/apiGateway-tests/get-letters.spec.ts index 91e43e5d6..c450a053c 100644 --- a/tests/component-tests/apiGateway-tests/get-letters.spec.ts +++ b/tests/component-tests/apiGateway-tests/get-letters.spec.ts @@ -42,7 +42,7 @@ test.describe("API Gateway Tests To Get List Of Pending Letters", () => { } }); - test("GET /letters retrieve letter should match SHA256 from GET /letter{id} of the same letter", async ({ request }) => { + test("GET /letters and GET /letter{id} sha256 match", async ({ request }) => { const headers = createValidRequestHeaders(); const { responseBody, statusCode } = await getLettersWithRetry( request, @@ -59,10 +59,9 @@ test.describe("API Gateway Tests To Get List Of Pending Letters", () => { } expect(responseBody.data.length).toBeGreaterThanOrEqual(1); const getLettersSha = responseBody.data[0].attributes.sha256Hash; - const id = responseBody.data[0].id; - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${id}`, { - headers, - }); + const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${responseBody.data[0].id}`, { + headers, + }); const letterResponseBody = await response.json(); expect(response.status()).toBe(200); expect(letterResponseBody.data.attributes.sha256Hash).toBe(getLettersSha); From 2d4d790dc567fdcc5d3352a9a02f8646f9eaaf8e Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 16:39:10 +0100 Subject: [PATCH 26/28] yet more lint fixes --- .../component-tests/apiGateway-tests/get-letters.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/component-tests/apiGateway-tests/get-letters.spec.ts b/tests/component-tests/apiGateway-tests/get-letters.spec.ts index c450a053c..bcdd695fc 100644 --- a/tests/component-tests/apiGateway-tests/get-letters.spec.ts +++ b/tests/component-tests/apiGateway-tests/get-letters.spec.ts @@ -59,9 +59,12 @@ test.describe("API Gateway Tests To Get List Of Pending Letters", () => { } expect(responseBody.data.length).toBeGreaterThanOrEqual(1); const getLettersSha = responseBody.data[0].attributes.sha256Hash; - const response = await request.get(`${baseUrl}/${SUPPLIER_LETTERS}/${responseBody.data[0].id}`, { - headers, - }); + const response = await request.get( + `${baseUrl}/${SUPPLIER_LETTERS}/${responseBody.data[0].id}`, + { + headers, + }, + ); const letterResponseBody = await response.json(); expect(response.status()).toBe(200); expect(letterResponseBody.data.attributes.sha256Hash).toBe(getLettersSha); From f65f222d3b220095140ce337fc012063c8dc339f Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Tue, 9 Jun 2026 17:14:43 +0100 Subject: [PATCH 27/28] add e2e test to compute hash of pdf and ensure matches --- .../api/letters/test_get_letter_status.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tests/e2e-tests/api/letters/test_get_letter_status.py b/tests/e2e-tests/api/letters/test_get_letter_status.py index 18e9d97ae..4ad22e62a 100644 --- a/tests/e2e-tests/api/letters/test_get_letter_status.py +++ b/tests/e2e-tests/api/letters/test_get_letter_status.py @@ -1,3 +1,4 @@ +import hashlib import requests import pytest from lib.fixtures import * # NOSONAR @@ -22,6 +23,35 @@ def test_200_get_letter_status(url, authentication_secret): ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" +@pytest.mark.test +@pytest.mark.devtest +@pytest.mark.inttest +@pytest.mark.prodtest +def test_200_get_letter_status_matches_pdf_hash(url, authentication_secret): + headers = Generators.generate_valid_headers(authentication_secret) + + ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) + letter_id = ids[0] + + print(f"calling GET {url}{LETTERS_ENDPOINT}/{letter_id}") + get_message_response = requests.get(f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers) + + ErrorHandler.handle_retry(get_message_response) + expected_sha256 = get_message_response.json().get("data", {}).get("attributes", {}).get("sha256Hash") + + get_pdf_response = requests.get( + f"{url}{LETTERS_ENDPOINT}/{letter_id}/data", + headers=headers, + allow_redirects=True, + ) + downloaded_pdf_sha256 = hashlib.sha256(get_pdf_response.content).hexdigest() + + assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" + assert expected_sha256 is not None, "Expected sha256Hash in GET /letters/{id} response" + assert downloaded_pdf_sha256 == expected_sha256, ( + f"Expected PDF sha256 {expected_sha256}, got {downloaded_pdf_sha256}" + ) + @pytest.mark.test @pytest.mark.devtest @pytest.mark.inttest From fa5e05171fb617656f0c6ff32ea353c9cf8184cf Mon Sep 17 00:00:00 2001 From: "ross.faulds2" Date: Thu, 11 Jun 2026 12:04:44 +0100 Subject: [PATCH 28/28] remove hash test that had no real value --- .../api/letters/test_get_letter_status.py | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/tests/e2e-tests/api/letters/test_get_letter_status.py b/tests/e2e-tests/api/letters/test_get_letter_status.py index 4ad22e62a..d3e0a72f8 100644 --- a/tests/e2e-tests/api/letters/test_get_letter_status.py +++ b/tests/e2e-tests/api/letters/test_get_letter_status.py @@ -23,35 +23,6 @@ def test_200_get_letter_status(url, authentication_secret): ErrorHandler.handle_retry(get_message_response) assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" -@pytest.mark.test -@pytest.mark.devtest -@pytest.mark.inttest -@pytest.mark.prodtest -def test_200_get_letter_status_matches_pdf_hash(url, authentication_secret): - headers = Generators.generate_valid_headers(authentication_secret) - - ids = get_pending_letter_ids(url, headers, LETTERS_ENDPOINT, limit=1) - letter_id = ids[0] - - print(f"calling GET {url}{LETTERS_ENDPOINT}/{letter_id}") - get_message_response = requests.get(f"{url}{LETTERS_ENDPOINT}/{letter_id}", headers=headers) - - ErrorHandler.handle_retry(get_message_response) - expected_sha256 = get_message_response.json().get("data", {}).get("attributes", {}).get("sha256Hash") - - get_pdf_response = requests.get( - f"{url}{LETTERS_ENDPOINT}/{letter_id}/data", - headers=headers, - allow_redirects=True, - ) - downloaded_pdf_sha256 = hashlib.sha256(get_pdf_response.content).hexdigest() - - assert get_message_response.status_code == 200, f"Response: {get_message_response.status_code}: {get_message_response.text}" - assert expected_sha256 is not None, "Expected sha256Hash in GET /letters/{id} response" - assert downloaded_pdf_sha256 == expected_sha256, ( - f"Expected PDF sha256 {expected_sha256}, got {downloaded_pdf_sha256}" - ) - @pytest.mark.test @pytest.mark.devtest @pytest.mark.inttest