@squiz/dx-common-lib 1.72.1 → 1.72.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEventStreamHandler = createEventStreamHandler;
4
+ /**
5
+ * DynamoDB stream Lambda handler factory for mapping stream records to domain events and publishing via the event bus.
6
+ *
7
+ * Handles:
8
+ * - Record batching by configurable size
9
+ * - Tenant extraction from stream ARN and/or stream record (via `extractTenantId`)
10
+ * - Entity type detection via prefix matching
11
+ * - Mapper invocation for known entity types
12
+ * - Event grouping by tenant
13
+ * - Batch event publishing with retry tracking
14
+ * - Failure reporting for Lambda retry mechanism
15
+ */
16
+ function createEventStreamHandler(config) {
17
+ const { logger, eventBusService, tableServiceIdentifier, entityTypePrefixMap, mappers, extractTenantId, batchSize = 10, } = config;
18
+ function extractEntityType(record) {
19
+ var _a, _b, _c;
20
+ const pk = (_c = (_b = (_a = record.dynamodb) === null || _a === void 0 ? void 0 : _a.Keys) === null || _b === void 0 ? void 0 : _b.pk) === null || _c === void 0 ? void 0 : _c.S;
21
+ if (!pk) {
22
+ return undefined;
23
+ }
24
+ for (const [prefix, entityType] of Object.entries(entityTypePrefixMap)) {
25
+ if (pk.startsWith(prefix)) {
26
+ return entityType;
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ return async function handler(event, context) {
32
+ logger.info('Processing DynamoDB stream batch', {
33
+ recordCount: event.Records.length,
34
+ requestId: context.awsRequestId,
35
+ service: tableServiceIdentifier,
36
+ });
37
+ const mappedRecords = [];
38
+ // Process records in batches
39
+ for (let i = 0; i < event.Records.length; i += batchSize) {
40
+ const batch = event.Records.slice(i, i + batchSize);
41
+ const batchResults = await Promise.all(batch.map(async (record, batchIndex) => {
42
+ var _a, _b, _c, _d, _e, _f;
43
+ try {
44
+ if (!record.eventSourceARN) {
45
+ throw new Error('Missing eventSourceARN on stream record');
46
+ }
47
+ const tenantId = extractTenantId(record.eventSourceARN, tableServiceIdentifier, record);
48
+ const tableName = record.eventSourceARN.split('/')[1];
49
+ const entityType = extractEntityType(record);
50
+ logger.info('Processed stream record', {
51
+ eventName: record.eventName,
52
+ tenantId,
53
+ tableName,
54
+ entityType,
55
+ pk: (_c = (_b = (_a = record.dynamodb) === null || _a === void 0 ? void 0 : _a.Keys) === null || _b === void 0 ? void 0 : _b.pk) === null || _c === void 0 ? void 0 : _c.S,
56
+ sk: (_f = (_e = (_d = record.dynamodb) === null || _d === void 0 ? void 0 : _d.Keys) === null || _e === void 0 ? void 0 : _e.sk) === null || _f === void 0 ? void 0 : _f.S,
57
+ });
58
+ if (entityType !== undefined) {
59
+ const mapper = mappers[entityType];
60
+ if (mapper) {
61
+ // Note: For CMP, mappers return 0 or 1 event per record
62
+ const events = await mapper(record);
63
+ return { recordIndex: i + batchIndex, record, tenantId, events };
64
+ }
65
+ else {
66
+ logger.debug(`No event mapper registered for entity type: ${entityType}`);
67
+ return { recordIndex: i + batchIndex, record, tenantId, events: [] };
68
+ }
69
+ }
70
+ return { recordIndex: i + batchIndex, record, tenantId, events: [] };
71
+ }
72
+ catch (error) {
73
+ logger.error('Failed to process record', {
74
+ error: error instanceof Error ? error.message : String(error),
75
+ eventSourceARN: record.eventSourceARN,
76
+ });
77
+ return { recordIndex: i + batchIndex, record, tenantId: 'unknown', events: [] };
78
+ }
79
+ }));
80
+ mappedRecords.push(...batchResults);
81
+ }
82
+ // Group events by tenant and publish separately for each tenant
83
+ const eventsByTenant = new Map();
84
+ for (const { recordIndex, record, tenantId, events } of mappedRecords) {
85
+ if (events.length > 0) {
86
+ const existing = eventsByTenant.get(tenantId) || [];
87
+ existing.push({ recordIndex, record, events });
88
+ eventsByTenant.set(tenantId, existing);
89
+ }
90
+ }
91
+ // Track failed records for retry
92
+ const failedRecordIndices = new Set();
93
+ // Publish batches for each tenant with their own context in parallel
94
+ await Promise.all(Array.from(eventsByTenant.entries()).map(async ([tenantId, recordsWithEvents]) => {
95
+ var _a;
96
+ const events = recordsWithEvents.flatMap((r) => r.events);
97
+ logger.info('Publishing batch of events for tenant', { tenantId, eventCount: events.length });
98
+ try {
99
+ eventBusService.setTenantId(tenantId);
100
+ await eventBusService.publishEvents(events);
101
+ logger.info('Successfully published all events for tenant', { tenantId });
102
+ }
103
+ catch (error) {
104
+ logger.error('Failed to publish events for tenant', {
105
+ tenantId,
106
+ error: error instanceof Error ? error.message : String(error),
107
+ });
108
+ // Mark all records for this tenant as failed
109
+ for (const { record } of recordsWithEvents) {
110
+ const sequenceNumber = (_a = record.dynamodb) === null || _a === void 0 ? void 0 : _a.SequenceNumber;
111
+ if (sequenceNumber) {
112
+ failedRecordIndices.add(sequenceNumber);
113
+ }
114
+ }
115
+ }
116
+ }));
117
+ const batchItemFailures = Array.from(failedRecordIndices).map((sequenceNumber) => ({
118
+ itemIdentifier: sequenceNumber,
119
+ }));
120
+ logger.info('Batch processing complete', {
121
+ totalRecords: event.Records.length,
122
+ failedRecords: batchItemFailures.length,
123
+ service: tableServiceIdentifier,
124
+ });
125
+ return { batchItemFailures };
126
+ };
127
+ }
128
+ //# sourceMappingURL=EventStreamHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStreamHandler.js","sourceRoot":"","sources":["../../src/stream/EventStreamHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,364 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /*!
4
+ * @license
5
+ * Copyright Squiz Australia Pty Ltd. All Rights Reserved.
6
+ */
7
+ const EventStreamHandler_1 = require("./EventStreamHandler");
8
+ describe('EventStreamHandler', () => {
9
+ let mockLogger;
10
+ let mockEventBusService;
11
+ beforeEach(() => {
12
+ mockLogger = {
13
+ info: jest.fn(),
14
+ debug: jest.fn(),
15
+ error: jest.fn(),
16
+ };
17
+ mockEventBusService = {
18
+ setTenantId: jest.fn(),
19
+ publishEvents: jest.fn().mockResolvedValue(undefined),
20
+ };
21
+ });
22
+ const createMockRecord = (overrides = {}) => {
23
+ return {
24
+ eventID: '1',
25
+ eventVersion: '1.0',
26
+ dynamodb: {
27
+ Keys: {
28
+ pk: { S: 'cv#component-123' },
29
+ sk: { S: 'v#1.0.0' },
30
+ },
31
+ SequenceNumber: '111',
32
+ SizeBytes: 26,
33
+ StreamViewType: 'NEW_AND_OLD_IMAGES',
34
+ },
35
+ awsRegion: 'us-east-1',
36
+ eventName: 'INSERT',
37
+ eventSource: 'aws:dynamodb',
38
+ eventSourceARN: 'arn:aws:dynamodb:us-east-1:123456789012:table/dev.cmp-service.tenant123/stream/2024-01-01T00:00:00.000',
39
+ ...overrides,
40
+ };
41
+ };
42
+ const createMockContext = () => ({
43
+ awsRequestId: 'test-request-id',
44
+ invokeid: 'test-invoke-id',
45
+ logGroupName: 'test-log-group',
46
+ logStreamName: 'test-log-stream',
47
+ functionName: 'test-function',
48
+ memoryLimitInMB: '128',
49
+ functionVersion: '1',
50
+ getRemainingTimeInMillis: jest.fn(() => 30000),
51
+ done: jest.fn(),
52
+ fail: jest.fn(),
53
+ succeed: jest.fn(),
54
+ });
55
+ describe('createEventStreamHandler', () => {
56
+ it('should successfully process records with mapped events', async () => {
57
+ const mockMapper = jest.fn(async () => [
58
+ { detailType: 'component.change.create', detail: { name: 'test' } },
59
+ ]);
60
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
61
+ logger: mockLogger,
62
+ eventBusService: mockEventBusService,
63
+ tableServiceIdentifier: 'cmp',
64
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
65
+ mappers: { componentVersion: mockMapper },
66
+ extractTenantId: (arn, _identifier) => {
67
+ const tableName = arn.split('/')[1];
68
+ const parts = tableName.split('.');
69
+ return parts[parts.length - 1];
70
+ },
71
+ batchSize: 10,
72
+ });
73
+ const event = {
74
+ Records: [createMockRecord()],
75
+ };
76
+ const response = await handle(event, createMockContext());
77
+ expect(mockLogger.info).toHaveBeenCalledWith('Processing DynamoDB stream batch', expect.any(Object));
78
+ expect(mockEventBusService.setTenantId).toHaveBeenCalledWith('tenant123');
79
+ expect(mockEventBusService.publishEvents).toHaveBeenCalledWith([
80
+ { detailType: 'component.change.create', detail: { name: 'test' } },
81
+ ]);
82
+ expect(response.batchItemFailures).toHaveLength(0);
83
+ });
84
+ it('should pass the stream record as the third argument to extractTenantId', async () => {
85
+ const extractTenantId = jest.fn((arn, _identifier, _record) => {
86
+ const tableName = arn.split('/')[1];
87
+ const parts = tableName.split('.');
88
+ return parts[parts.length - 1];
89
+ });
90
+ const mockMapper = jest.fn(async () => [{ detailType: 'component.change.create', detail: {} }]);
91
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
92
+ logger: mockLogger,
93
+ eventBusService: mockEventBusService,
94
+ tableServiceIdentifier: 'cmp',
95
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
96
+ mappers: { componentVersion: mockMapper },
97
+ extractTenantId,
98
+ });
99
+ const streamRecord = createMockRecord();
100
+ await handle({ Records: [streamRecord] }, createMockContext());
101
+ expect(extractTenantId).toHaveBeenCalledWith(streamRecord.eventSourceARN, 'cmp', streamRecord);
102
+ });
103
+ it('should handle records with no mapper registered', async () => {
104
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
105
+ logger: mockLogger,
106
+ eventBusService: mockEventBusService,
107
+ tableServiceIdentifier: 'cmp',
108
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
109
+ mappers: {},
110
+ extractTenantId: (arn, _identifier) => {
111
+ const tableName = arn.split('/')[1];
112
+ const parts = tableName.split('.');
113
+ return parts[parts.length - 1];
114
+ },
115
+ });
116
+ const event = {
117
+ Records: [createMockRecord()],
118
+ };
119
+ const response = await handle(event, createMockContext());
120
+ expect(mockLogger.debug).toHaveBeenCalledWith('No event mapper registered for entity type: componentVersion');
121
+ expect(mockEventBusService.publishEvents).not.toHaveBeenCalled();
122
+ expect(response.batchItemFailures).toHaveLength(0);
123
+ });
124
+ it('should handle records with unknown entity types', async () => {
125
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
126
+ logger: mockLogger,
127
+ eventBusService: mockEventBusService,
128
+ tableServiceIdentifier: 'cmp',
129
+ entityTypePrefixMap: { 'cs#': 'componentSet' },
130
+ mappers: {},
131
+ extractTenantId: (arn, _identifier) => {
132
+ const tableName = arn.split('/')[1];
133
+ const parts = tableName.split('.');
134
+ return parts[parts.length - 1];
135
+ },
136
+ });
137
+ const event = {
138
+ Records: [createMockRecord()],
139
+ };
140
+ const response = await handle(event, createMockContext());
141
+ expect(mockEventBusService.publishEvents).not.toHaveBeenCalled();
142
+ expect(response.batchItemFailures).toHaveLength(0);
143
+ });
144
+ it('should handle records without eventSourceARN', async () => {
145
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
146
+ logger: mockLogger,
147
+ eventBusService: mockEventBusService,
148
+ tableServiceIdentifier: 'cmp',
149
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
150
+ mappers: {},
151
+ extractTenantId: (arn, _identifier) => {
152
+ const tableName = arn.split('/')[1];
153
+ const parts = tableName.split('.');
154
+ return parts[parts.length - 1];
155
+ },
156
+ });
157
+ const event = {
158
+ Records: [createMockRecord({ eventSourceARN: undefined })],
159
+ };
160
+ const response = await handle(event, createMockContext());
161
+ expect(mockLogger.error).toHaveBeenCalledWith('Failed to process record', expect.any(Object));
162
+ expect(response.batchItemFailures).toHaveLength(0);
163
+ });
164
+ it('should batch records according to batchSize', async () => {
165
+ const mockMapper = jest.fn(async () => [{ detailType: 'component.change.create', detail: {} }]);
166
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
167
+ logger: mockLogger,
168
+ eventBusService: mockEventBusService,
169
+ tableServiceIdentifier: 'cmp',
170
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
171
+ mappers: { componentVersion: mockMapper },
172
+ extractTenantId: (arn, _identifier) => {
173
+ const tableName = arn.split('/')[1];
174
+ const parts = tableName.split('.');
175
+ return parts[parts.length - 1];
176
+ },
177
+ batchSize: 2,
178
+ });
179
+ const records = [
180
+ createMockRecord({ eventID: '1' }),
181
+ createMockRecord({ eventID: '2' }),
182
+ createMockRecord({ eventID: '3' }),
183
+ ];
184
+ const event = { Records: records };
185
+ await handle(event, createMockContext());
186
+ expect(mockMapper).toHaveBeenCalledTimes(3);
187
+ });
188
+ it('should mark records as failed when event publishing fails', async () => {
189
+ const mockMapper = jest.fn(async () => [
190
+ { detailType: 'component.change.create', detail: { name: 'test' } },
191
+ ]);
192
+ mockEventBusService.publishEvents.mockRejectedValue(new Error('Publishing failed'));
193
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
194
+ logger: mockLogger,
195
+ eventBusService: mockEventBusService,
196
+ tableServiceIdentifier: 'cmp',
197
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
198
+ mappers: { componentVersion: mockMapper },
199
+ extractTenantId: (arn, _identifier) => {
200
+ const tableName = arn.split('/')[1];
201
+ const parts = tableName.split('.');
202
+ return parts[parts.length - 1];
203
+ },
204
+ });
205
+ const event = {
206
+ Records: [createMockRecord()],
207
+ };
208
+ const response = await handle(event, createMockContext());
209
+ expect(mockLogger.error).toHaveBeenCalledWith('Failed to publish events for tenant', expect.any(Object));
210
+ expect(response.batchItemFailures).toHaveLength(1);
211
+ expect(response.batchItemFailures[0].itemIdentifier).toBe('111');
212
+ });
213
+ it('should handle mapper returning empty events', async () => {
214
+ const mockMapper = jest.fn(async () => []);
215
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
216
+ logger: mockLogger,
217
+ eventBusService: mockEventBusService,
218
+ tableServiceIdentifier: 'cmp',
219
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
220
+ mappers: { componentVersion: mockMapper },
221
+ extractTenantId: (arn, _identifier) => {
222
+ const tableName = arn.split('/')[1];
223
+ const parts = tableName.split('.');
224
+ return parts[parts.length - 1];
225
+ },
226
+ });
227
+ const event = {
228
+ Records: [createMockRecord()],
229
+ };
230
+ const response = await handle(event, createMockContext());
231
+ expect(mockEventBusService.publishEvents).not.toHaveBeenCalled();
232
+ expect(response.batchItemFailures).toHaveLength(0);
233
+ });
234
+ it('should handle multiple mappers for different entity types', async () => {
235
+ const cvMapper = jest.fn(async () => [
236
+ { detailType: 'component.change.create', detail: { type: 'cv' } },
237
+ ]);
238
+ const csMapper = jest.fn(async () => [{ detailType: 'set.change.create', detail: { type: 'cs' } }]);
239
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
240
+ logger: mockLogger,
241
+ eventBusService: mockEventBusService,
242
+ tableServiceIdentifier: 'cmp',
243
+ entityTypePrefixMap: { 'cv#': 'componentVersion', 'cs#': 'componentSet' },
244
+ mappers: { componentVersion: cvMapper, componentSet: csMapper },
245
+ extractTenantId: (arn, _identifier) => {
246
+ const tableName = arn.split('/')[1];
247
+ const parts = tableName.split('.');
248
+ return parts[parts.length - 1];
249
+ },
250
+ });
251
+ const event = {
252
+ Records: [
253
+ createMockRecord({ eventID: '1' }),
254
+ createMockRecord({ eventID: '2', dynamodb: { Keys: { pk: { S: 'cs#set-123' } } } }),
255
+ ],
256
+ };
257
+ await handle(event, createMockContext());
258
+ expect(cvMapper).toHaveBeenCalledTimes(1);
259
+ expect(csMapper).toHaveBeenCalledTimes(1);
260
+ });
261
+ it('should handle mapper errors gracefully', async () => {
262
+ const mockMapper = jest.fn(async () => {
263
+ throw new Error('Mapper failed');
264
+ });
265
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
266
+ logger: mockLogger,
267
+ eventBusService: mockEventBusService,
268
+ tableServiceIdentifier: 'cmp',
269
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
270
+ mappers: { componentVersion: mockMapper },
271
+ extractTenantId: (arn, _identifier) => {
272
+ const tableName = arn.split('/')[1];
273
+ const parts = tableName.split('.');
274
+ return parts[parts.length - 1];
275
+ },
276
+ });
277
+ const event = {
278
+ Records: [createMockRecord()],
279
+ };
280
+ const response = await handle(event, createMockContext());
281
+ expect(mockLogger.error).toHaveBeenCalledWith('Failed to process record', expect.any(Object));
282
+ expect(response.batchItemFailures).toHaveLength(0);
283
+ });
284
+ it('should group events by tenant and publish separately', async () => {
285
+ const mockMapper = jest.fn(async () => [{ detailType: 'component.change.create', detail: {} }]);
286
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
287
+ logger: mockLogger,
288
+ eventBusService: mockEventBusService,
289
+ tableServiceIdentifier: 'cmp',
290
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
291
+ mappers: { componentVersion: mockMapper },
292
+ extractTenantId: (arn) => {
293
+ if (arn.includes('tenant1'))
294
+ return 'tenant1';
295
+ if (arn.includes('tenant2'))
296
+ return 'tenant2';
297
+ return 'unknown';
298
+ },
299
+ });
300
+ const event = {
301
+ Records: [
302
+ createMockRecord({
303
+ eventID: '1',
304
+ eventSourceARN: 'arn:aws:dynamodb:us-east-1:123456789012:table/dev.cmp-service.tenant1/stream/ts',
305
+ }),
306
+ createMockRecord({
307
+ eventID: '2',
308
+ eventSourceARN: 'arn:aws:dynamodb:us-east-1:123456789012:table/dev.cmp-service.tenant2/stream/ts',
309
+ }),
310
+ ],
311
+ };
312
+ await handle(event, createMockContext());
313
+ expect(mockEventBusService.setTenantId).toHaveBeenCalledWith('tenant1');
314
+ expect(mockEventBusService.setTenantId).toHaveBeenCalledWith('tenant2');
315
+ expect(mockEventBusService.publishEvents).toHaveBeenCalledTimes(2);
316
+ });
317
+ it('should handle records without primary key', async () => {
318
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
319
+ logger: mockLogger,
320
+ eventBusService: mockEventBusService,
321
+ tableServiceIdentifier: 'cmp',
322
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
323
+ mappers: {},
324
+ extractTenantId: (arn, _identifier) => {
325
+ const tableName = arn.split('/')[1];
326
+ const parts = tableName.split('.');
327
+ return parts[parts.length - 1];
328
+ },
329
+ });
330
+ const event = {
331
+ Records: [createMockRecord({ dynamodb: { SequenceNumber: '111', SizeBytes: 0 } })],
332
+ };
333
+ const response = await handle(event, createMockContext());
334
+ expect(response.batchItemFailures).toHaveLength(0);
335
+ });
336
+ it('should log batch processing summary', async () => {
337
+ const mockMapper = jest.fn(async () => [{ detailType: 'component.change.create', detail: {} }]);
338
+ const handle = (0, EventStreamHandler_1.createEventStreamHandler)({
339
+ logger: mockLogger,
340
+ eventBusService: mockEventBusService,
341
+ tableServiceIdentifier: 'cmp',
342
+ entityTypePrefixMap: { 'cv#': 'componentVersion' },
343
+ mappers: { componentVersion: mockMapper },
344
+ extractTenantId: (arn, _identifier) => {
345
+ const tableName = arn.split('/')[1];
346
+ const parts = tableName.split('.');
347
+ return parts[parts.length - 1];
348
+ },
349
+ });
350
+ const event = {
351
+ Records: [createMockRecord()],
352
+ };
353
+ await handle(event, createMockContext());
354
+ const finalLog = mockLogger.info.mock.calls.find((call) => call[0] === 'Batch processing complete');
355
+ expect(finalLog).toBeDefined();
356
+ expect(finalLog[1]).toEqual({
357
+ totalRecords: 1,
358
+ failedRecords: 0,
359
+ service: 'cmp',
360
+ });
361
+ });
362
+ });
363
+ });
364
+ //# sourceMappingURL=EventStreamHandler.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventStreamHandler.spec.js","sourceRoot":"","sources":["../../src/stream/EventStreamHandler.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /*!
2
+ * @license
3
+ * Copyright Squiz Australia Pty Ltd. All Rights Reserved.
4
+ */
5
+ /**
6
+ * Extracts tenant ID from a DynamoDB stream ARN
7
+ *
8
+ * ARN format: arn:aws:dynamodb:region:account:table/tableName/stream/timestamp
9
+ * Table name format: {stackPrefix}.{tableServiceName}-service.{tenantId}
10
+ *
11
+ * @param arn - The event source ARN from the DynamoDB stream record
12
+ * @param tableServiceIdentifier - The service identifier (e.g., 'cmp', 'content') to validate in table name
13
+ * @returns The tenant ID extracted from the table name
14
+ * @throws Error if ARN or table name format is invalid
15
+ */
16
+ export declare function extractTenantIdFromArn(arn: string, tableServiceIdentifier: string): string;
17
+ /**
18
+ * Extracts string or number value from DynamoDB attribute format
19
+ *
20
+ * DynamoDB represents attributes as objects with type keys (e.g., { S: "value" }, { N: "123" })
21
+ * This helper extracts the actual value.
22
+ *
23
+ * @param value - The DynamoDB attribute value object
24
+ * @returns The extracted string value, or undefined if the attribute is empty
25
+ */
26
+ export declare function getDynamoValue(value?: {
27
+ S?: string;
28
+ N?: string;
29
+ }): string | undefined;
30
+ /**
31
+ * Determines the action type based on DynamoDB event name
32
+ *
33
+ * Maps DynamoDB stream event types to semantic action types suitable for event publishing.
34
+ *
35
+ * @param eventName - The DynamoDB stream event name (INSERT, MODIFY, REMOVE)
36
+ * @returns The corresponding action: 'create', 'update', or 'delete'
37
+ */
38
+ export declare function getActionType(eventName?: string): 'create' | 'update' | 'delete';
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ /*!
3
+ * @license
4
+ * Copyright Squiz Australia Pty Ltd. All Rights Reserved.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.extractTenantIdFromArn = extractTenantIdFromArn;
8
+ exports.getDynamoValue = getDynamoValue;
9
+ exports.getActionType = getActionType;
10
+ /**
11
+ * Extracts tenant ID from a DynamoDB stream ARN
12
+ *
13
+ * ARN format: arn:aws:dynamodb:region:account:table/tableName/stream/timestamp
14
+ * Table name format: {stackPrefix}.{tableServiceName}-service.{tenantId}
15
+ *
16
+ * @param arn - The event source ARN from the DynamoDB stream record
17
+ * @param tableServiceIdentifier - The service identifier (e.g., 'cmp', 'content') to validate in table name
18
+ * @returns The tenant ID extracted from the table name
19
+ * @throws Error if ARN or table name format is invalid
20
+ */
21
+ function extractTenantIdFromArn(arn, tableServiceIdentifier) {
22
+ const tableName = arn.split('/')[1];
23
+ if (!tableName) {
24
+ throw new Error(`Invalid ARN format: ${arn}`);
25
+ }
26
+ const parts = tableName.split('.');
27
+ if (parts.length < 3 || parts[1] !== `${tableServiceIdentifier}-service`) {
28
+ throw new Error(`Invalid ${tableServiceIdentifier} table name format: ${tableName}. Expected format: {prefix}.${tableServiceIdentifier}-service.{tenantId}`);
29
+ }
30
+ return parts[parts.length - 1];
31
+ }
32
+ /**
33
+ * Extracts string or number value from DynamoDB attribute format
34
+ *
35
+ * DynamoDB represents attributes as objects with type keys (e.g., { S: "value" }, { N: "123" })
36
+ * This helper extracts the actual value.
37
+ *
38
+ * @param value - The DynamoDB attribute value object
39
+ * @returns The extracted string value, or undefined if the attribute is empty
40
+ */
41
+ function getDynamoValue(value) {
42
+ return (value === null || value === void 0 ? void 0 : value.S) || (value === null || value === void 0 ? void 0 : value.N);
43
+ }
44
+ /**
45
+ * Determines the action type based on DynamoDB event name
46
+ *
47
+ * Maps DynamoDB stream event types to semantic action types suitable for event publishing.
48
+ *
49
+ * @param eventName - The DynamoDB stream event name (INSERT, MODIFY, REMOVE)
50
+ * @returns The corresponding action: 'create', 'update', or 'delete'
51
+ */
52
+ function getActionType(eventName) {
53
+ if (eventName === 'INSERT')
54
+ return 'create';
55
+ if (eventName === 'REMOVE')
56
+ return 'delete';
57
+ return 'update';
58
+ }
59
+ //# sourceMappingURL=StreamUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamUtils.js","sourceRoot":"","sources":["../../src/stream/StreamUtils.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAaH,wDAcC;AAWD,wCAEC;AAUD,sCAIC;AApDD;;;;;;;;;;GAUG;AACH,SAAgB,sBAAsB,CAAC,GAAW,EAAE,sBAA8B;IAChF,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,sBAAsB,UAAU,EAAE,CAAC;QACzE,MAAM,IAAI,KAAK,CACb,WAAW,sBAAsB,uBAAuB,SAAS,+BAA+B,sBAAsB,qBAAqB,CAC5I,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAAC,KAAkC;IAC/D,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,CAAC,MAAI,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,CAAC,CAAA,CAAC;AAC9B,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,aAAa,CAAC,SAAkB;IAC9C,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC5C,IAAI,SAAS,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC5C,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1 @@
1
+ export {};