@squiz/dx-common-lib 1.72.0 → 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.
- package/CHANGELOG.md +18 -0
- package/lib/events/EventBusService.d.ts +107 -0
- package/lib/events/EventBusService.js +196 -0
- package/lib/events/EventBusService.js.map +1 -0
- package/lib/events/EventBusService.spec.d.ts +1 -0
- package/lib/events/EventBusService.spec.js +527 -0
- package/lib/events/EventBusService.spec.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -1
- package/lib/secret-api-key-service/DevSecretApiKeyService.d.ts +25 -0
- package/lib/secret-api-key-service/DevSecretApiKeyService.js +35 -0
- package/lib/secret-api-key-service/DevSecretApiKeyService.js.map +1 -0
- package/lib/secret-api-key-service/DevSecretApiKeyService.spec.d.ts +1 -0
- package/lib/secret-api-key-service/DevSecretApiKeyService.spec.js +157 -0
- package/lib/secret-api-key-service/DevSecretApiKeyService.spec.js.map +1 -0
- package/lib/secret-api-key-service/SecretApiKeyService.d.ts +0 -9
- package/lib/secret-api-key-service/SecretApiKeyService.js +0 -7
- package/lib/secret-api-key-service/SecretApiKeyService.js.map +1 -1
- package/lib/secret-api-key-service/SecretApiKeyService.spec.js +0 -37
- package/lib/secret-api-key-service/SecretApiKeyService.spec.js.map +1 -1
- package/lib/secret-api-key-service/getSecretApiKeyService.d.ts +20 -0
- package/lib/secret-api-key-service/getSecretApiKeyService.js +41 -0
- package/lib/secret-api-key-service/getSecretApiKeyService.js.map +1 -0
- package/lib/secret-api-key-service/getSecretApiKeyService.spec.d.ts +1 -0
- package/lib/secret-api-key-service/getSecretApiKeyService.spec.js +313 -0
- package/lib/secret-api-key-service/getSecretApiKeyService.spec.js.map +1 -0
- package/lib/stream/EventStreamHandler.d.ts +38 -0
- package/lib/stream/EventStreamHandler.js +128 -0
- package/lib/stream/EventStreamHandler.js.map +1 -0
- package/lib/stream/EventStreamHandler.spec.d.ts +1 -0
- package/lib/stream/EventStreamHandler.spec.js +364 -0
- package/lib/stream/EventStreamHandler.spec.js.map +1 -0
- package/lib/stream/StreamUtils.d.ts +38 -0
- package/lib/stream/StreamUtils.js +59 -0
- package/lib/stream/StreamUtils.js.map +1 -0
- package/lib/stream/StreamUtils.spec.d.ts +1 -0
- package/lib/stream/StreamUtils.spec.js +92 -0
- package/lib/stream/StreamUtils.spec.js.map +1 -0
- package/package.json +3 -2
- package/src/events/EventBusService.spec.ts +707 -0
- package/src/events/EventBusService.ts +316 -0
- package/src/index.ts +5 -0
- package/src/secret-api-key-service/DevSecretApiKeyService.spec.ts +211 -0
- package/src/secret-api-key-service/DevSecretApiKeyService.ts +36 -0
- package/src/secret-api-key-service/SecretApiKeyService.spec.ts +0 -46
- package/src/secret-api-key-service/SecretApiKeyService.ts +0 -13
- package/src/secret-api-key-service/getSecretApiKeyService.spec.ts +405 -0
- package/src/secret-api-key-service/getSecretApiKeyService.ts +45 -0
- package/src/stream/EventStreamHandler.spec.ts +440 -0
- package/src/stream/EventStreamHandler.ts +192 -0
- package/src/stream/StreamUtils.spec.ts +113 -0
- package/src/stream/StreamUtils.ts +58 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const getSecretApiKeyService_1 = require("./getSecretApiKeyService");
|
|
4
|
+
const DevSecretApiKeyService_1 = require("./DevSecretApiKeyService");
|
|
5
|
+
const SecretApiKeyService_1 = require("./SecretApiKeyService");
|
|
6
|
+
// Mock the util module
|
|
7
|
+
jest.mock('../util', () => ({
|
|
8
|
+
getNodeEnv: jest.fn(),
|
|
9
|
+
never: jest.fn((value) => {
|
|
10
|
+
throw new Error(`Unexpected value: ${value}`);
|
|
11
|
+
}),
|
|
12
|
+
}));
|
|
13
|
+
// Import the mocked function
|
|
14
|
+
const util_1 = require("../util");
|
|
15
|
+
const mockGetNodeEnv = util_1.getNodeEnv;
|
|
16
|
+
describe('getSecretApiKeyService', () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
jest.clearAllMocks();
|
|
19
|
+
});
|
|
20
|
+
describe('Development Environment', () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
23
|
+
});
|
|
24
|
+
it('should return EnvApiKeyService when environment is development', () => {
|
|
25
|
+
const envVars = {
|
|
26
|
+
deploymentEnvironment: 'dev',
|
|
27
|
+
apiKey: 'test-dev-api-key',
|
|
28
|
+
};
|
|
29
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
30
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
31
|
+
});
|
|
32
|
+
it('should pass API key to EnvApiKeyService', async () => {
|
|
33
|
+
const apiKey = 'my-dev-api-key-123';
|
|
34
|
+
const envVars = {
|
|
35
|
+
deploymentEnvironment: 'dev',
|
|
36
|
+
apiKey,
|
|
37
|
+
};
|
|
38
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
39
|
+
const result = await service.getApiKey();
|
|
40
|
+
expect(result).toBe(apiKey);
|
|
41
|
+
});
|
|
42
|
+
it('should create EnvApiKeyService even when awsRegion is provided', () => {
|
|
43
|
+
const envVars = {
|
|
44
|
+
deploymentEnvironment: 'dev',
|
|
45
|
+
apiKey: 'dev-key',
|
|
46
|
+
awsRegion: 'us-east-1', // This should be ignored in dev
|
|
47
|
+
secretName: 'some-secret', // This should also be ignored
|
|
48
|
+
};
|
|
49
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
50
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
51
|
+
});
|
|
52
|
+
it('should throw error if apiKey is empty in development', () => {
|
|
53
|
+
const envVars = {
|
|
54
|
+
deploymentEnvironment: 'dev',
|
|
55
|
+
apiKey: '',
|
|
56
|
+
};
|
|
57
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow('API key must be a non-empty string');
|
|
58
|
+
});
|
|
59
|
+
it('should throw error if apiKey is undefined in development', () => {
|
|
60
|
+
const envVars = {
|
|
61
|
+
deploymentEnvironment: 'dev',
|
|
62
|
+
apiKey: undefined,
|
|
63
|
+
};
|
|
64
|
+
// TypeScript will complain, but at runtime this could happen
|
|
65
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow();
|
|
66
|
+
});
|
|
67
|
+
it('should handle whitespace-only apiKey in development', () => {
|
|
68
|
+
const envVars = {
|
|
69
|
+
deploymentEnvironment: 'dev',
|
|
70
|
+
apiKey: ' ',
|
|
71
|
+
};
|
|
72
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow('API key must be a non-empty string');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('Production Environment', () => {
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
78
|
+
});
|
|
79
|
+
it('should return SecretApiKeyService when environment is production', () => {
|
|
80
|
+
const envVars = {
|
|
81
|
+
deploymentEnvironment: 'prod',
|
|
82
|
+
secretName: 'my-secret-name',
|
|
83
|
+
awsRegion: 'us-east-1',
|
|
84
|
+
};
|
|
85
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
86
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
87
|
+
});
|
|
88
|
+
it('should pass secretName and awsRegion to SecretApiKeyService', () => {
|
|
89
|
+
const secretName = '/servicekeys-prod-us/metrics-api';
|
|
90
|
+
const awsRegion = 'ap-southeast-2';
|
|
91
|
+
const envVars = {
|
|
92
|
+
deploymentEnvironment: 'prod',
|
|
93
|
+
secretName,
|
|
94
|
+
awsRegion,
|
|
95
|
+
};
|
|
96
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
97
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
98
|
+
// We can't directly inspect the private properties, but we can verify the type
|
|
99
|
+
});
|
|
100
|
+
it('should create SecretApiKeyService even when apiKey is provided', () => {
|
|
101
|
+
const envVars = {
|
|
102
|
+
deploymentEnvironment: 'prod',
|
|
103
|
+
apiKey: 'some-key', // This should be ignored in prod
|
|
104
|
+
secretName: 'my-secret',
|
|
105
|
+
awsRegion: 'us-west-2',
|
|
106
|
+
};
|
|
107
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
108
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
109
|
+
});
|
|
110
|
+
it('should handle production with various AWS regions', () => {
|
|
111
|
+
const regions = ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-southeast-2', 'ap-northeast-1'];
|
|
112
|
+
regions.forEach((region) => {
|
|
113
|
+
const envVars = {
|
|
114
|
+
deploymentEnvironment: 'prod',
|
|
115
|
+
secretName: 'test-secret',
|
|
116
|
+
awsRegion: region,
|
|
117
|
+
};
|
|
118
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
119
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
it('should handle production with various secret name formats', () => {
|
|
123
|
+
const secretNames = [
|
|
124
|
+
'/servicekeys-prod-us/metrics',
|
|
125
|
+
'servicekeys-dev-au/feaas-metrics',
|
|
126
|
+
'arn:aws:secretsmanager:us-east-1:123456789:secret:metrics-key',
|
|
127
|
+
'simple-secret-name',
|
|
128
|
+
];
|
|
129
|
+
secretNames.forEach((secretName) => {
|
|
130
|
+
const envVars = {
|
|
131
|
+
deploymentEnvironment: 'prod',
|
|
132
|
+
secretName,
|
|
133
|
+
awsRegion: 'us-east-1',
|
|
134
|
+
};
|
|
135
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
136
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe('Invalid Environment', () => {
|
|
141
|
+
it('should throw error for test environment', () => {
|
|
142
|
+
mockGetNodeEnv.mockReturnValue('test');
|
|
143
|
+
const envVars = {
|
|
144
|
+
deploymentEnvironment: 'test',
|
|
145
|
+
apiKey: 'test-key',
|
|
146
|
+
};
|
|
147
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow();
|
|
148
|
+
});
|
|
149
|
+
it('should throw error for staging environment', () => {
|
|
150
|
+
mockGetNodeEnv.mockReturnValue('staging');
|
|
151
|
+
const envVars = {
|
|
152
|
+
deploymentEnvironment: 'staging',
|
|
153
|
+
apiKey: 'staging-key',
|
|
154
|
+
};
|
|
155
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow();
|
|
156
|
+
});
|
|
157
|
+
it('should throw error for unknown environment', () => {
|
|
158
|
+
mockGetNodeEnv.mockReturnValue('unknown');
|
|
159
|
+
const envVars = {
|
|
160
|
+
deploymentEnvironment: 'unknown',
|
|
161
|
+
apiKey: 'unknown-key',
|
|
162
|
+
};
|
|
163
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow();
|
|
164
|
+
});
|
|
165
|
+
it('should throw error for empty string environment', () => {
|
|
166
|
+
mockGetNodeEnv.mockReturnValue('');
|
|
167
|
+
const envVars = {
|
|
168
|
+
deploymentEnvironment: '',
|
|
169
|
+
apiKey: 'test-key',
|
|
170
|
+
};
|
|
171
|
+
expect(() => (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars)).toThrow();
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
describe('Environment Variables Validation', () => {
|
|
175
|
+
it('should handle envVars with all possible fields in development', () => {
|
|
176
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
177
|
+
const envVars = {
|
|
178
|
+
deploymentEnvironment: 'dev',
|
|
179
|
+
apiKey: 'dev-key',
|
|
180
|
+
awsRegion: 'us-east-1',
|
|
181
|
+
secretName: 'secret-name',
|
|
182
|
+
};
|
|
183
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
184
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
185
|
+
});
|
|
186
|
+
it('should handle envVars with only required fields for production', () => {
|
|
187
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
188
|
+
const envVars = {
|
|
189
|
+
deploymentEnvironment: 'prod',
|
|
190
|
+
secretName: 'secret-name',
|
|
191
|
+
awsRegion: 'us-east-1',
|
|
192
|
+
};
|
|
193
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
194
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
195
|
+
});
|
|
196
|
+
it('should handle different deployment environments', () => {
|
|
197
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
198
|
+
const deploymentEnvs = ['dev', 'development', 'local', 'dev-au', 'dev-us'];
|
|
199
|
+
deploymentEnvs.forEach((deploymentEnv) => {
|
|
200
|
+
const envVars = {
|
|
201
|
+
deploymentEnvironment: deploymentEnv,
|
|
202
|
+
apiKey: 'test-key',
|
|
203
|
+
};
|
|
204
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
205
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
describe('Real-world Scenarios', () => {
|
|
210
|
+
it('should handle local development scenario', async () => {
|
|
211
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
212
|
+
const envVars = {
|
|
213
|
+
deploymentEnvironment: 'dev-au',
|
|
214
|
+
apiKey: 'local-dev-metrics-api-key-12345',
|
|
215
|
+
};
|
|
216
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
217
|
+
const apiKey = await service.getApiKey();
|
|
218
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
219
|
+
expect(apiKey).toBe('local-dev-metrics-api-key-12345');
|
|
220
|
+
});
|
|
221
|
+
it('should handle production AWS scenario', () => {
|
|
222
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
223
|
+
const envVars = {
|
|
224
|
+
deploymentEnvironment: 'prod-us',
|
|
225
|
+
secretName: '/servicekeys-prod-us/feaas-metrics',
|
|
226
|
+
awsRegion: 'us-east-1',
|
|
227
|
+
};
|
|
228
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
229
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
230
|
+
});
|
|
231
|
+
it('should handle Australian region production scenario', () => {
|
|
232
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
233
|
+
const envVars = {
|
|
234
|
+
deploymentEnvironment: 'prod-au',
|
|
235
|
+
secretName: '/servicekeys-prod-au/feaas-metrics',
|
|
236
|
+
awsRegion: 'ap-southeast-2',
|
|
237
|
+
};
|
|
238
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
239
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
240
|
+
});
|
|
241
|
+
it('should create different instances for different calls', () => {
|
|
242
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
243
|
+
const envVars1 = {
|
|
244
|
+
deploymentEnvironment: 'dev',
|
|
245
|
+
apiKey: 'key-1',
|
|
246
|
+
};
|
|
247
|
+
const envVars2 = {
|
|
248
|
+
deploymentEnvironment: 'dev',
|
|
249
|
+
apiKey: 'key-2',
|
|
250
|
+
};
|
|
251
|
+
const service1 = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars1);
|
|
252
|
+
const service2 = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars2);
|
|
253
|
+
// Should be different instances
|
|
254
|
+
expect(service1).not.toBe(service2);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
describe('Edge Cases', () => {
|
|
258
|
+
it('should handle very long secret names in production', () => {
|
|
259
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
260
|
+
const envVars = {
|
|
261
|
+
deploymentEnvironment: 'prod',
|
|
262
|
+
secretName: '/very/long/secret/path/with/many/segments/'.repeat(10),
|
|
263
|
+
awsRegion: 'us-east-1',
|
|
264
|
+
};
|
|
265
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
266
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
267
|
+
});
|
|
268
|
+
it('should handle very long API keys in development', () => {
|
|
269
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
270
|
+
const envVars = {
|
|
271
|
+
deploymentEnvironment: 'dev',
|
|
272
|
+
apiKey: 'x'.repeat(1000),
|
|
273
|
+
};
|
|
274
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
275
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
276
|
+
});
|
|
277
|
+
it('should handle special characters in secretName', () => {
|
|
278
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
279
|
+
const envVars = {
|
|
280
|
+
deploymentEnvironment: 'prod',
|
|
281
|
+
secretName: '/servicekeys-prod_us/feaas.metrics-api',
|
|
282
|
+
awsRegion: 'us-east-1',
|
|
283
|
+
};
|
|
284
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
285
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
describe('Type Safety', () => {
|
|
289
|
+
it('should accept all valid envVars properties', () => {
|
|
290
|
+
mockGetNodeEnv.mockReturnValue('development');
|
|
291
|
+
const envVars = {
|
|
292
|
+
deploymentEnvironment: 'dev',
|
|
293
|
+
apiKey: 'key',
|
|
294
|
+
awsRegion: undefined,
|
|
295
|
+
secretName: 'secret',
|
|
296
|
+
};
|
|
297
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
298
|
+
expect(service).toBeInstanceOf(DevSecretApiKeyService_1.DevSecretApiKeyService);
|
|
299
|
+
});
|
|
300
|
+
it('should handle optional undefined properties correctly', () => {
|
|
301
|
+
mockGetNodeEnv.mockReturnValue('production');
|
|
302
|
+
const envVars = {
|
|
303
|
+
deploymentEnvironment: 'prod',
|
|
304
|
+
apiKey: undefined,
|
|
305
|
+
awsRegion: 'us-east-1',
|
|
306
|
+
secretName: 'secret-name',
|
|
307
|
+
};
|
|
308
|
+
const service = (0, getSecretApiKeyService_1.getSecretApiKeyService)(envVars);
|
|
309
|
+
expect(service).toBeInstanceOf(SecretApiKeyService_1.SecretApiKeyService);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
//# sourceMappingURL=getSecretApiKeyService.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getSecretApiKeyService.spec.js","sourceRoot":"","sources":["../../src/secret-api-key-service/getSecretApiKeyService.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
|
+
import { DynamoDBStreamEvent, DynamoDBRecord, Context, DynamoDBBatchResponse } from 'aws-lambda';
|
|
6
|
+
import { Logger } from '@squiz/dx-logger-lib';
|
|
7
|
+
import { EventBusService } from '../events/EventBusService';
|
|
8
|
+
export interface EventPayload {
|
|
9
|
+
detailType: string;
|
|
10
|
+
detail: object;
|
|
11
|
+
}
|
|
12
|
+
export type EventMapper = (record: DynamoDBRecord) => Promise<EventPayload[]>;
|
|
13
|
+
/** Resolves tenant id for publishing; use `record` when tenant is carried on the item rather than derivable from the table ARN. */
|
|
14
|
+
export type ExtractTenantId = (arn: string, tableServiceIdentifier: string, record: DynamoDBRecord) => string;
|
|
15
|
+
interface EventStreamHandlerConfig<T extends string> {
|
|
16
|
+
logger: Logger;
|
|
17
|
+
eventBusService: EventBusService;
|
|
18
|
+
/** Matches the table name segment in ARNs (e.g. `cmp` for `*.cmp-service.*`); also used as log field `service`. */
|
|
19
|
+
tableServiceIdentifier: string;
|
|
20
|
+
entityTypePrefixMap: Record<string, T>;
|
|
21
|
+
mappers: Partial<Record<T, EventMapper>>;
|
|
22
|
+
extractTenantId: ExtractTenantId;
|
|
23
|
+
batchSize?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* DynamoDB stream Lambda handler factory for mapping stream records to domain events and publishing via the event bus.
|
|
27
|
+
*
|
|
28
|
+
* Handles:
|
|
29
|
+
* - Record batching by configurable size
|
|
30
|
+
* - Tenant extraction from stream ARN and/or stream record (via `extractTenantId`)
|
|
31
|
+
* - Entity type detection via prefix matching
|
|
32
|
+
* - Mapper invocation for known entity types
|
|
33
|
+
* - Event grouping by tenant
|
|
34
|
+
* - Batch event publishing with retry tracking
|
|
35
|
+
* - Failure reporting for Lambda retry mechanism
|
|
36
|
+
*/
|
|
37
|
+
export declare function createEventStreamHandler<T extends string>(config: EventStreamHandlerConfig<T>): (event: DynamoDBStreamEvent, context: Context) => Promise<DynamoDBBatchResponse>;
|
|
38
|
+
export {};
|
|
@@ -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 {};
|