Skip to content

Commit e02b34d

Browse files
committed
CCM-12896: Update ttl-create-lambda to use new types/validators
1 parent 39b41f7 commit e02b34d

File tree

11 files changed

+207
-276
lines changed

11 files changed

+207
-276
lines changed

lambdas/ttl-create-lambda/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"dependencies": {
33
"@aws-sdk/lib-dynamodb": "^3.908.0",
4-
"utils": "^0.0.1",
5-
"zod": "^4.1.12"
4+
"digital-letters-events": "^0.0.1",
5+
"utils": "^0.0.1"
66
},
77
"devDependencies": {
88
"@tsconfig/node22": "^22.0.2",

lambdas/ttl-create-lambda/src/__tests__/apis/sqs-trigger-lambda.test.ts

Lines changed: 61 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { createHandler } from 'apis/sqs-trigger-lambda';
22
import type { SQSEvent } from 'aws-lambda';
3-
import { $TtlItemBusEvent, TtlItemBusEvent } from 'utils';
3+
import {
4+
ItemEnqueued,
5+
MESHInboxMessageDownloaded,
6+
} from 'digital-letters-events';
47
import { randomUUID } from 'node:crypto';
58

69
jest.mock('node:crypto', () => ({
@@ -18,35 +21,43 @@ describe('createHandler', () => {
1821
let logger: any;
1922
let handler: any;
2023

21-
const validItem: TtlItemBusEvent = {
22-
detail: {
23-
profileversion: '1.0.0',
24-
profilepublished: '2025-10',
25-
id: '550e8400-e29b-41d4-a716-446655440001',
26-
specversion: '1.0',
27-
source:
28-
'/nhs/england/notify/production/primary/data-plane/digital-letters',
29-
subject:
30-
'customer/920fca11-596a-4eca-9c47-99f624614658/recipient/769acdd4-6a47-496f-999f-76a6fd2c3959',
31-
type: 'uk.nhs.notify.digital.letters.sent.v1',
32-
time: '2023-06-20T12:00:00Z',
33-
recordedtime: '2023-06-20T12:00:00.250Z',
34-
severitynumber: 2,
35-
traceparent: '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
36-
datacontenttype: 'application/json',
37-
dataschema:
38-
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10/digital-letter-base-data.schema.json',
39-
dataschemaversion: '1.0',
40-
severitytext: 'INFO',
41-
data: {
42-
messageUri: 'https://example.com/ttl/resource',
43-
'digital-letter-id': '123e4567-e89b-12d3-a456-426614174000',
44-
messageReference: 'ref1',
45-
senderId: 'sender1',
46-
},
24+
const messageDownloadedEvent: MESHInboxMessageDownloaded = {
25+
id: '550e8400-e29b-41d4-a716-446655440001',
26+
specversion: '1.0',
27+
source:
28+
'/nhs/england/notify/production/primary/data-plane/digitalletters/mesh',
29+
subject:
30+
'customer/920fca11-596a-4eca-9c47-99f624614658/recipient/769acdd4-6a47-496f-999f-76a6fd2c3959',
31+
type: 'uk.nhs.notify.digital.letters.mesh.inbox.message.downloaded.v1',
32+
time: '2023-06-20T12:00:00Z',
33+
recordedtime: '2023-06-20T12:00:00.250Z',
34+
severitynumber: 2,
35+
traceparent: '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
36+
datacontenttype: 'application/json',
37+
dataschema:
38+
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-mesh-inbox-message-downloaded-data.schema.json',
39+
severitytext: 'INFO',
40+
data: {
41+
messageUri: 'https://example.com/ttl/resource',
42+
messageReference: 'ref1',
43+
senderId: 'sender1',
4744
},
4845
};
4946

47+
const eventBusEvent = {
48+
detail: messageDownloadedEvent,
49+
};
50+
51+
const itemEnqueuedEvent: ItemEnqueued = {
52+
...messageDownloadedEvent,
53+
id: '550e8400-e29b-41d4-a716-446655440001',
54+
time: '2023-06-20T12:00:00.250Z',
55+
recordedtime: '2023-06-20T12:00:00.250Z',
56+
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
57+
dataschema:
58+
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-queue-item-enqueued-data.schema.json',
59+
};
60+
5061
beforeEach(() => {
5162
createTtl = { send: jest.fn() };
5263
eventPublisher = { sendEvents: jest.fn().mockResolvedValue([]) };
@@ -55,27 +66,16 @@ describe('createHandler', () => {
5566
});
5667

5768
it('processes a valid SQS event and returns success', async () => {
58-
jest
59-
.spyOn($TtlItemBusEvent, 'safeParse')
60-
.mockReturnValue({ success: true, data: validItem });
6169
createTtl.send.mockResolvedValue('sent');
6270
const event: SQSEvent = {
63-
Records: [{ body: JSON.stringify(validItem), messageId: 'msg1' }],
71+
Records: [{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' }],
6472
} as any;
6573

6674
const res = await handler(event);
6775

6876
expect(res.batchItemFailures).toEqual([]);
69-
expect(createTtl.send).toHaveBeenCalledWith(validItem.detail);
70-
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([
71-
{
72-
...validItem.detail,
73-
id: '550e8400-e29b-41d4-a716-446655440001',
74-
time: '2023-06-20T12:00:00.250Z',
75-
recordedtime: '2023-06-20T12:00:00.250Z',
76-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
77-
},
78-
]);
77+
expect(createTtl.send).toHaveBeenCalledWith(messageDownloadedEvent);
78+
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([itemEnqueuedEvent]);
7979
expect(logger.info).toHaveBeenCalledWith({
8080
description: 'Processed SQS Event.',
8181
failed: 0,
@@ -85,10 +85,6 @@ describe('createHandler', () => {
8585
});
8686

8787
it('handles parse failure and logs error', async () => {
88-
const zodError = { errors: [] } as any;
89-
jest
90-
.spyOn($TtlItemBusEvent, 'safeParse')
91-
.mockReturnValue({ success: false, error: zodError });
9288
const event: SQSEvent = {
9389
Records: [{ body: '{}', messageId: 'msg2' }],
9490
} as any;
@@ -110,12 +106,9 @@ describe('createHandler', () => {
110106
});
111107

112108
it('handles createTtl.send failure', async () => {
113-
jest
114-
.spyOn($TtlItemBusEvent, 'safeParse')
115-
.mockReturnValue({ success: true, data: validItem });
116109
createTtl.send.mockResolvedValue('failed');
117110
const event: SQSEvent = {
118-
Records: [{ body: JSON.stringify(validItem), messageId: 'msg3' }],
111+
Records: [{ body: JSON.stringify(eventBusEvent), messageId: 'msg3' }],
119112
} as any;
120113

121114
const res = await handler(event);
@@ -130,11 +123,9 @@ describe('createHandler', () => {
130123
});
131124

132125
it('handles thrown error and logs', async () => {
133-
jest.spyOn($TtlItemBusEvent, 'safeParse').mockImplementation(() => {
134-
throw new Error('bad json');
135-
});
126+
createTtl.send.mockRejectedValue(new Error('TTL service error'));
136127
const event: SQSEvent = {
137-
Records: [{ body: '{}', messageId: 'msg4' }],
128+
Records: [{ body: JSON.stringify(eventBusEvent), messageId: 'msg4' }],
138129
} as any;
139130

140131
const res = await handler(event);
@@ -181,15 +172,12 @@ describe('createHandler', () => {
181172
});
182173

183174
it('processes multiple successful events and sends them as a batch', async () => {
184-
jest
185-
.spyOn($TtlItemBusEvent, 'safeParse')
186-
.mockReturnValue({ success: true, data: validItem });
187175
createTtl.send.mockResolvedValue('sent');
188176
const sqsEvent: SQSEvent = {
189177
Records: [
190-
{ body: JSON.stringify(validItem), messageId: 'msg1' },
191-
{ body: JSON.stringify(validItem), messageId: 'msg2' },
192-
{ body: JSON.stringify(validItem), messageId: 'msg3' },
178+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' },
179+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg2' },
180+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg3' },
193181
],
194182
} as any;
195183

@@ -198,27 +186,9 @@ describe('createHandler', () => {
198186
expect(res.batchItemFailures).toEqual([]);
199187
expect(createTtl.send).toHaveBeenCalledTimes(3);
200188
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([
201-
{
202-
...validItem.detail,
203-
id: '550e8400-e29b-41d4-a716-446655440001',
204-
time: '2023-06-20T12:00:00.250Z',
205-
recordedtime: '2023-06-20T12:00:00.250Z',
206-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
207-
},
208-
{
209-
...validItem.detail,
210-
id: '550e8400-e29b-41d4-a716-446655440001',
211-
time: '2023-06-20T12:00:00.250Z',
212-
recordedtime: '2023-06-20T12:00:00.250Z',
213-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
214-
},
215-
{
216-
...validItem.detail,
217-
id: '550e8400-e29b-41d4-a716-446655440001',
218-
time: '2023-06-20T12:00:00.250Z',
219-
recordedtime: '2023-06-20T12:00:00.250Z',
220-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
221-
},
189+
itemEnqueuedEvent,
190+
itemEnqueuedEvent,
191+
itemEnqueuedEvent,
222192
]);
223193
expect(logger.info).toHaveBeenCalledWith({
224194
description: 'Processed SQS Event.',
@@ -229,38 +199,23 @@ describe('createHandler', () => {
229199
});
230200

231201
it('handles partial event publishing failures and logs warning', async () => {
232-
jest
233-
.spyOn($TtlItemBusEvent, 'safeParse')
234-
.mockReturnValue({ success: true, data: validItem });
235202
createTtl.send.mockResolvedValue('sent');
236-
const failedEvents = [validItem];
203+
const failedEvents = [messageDownloadedEvent];
237204
eventPublisher.sendEvents.mockResolvedValue(failedEvents);
238205

239206
const event: SQSEvent = {
240207
Records: [
241-
{ body: JSON.stringify(validItem), messageId: 'msg1' },
242-
{ body: JSON.stringify(validItem), messageId: 'msg2' },
208+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' },
209+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg2' },
243210
],
244211
} as any;
245212

246213
const res = await handler(event);
247214

248215
expect(res.batchItemFailures).toEqual([]);
249216
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([
250-
{
251-
...validItem.detail,
252-
id: '550e8400-e29b-41d4-a716-446655440001',
253-
time: '2023-06-20T12:00:00.250Z',
254-
recordedtime: '2023-06-20T12:00:00.250Z',
255-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
256-
},
257-
{
258-
...validItem.detail,
259-
id: '550e8400-e29b-41d4-a716-446655440001',
260-
time: '2023-06-20T12:00:00.250Z',
261-
recordedtime: '2023-06-20T12:00:00.250Z',
262-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
263-
},
217+
itemEnqueuedEvent,
218+
itemEnqueuedEvent,
264219
]);
265220
expect(logger.warn).toHaveBeenCalledWith({
266221
description: 'Some events failed to publish',
@@ -270,29 +225,18 @@ describe('createHandler', () => {
270225
});
271226

272227
it('handles event publishing exception and logs warning', async () => {
273-
jest
274-
.spyOn($TtlItemBusEvent, 'safeParse')
275-
.mockReturnValue({ success: true, data: validItem });
276228
createTtl.send.mockResolvedValue('sent');
277229
const publishError = new Error('EventBridge error');
278230
eventPublisher.sendEvents.mockRejectedValue(publishError);
279231

280232
const event: SQSEvent = {
281-
Records: [{ body: JSON.stringify(validItem), messageId: 'msg1' }],
233+
Records: [{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' }],
282234
} as any;
283235

284236
const res = await handler(event);
285237

286238
expect(res.batchItemFailures).toEqual([]);
287-
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([
288-
{
289-
...validItem.detail,
290-
id: '550e8400-e29b-41d4-a716-446655440001',
291-
time: '2023-06-20T12:00:00.250Z',
292-
recordedtime: '2023-06-20T12:00:00.250Z',
293-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
294-
},
295-
]);
239+
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([itemEnqueuedEvent]);
296240
expect(logger.warn).toHaveBeenCalledWith({
297241
err: publishError,
298242
description: 'Failed to send events to EventBridge',
@@ -301,13 +245,10 @@ describe('createHandler', () => {
301245
});
302246

303247
it('does not call eventPublisher when no successful events', async () => {
304-
jest
305-
.spyOn($TtlItemBusEvent, 'safeParse')
306-
.mockReturnValue({ success: true, data: validItem });
307248
createTtl.send.mockResolvedValue('failed');
308249

309250
const event: SQSEvent = {
310-
Records: [{ body: JSON.stringify(validItem), messageId: 'msg1' }],
251+
Records: [{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' }],
311252
} as any;
312253

313254
const res = await handler(event);
@@ -323,20 +264,15 @@ describe('createHandler', () => {
323264
});
324265

325266
it('handles mixed success and failure scenarios', async () => {
326-
jest
327-
.spyOn($TtlItemBusEvent, 'safeParse')
328-
.mockReturnValueOnce({ success: true, data: validItem })
329-
.mockReturnValueOnce({ success: false, error: { errors: [] } as any })
330-
.mockReturnValueOnce({ success: true, data: validItem });
331267
createTtl.send
332268
.mockResolvedValueOnce('sent')
333269
.mockResolvedValueOnce('failed');
334270

335271
const event: SQSEvent = {
336272
Records: [
337-
{ body: JSON.stringify(validItem), messageId: 'msg1' },
273+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg1' },
338274
{ body: '{}', messageId: 'msg2' },
339-
{ body: JSON.stringify(validItem), messageId: 'msg3' },
275+
{ body: JSON.stringify(eventBusEvent), messageId: 'msg3' },
340276
],
341277
} as any;
342278

@@ -346,15 +282,7 @@ describe('createHandler', () => {
346282
{ itemIdentifier: 'msg2' },
347283
{ itemIdentifier: 'msg3' },
348284
]);
349-
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([
350-
{
351-
...validItem.detail,
352-
id: '550e8400-e29b-41d4-a716-446655440001',
353-
time: '2023-06-20T12:00:00.250Z',
354-
recordedtime: '2023-06-20T12:00:00.250Z',
355-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
356-
},
357-
]);
285+
expect(eventPublisher.sendEvents).toHaveBeenCalledWith([itemEnqueuedEvent]);
358286
expect(logger.info).toHaveBeenCalledWith({
359287
description: 'Processed SQS Event.',
360288
failed: 2,

lambdas/ttl-create-lambda/src/__tests__/app/create-ttl.test.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,27 @@
11
import { CreateTtl } from 'app/create-ttl';
22
import { TtlRepository } from 'infra/ttl-repository';
3-
import { TtlItemEvent } from 'utils';
3+
import { MESHInboxMessageDownloaded } from 'digital-letters-events';
44

55
describe('CreateTtl', () => {
66
let repo: jest.Mocked<TtlRepository>;
77
let logger: any;
88
let createTtl: CreateTtl;
9-
const item: TtlItemEvent = {
10-
profileversion: '1.0.0',
11-
profilepublished: '2025-10',
9+
const item: MESHInboxMessageDownloaded = {
1210
id: '550e8400-e29b-41d4-a716-446655440001',
1311
specversion: '1.0',
1412
source: '/nhs/england/notify/production/primary/data-plane/digital-letters',
1513
subject:
1614
'customer/920fca11-596a-4eca-9c47-99f624614658/recipient/769acdd4-6a47-496f-999f-76a6fd2c3959',
17-
type: 'uk.nhs.notify.digital.letters.queue.item.enqueued.v1',
15+
type: 'uk.nhs.notify.digital.letters.mesh.inbox.message.downloaded.v1',
1816
time: '2023-06-20T12:00:00Z',
1917
recordedtime: '2023-06-20T12:00:00.250Z',
2018
severitynumber: 2,
2119
traceparent: '00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01',
2220
datacontenttype: 'application/json',
2321
dataschema:
24-
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10/digital-letter-base-data.schema.json',
25-
dataschemaversion: '1.0',
22+
'https://notify.nhs.uk/cloudevents/schemas/digital-letters/2025-10-draft/data/digital-letters-mesh-inbox-message-downloaded-data.schema.json',
2623
severitytext: 'INFO',
2724
data: {
28-
'digital-letter-id': '123e4567-e89b-12d3-a456-426614174000',
2925
messageReference: 'ref1',
3026
senderId: 'sender1',
3127
messageUri: 'https://example.com/ttl/resource',

0 commit comments

Comments
 (0)