Skip to content

Commit 57f7483

Browse files
committed
CCM-12745: change to a class and use in create lambda
1 parent cd45cd8 commit 57f7483

File tree

8 files changed

+263
-154
lines changed

8 files changed

+263
-154
lines changed

infrastructure/terraform/components/dl/module_lambda_ttl_create.tf

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ module "ttl_create" {
3636
log_subscription_role_arn = local.acct.log_subscription_role_arn
3737

3838
lambda_env_vars = {
39-
"TTL_TABLE_NAME" = aws_dynamodb_table.ttl.name
40-
"TTL_WAIT_TIME_HOURS" = 24
41-
"TTL_SHARD_COUNT" = local.ttl_shard_count
39+
"TTL_TABLE_NAME" = aws_dynamodb_table.ttl.name
40+
"TTL_WAIT_TIME_HOURS" = 24
41+
"TTL_SHARD_COUNT" = local.ttl_shard_count
42+
"EVENT_PUBLISH_BUS_ARN" = aws_cloudwatch_event_bus.main.arn
43+
"EVENT_PUBLISH_DLQ_URL" = module.sqs_ttl.dlq_queue_url
4244
}
4345
}
4446

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { $TtlItem } from 'app/ttl-item-validator';
44

55
describe('createHandler', () => {
66
let createTtl: any;
7+
let eventPublisher: any;
78
let logger: any;
89
let handler: any;
910

@@ -23,8 +24,9 @@ describe('createHandler', () => {
2324

2425
beforeEach(() => {
2526
createTtl = { send: jest.fn() };
27+
eventPublisher = { sendEvents: jest.fn() };
2628
logger = { error: jest.fn(), info: jest.fn() };
27-
handler = createHandler({ createTtl, logger });
29+
handler = createHandler({ createTtl, eventPublisher, logger });
2830
});
2931

3032
it('processes a valid SQS event and returns success', async () => {

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@ import type {
33
SQSBatchResponse,
44
SQSEvent,
55
} from 'aws-lambda';
6-
import { Logger } from 'utils';
6+
import { EventPublisher, Logger } from 'utils';
77
import type { CreateTtl, CreateTtlOutcome } from 'app/create-ttl';
88
import { $TtlItem } from 'app/ttl-item-validator';
99

10-
type CreateHandlerDependencies = {
10+
interface CreateHandlerDependencies {
1111
createTtl: CreateTtl;
12+
eventPublisher: EventPublisher;
1213
logger: Logger;
13-
};
14+
}
1415

1516
export const createHandler = ({
1617
createTtl,
18+
eventPublisher,
1719
logger,
1820
}: CreateHandlerDependencies) =>
1921
async function handler(event: SQSEvent): Promise<SQSBatchResponse> {
@@ -37,6 +39,8 @@ export const createHandler = ({
3739

3840
const result = await createTtl.send(item);
3941

42+
eventPublisher.sendEvents([item])
43+
4044
if (result === 'failed') {
4145
batchItemFailures.push({ itemIdentifier: messageId });
4246
}

lambdas/ttl-create-lambda/src/container.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { dynamoClient, logger } from 'utils';
1+
import { dynamoClient, EventPublisher, logger } from 'utils';
22
import { loadConfig } from 'infra/config';
33
import { TtlRepository } from 'infra/ttl-repository';
44
import { CreateTtl } from 'app/create-ttl';
@@ -16,8 +16,15 @@ export const createContainer = () => {
1616

1717
const createTtl = new CreateTtl(requestTtlRepository, logger);
1818

19+
const eventPublisher = new EventPublisher({
20+
eventBusArn: loadConfig().eventPublishBusArn,
21+
dlqUrl: loadConfig().eventPublishDlqUrl,
22+
logger,
23+
});
24+
1925
return {
2026
createTtl,
27+
eventPublisher,
2128
logger,
2229
};
2330
};

lambdas/ttl-create-lambda/src/infra/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ export type TtlCreateConfig = {
44
ttlTableName: string;
55
ttlWaitTimeHours: number;
66
ttlShardCount: number;
7+
eventPublishBusArn: string;
8+
eventPublishDlqUrl: string;
79
};
810

911
export function loadConfig(): TtlCreateConfig {
1012
return {
1113
ttlTableName: defaultConfigReader.getValue('TTL_TABLE_NAME'),
1214
ttlWaitTimeHours: defaultConfigReader.getInt('TTL_WAIT_TIME_HOURS'),
1315
ttlShardCount: defaultConfigReader.getInt('TTL_SHARD_COUNT'),
16+
eventPublishBusArn: defaultConfigReader.getValue('EVENT_PUBLISH_BUS_ARN'),
17+
eventPublishDlqUrl: defaultConfigReader.getValue('EVENT_PUBLISH_DLQ_URL'),
1418
};
1519
}

utils/utils/src/__tests__/event-publish/event-publish.test.ts

Lines changed: 76 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,24 @@ import {
55
} from '@aws-sdk/client-eventbridge';
66
import { mockClient } from 'aws-sdk-client-mock';
77
import { CloudEvent } from 'types';
8+
import { Logger } from 'logger';
89

9-
import { EventPublishConfig, sendEvents } from 'event-publish';
10-
11-
process.env.EVENT_BUS_ARN =
12-
'arn:aws:events:us-east-1:123456789012:event-bus/test-bus';
13-
process.env.DLQ_URL =
14-
'https://sqs.us-east-1.amazonaws.com/123456789012/test-dlq';
10+
import { EventPublishConfig, EventPublisher } from 'event-publish';
1511

1612
const eventBridgeMock = mockClient(EventBridgeClient);
1713
const sqsMock = mockClient(SQSClient);
1814

15+
const mockLogger: Logger = {
16+
info: jest.fn(),
17+
warn: jest.fn(),
18+
error: jest.fn(),
19+
debug: jest.fn(),
20+
} as any;
21+
1922
const testConfig: EventPublishConfig = {
2023
eventBusArn: 'arn:aws:events:us-east-1:123456789012:event-bus/test-bus',
2124
dlqUrl: 'https://sqs.us-east-1.amazonaws.com/123456789012/test-dlq',
25+
logger: mockLogger,
2226
};
2327

2428
const validCloudEvent: CloudEvent = {
@@ -55,10 +59,12 @@ describe('Event Publishing', () => {
5559
beforeEach(() => {
5660
eventBridgeMock.reset();
5761
sqsMock.reset();
62+
jest.clearAllMocks();
5863
});
5964

6065
test('should return empty array when no events provided', async () => {
61-
const result = await sendEvents([], testConfig);
66+
const publisher = new EventPublisher(testConfig);
67+
const result = await publisher.sendEvents([]);
6268

6369
expect(result).toEqual([]);
6470
expect(eventBridgeMock.calls()).toHaveLength(0);
@@ -71,7 +77,8 @@ describe('Event Publishing', () => {
7177
Entries: [{ EventId: 'event-1' }],
7278
});
7379

74-
const result = await sendEvents(validEvents, testConfig);
80+
const publisher = new EventPublisher(testConfig);
81+
const result = await publisher.sendEvents(validEvents);
7582

7683
expect(result).toEqual([]);
7784
expect(eventBridgeMock.calls()).toHaveLength(1);
@@ -105,7 +112,8 @@ describe('Event Publishing', () => {
105112
],
106113
});
107114

108-
const result = await sendEvents(invalidEvents, testConfig);
115+
const publisher = new EventPublisher(testConfig);
116+
const result = await publisher.sendEvents(invalidEvents);
109117

110118
expect(result).toEqual([]);
111119
expect(eventBridgeMock.calls()).toHaveLength(0);
@@ -134,7 +142,8 @@ describe('Event Publishing', () => {
134142
],
135143
});
136144

137-
const result = await sendEvents(mixedEvents, testConfig);
145+
const publisher = new EventPublisher(testConfig);
146+
const result = await publisher.sendEvents(mixedEvents);
138147

139148
expect(result).toEqual([]);
140149
expect(eventBridgeMock.calls()).toHaveLength(1);
@@ -177,7 +186,8 @@ describe('Event Publishing', () => {
177186
],
178187
});
179188

180-
const result = await sendEvents(validEvents, testConfig);
189+
const publisher = new EventPublisher(testConfig);
190+
const result = await publisher.sendEvents(validEvents);
181191

182192
expect(result).toEqual([]);
183193
expect(eventBridgeMock.calls()).toHaveLength(1);
@@ -207,7 +217,8 @@ describe('Event Publishing', () => {
207217
],
208218
});
209219

210-
const result = await sendEvents(validEvents, testConfig);
220+
const publisher = new EventPublisher(testConfig);
221+
const result = await publisher.sendEvents(validEvents);
211222

212223
expect(result).toEqual([]);
213224
expect(eventBridgeMock.calls()).toHaveLength(1);
@@ -229,7 +240,8 @@ describe('Event Publishing', () => {
229240
});
230241
});
231242

232-
const result = await sendEvents(invalidEvents, testConfig);
243+
const publisher = new EventPublisher(testConfig);
244+
const result = await publisher.sendEvents(invalidEvents);
233245

234246
expect(result).toHaveLength(1);
235247
expect(eventBridgeMock.calls()).toHaveLength(0);
@@ -239,7 +251,8 @@ describe('Event Publishing', () => {
239251
test('should handle DLQ send error and return all events as failed', async () => {
240252
sqsMock.on(SendMessageBatchCommand).rejects(new Error('DLQ error'));
241253

242-
const result = await sendEvents(invalidEvents, testConfig);
254+
const publisher = new EventPublisher(testConfig);
255+
const result = await publisher.sendEvents(invalidEvents);
243256

244257
expect(result).toEqual(invalidEvents);
245258
expect(eventBridgeMock.calls()).toHaveLength(0);
@@ -259,7 +272,8 @@ describe('Event Publishing', () => {
259272
Entries: [{ EventId: 'success' }],
260273
});
261274

262-
const result = await sendEvents(largeEventArray, testConfig);
275+
const publisher = new EventPublisher(testConfig);
276+
const result = await publisher.sendEvents(largeEventArray);
263277

264278
expect(result).toEqual([]);
265279
expect(eventBridgeMock.calls()).toHaveLength(3);
@@ -300,7 +314,8 @@ describe('Event Publishing', () => {
300314
subject: 'Patient/12345',
301315
};
302316

303-
await sendEvents([customEvent], testConfig);
317+
const publisher = new EventPublisher(testConfig);
318+
await publisher.sendEvents([customEvent]);
304319

305320
const eventBridgeCall = eventBridgeMock.calls()[0];
306321
const entry = (eventBridgeCall.args[0].input as any).Entries[0];
@@ -324,3 +339,48 @@ describe('Event Publishing', () => {
324339
expect(detailObject.specversion).toBe('1.0');
325340
});
326341
});
342+
343+
describe('EventPublisher Class', () => {
344+
beforeEach(() => {
345+
eventBridgeMock.reset();
346+
sqsMock.reset();
347+
jest.clearAllMocks();
348+
});
349+
350+
test('should throw error when eventBusArn is missing from config', () => {
351+
expect(
352+
() => new EventPublisher({ ...testConfig, eventBusArn: '' }),
353+
).toThrow('eventBusArn is required in config');
354+
});
355+
356+
test('should throw error when dlqUrl is missing from config', () => {
357+
expect(() => new EventPublisher({ ...testConfig, dlqUrl: '' })).toThrow(
358+
'dlqUrl is required in config',
359+
);
360+
});
361+
362+
test('should throw error when logger is missing from config', () => {
363+
expect(
364+
() => new EventPublisher({ ...testConfig, logger: null as any }),
365+
).toThrow('logger is required in config');
366+
});
367+
368+
test('should be reusable for multiple calls', async () => {
369+
eventBridgeMock.on(PutEventsCommand).resolves({
370+
FailedEntryCount: 0,
371+
Entries: [{ EventId: 'event-1' }],
372+
});
373+
374+
const publisher = new EventPublisher(testConfig);
375+
376+
// First call
377+
const result1 = await publisher.sendEvents([validCloudEvent]);
378+
expect(result1).toEqual([]);
379+
380+
// Second call with same publisher instance
381+
const result2 = await publisher.sendEvents([validCloudEvent2]);
382+
expect(result2).toEqual([]);
383+
384+
expect(eventBridgeMock.calls()).toHaveLength(2);
385+
});
386+
});

0 commit comments

Comments
 (0)