@squiz/dx-common-lib 1.72.0 → 1.72.1

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/lib/index.d.ts +2 -0
  3. package/lib/index.js +2 -0
  4. package/lib/index.js.map +1 -1
  5. package/lib/secret-api-key-service/DevSecretApiKeyService.d.ts +25 -0
  6. package/lib/secret-api-key-service/DevSecretApiKeyService.js +35 -0
  7. package/lib/secret-api-key-service/DevSecretApiKeyService.js.map +1 -0
  8. package/lib/secret-api-key-service/DevSecretApiKeyService.spec.d.ts +1 -0
  9. package/lib/secret-api-key-service/DevSecretApiKeyService.spec.js +157 -0
  10. package/lib/secret-api-key-service/DevSecretApiKeyService.spec.js.map +1 -0
  11. package/lib/secret-api-key-service/SecretApiKeyService.d.ts +0 -9
  12. package/lib/secret-api-key-service/SecretApiKeyService.js +0 -7
  13. package/lib/secret-api-key-service/SecretApiKeyService.js.map +1 -1
  14. package/lib/secret-api-key-service/SecretApiKeyService.spec.js +0 -37
  15. package/lib/secret-api-key-service/SecretApiKeyService.spec.js.map +1 -1
  16. package/lib/secret-api-key-service/getSecretApiKeyService.d.ts +20 -0
  17. package/lib/secret-api-key-service/getSecretApiKeyService.js +41 -0
  18. package/lib/secret-api-key-service/getSecretApiKeyService.js.map +1 -0
  19. package/lib/secret-api-key-service/getSecretApiKeyService.spec.d.ts +1 -0
  20. package/lib/secret-api-key-service/getSecretApiKeyService.spec.js +313 -0
  21. package/lib/secret-api-key-service/getSecretApiKeyService.spec.js.map +1 -0
  22. package/package.json +1 -1
  23. package/src/index.ts +2 -0
  24. package/src/secret-api-key-service/DevSecretApiKeyService.spec.ts +211 -0
  25. package/src/secret-api-key-service/DevSecretApiKeyService.ts +36 -0
  26. package/src/secret-api-key-service/SecretApiKeyService.spec.ts +0 -46
  27. package/src/secret-api-key-service/SecretApiKeyService.ts +0 -13
  28. package/src/secret-api-key-service/getSecretApiKeyService.spec.ts +405 -0
  29. package/src/secret-api-key-service/getSecretApiKeyService.ts +45 -0
  30. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,405 @@
1
+ import { getSecretApiKeyService } from './getSecretApiKeyService';
2
+ import { DevSecretApiKeyService } from './DevSecretApiKeyService';
3
+ import { SecretApiKeyService } from './SecretApiKeyService';
4
+
5
+ // Mock the util module
6
+ jest.mock('../util', () => ({
7
+ getNodeEnv: jest.fn(),
8
+ never: jest.fn((value: never) => {
9
+ throw new Error(`Unexpected value: ${value}`);
10
+ }),
11
+ }));
12
+
13
+ // Import the mocked function
14
+ import { getNodeEnv } from '../util';
15
+
16
+ const mockGetNodeEnv = getNodeEnv as jest.MockedFunction<typeof getNodeEnv>;
17
+
18
+ describe('getSecretApiKeyService', () => {
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ });
22
+
23
+ describe('Development Environment', () => {
24
+ beforeEach(() => {
25
+ mockGetNodeEnv.mockReturnValue('development');
26
+ });
27
+
28
+ it('should return EnvApiKeyService when environment is development', () => {
29
+ const envVars = {
30
+ deploymentEnvironment: 'dev',
31
+ apiKey: 'test-dev-api-key',
32
+ };
33
+
34
+ const service = getSecretApiKeyService(envVars);
35
+
36
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
37
+ });
38
+
39
+ it('should pass API key to EnvApiKeyService', async () => {
40
+ const apiKey = 'my-dev-api-key-123';
41
+ const envVars = {
42
+ deploymentEnvironment: 'dev',
43
+ apiKey,
44
+ };
45
+
46
+ const service = getSecretApiKeyService(envVars);
47
+ const result = await service.getApiKey();
48
+
49
+ expect(result).toBe(apiKey);
50
+ });
51
+
52
+ it('should create EnvApiKeyService even when awsRegion is provided', () => {
53
+ const envVars = {
54
+ deploymentEnvironment: 'dev',
55
+ apiKey: 'dev-key',
56
+ awsRegion: 'us-east-1', // This should be ignored in dev
57
+ secretName: 'some-secret', // This should also be ignored
58
+ };
59
+
60
+ const service = getSecretApiKeyService(envVars);
61
+
62
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
63
+ });
64
+
65
+ it('should throw error if apiKey is empty in development', () => {
66
+ const envVars = {
67
+ deploymentEnvironment: 'dev',
68
+ apiKey: '',
69
+ };
70
+
71
+ expect(() => getSecretApiKeyService(envVars)).toThrow('API key must be a non-empty string');
72
+ });
73
+
74
+ it('should throw error if apiKey is undefined in development', () => {
75
+ const envVars = {
76
+ deploymentEnvironment: 'dev',
77
+ apiKey: undefined,
78
+ };
79
+
80
+ // TypeScript will complain, but at runtime this could happen
81
+ expect(() => getSecretApiKeyService(envVars as any)).toThrow();
82
+ });
83
+
84
+ it('should handle whitespace-only apiKey in development', () => {
85
+ const envVars = {
86
+ deploymentEnvironment: 'dev',
87
+ apiKey: ' ',
88
+ };
89
+
90
+ expect(() => getSecretApiKeyService(envVars)).toThrow('API key must be a non-empty string');
91
+ });
92
+ });
93
+
94
+ describe('Production Environment', () => {
95
+ beforeEach(() => {
96
+ mockGetNodeEnv.mockReturnValue('production');
97
+ });
98
+
99
+ it('should return SecretApiKeyService when environment is production', () => {
100
+ const envVars = {
101
+ deploymentEnvironment: 'prod',
102
+ secretName: 'my-secret-name',
103
+ awsRegion: 'us-east-1',
104
+ };
105
+
106
+ const service = getSecretApiKeyService(envVars);
107
+
108
+ expect(service).toBeInstanceOf(SecretApiKeyService);
109
+ });
110
+
111
+ it('should pass secretName and awsRegion to SecretApiKeyService', () => {
112
+ const secretName = '/servicekeys-prod-us/metrics-api';
113
+ const awsRegion = 'ap-southeast-2';
114
+ const envVars = {
115
+ deploymentEnvironment: 'prod',
116
+ secretName,
117
+ awsRegion,
118
+ };
119
+
120
+ const service = getSecretApiKeyService(envVars);
121
+
122
+ expect(service).toBeInstanceOf(SecretApiKeyService);
123
+ // We can't directly inspect the private properties, but we can verify the type
124
+ });
125
+
126
+ it('should create SecretApiKeyService even when apiKey is provided', () => {
127
+ const envVars = {
128
+ deploymentEnvironment: 'prod',
129
+ apiKey: 'some-key', // This should be ignored in prod
130
+ secretName: 'my-secret',
131
+ awsRegion: 'us-west-2',
132
+ };
133
+
134
+ const service = getSecretApiKeyService(envVars);
135
+
136
+ expect(service).toBeInstanceOf(SecretApiKeyService);
137
+ });
138
+
139
+ it('should handle production with various AWS regions', () => {
140
+ const regions = ['us-east-1', 'us-west-2', 'eu-west-1', 'ap-southeast-2', 'ap-northeast-1'];
141
+
142
+ regions.forEach((region) => {
143
+ const envVars = {
144
+ deploymentEnvironment: 'prod',
145
+ secretName: 'test-secret',
146
+ awsRegion: region,
147
+ };
148
+
149
+ const service = getSecretApiKeyService(envVars);
150
+ expect(service).toBeInstanceOf(SecretApiKeyService);
151
+ });
152
+ });
153
+
154
+ it('should handle production with various secret name formats', () => {
155
+ const secretNames = [
156
+ '/servicekeys-prod-us/metrics',
157
+ 'servicekeys-dev-au/feaas-metrics',
158
+ 'arn:aws:secretsmanager:us-east-1:123456789:secret:metrics-key',
159
+ 'simple-secret-name',
160
+ ];
161
+
162
+ secretNames.forEach((secretName) => {
163
+ const envVars = {
164
+ deploymentEnvironment: 'prod',
165
+ secretName,
166
+ awsRegion: 'us-east-1',
167
+ };
168
+
169
+ const service = getSecretApiKeyService(envVars);
170
+ expect(service).toBeInstanceOf(SecretApiKeyService);
171
+ });
172
+ });
173
+ });
174
+
175
+ describe('Invalid Environment', () => {
176
+ it('should throw error for test environment', () => {
177
+ mockGetNodeEnv.mockReturnValue('test' as any);
178
+
179
+ const envVars = {
180
+ deploymentEnvironment: 'test',
181
+ apiKey: 'test-key',
182
+ };
183
+
184
+ expect(() => getSecretApiKeyService(envVars)).toThrow();
185
+ });
186
+
187
+ it('should throw error for staging environment', () => {
188
+ mockGetNodeEnv.mockReturnValue('staging' as any);
189
+
190
+ const envVars = {
191
+ deploymentEnvironment: 'staging',
192
+ apiKey: 'staging-key',
193
+ };
194
+
195
+ expect(() => getSecretApiKeyService(envVars)).toThrow();
196
+ });
197
+
198
+ it('should throw error for unknown environment', () => {
199
+ mockGetNodeEnv.mockReturnValue('unknown' as any);
200
+
201
+ const envVars = {
202
+ deploymentEnvironment: 'unknown',
203
+ apiKey: 'unknown-key',
204
+ };
205
+
206
+ expect(() => getSecretApiKeyService(envVars)).toThrow();
207
+ });
208
+
209
+ it('should throw error for empty string environment', () => {
210
+ mockGetNodeEnv.mockReturnValue('' as any);
211
+
212
+ const envVars = {
213
+ deploymentEnvironment: '',
214
+ apiKey: 'test-key',
215
+ };
216
+
217
+ expect(() => getSecretApiKeyService(envVars)).toThrow();
218
+ });
219
+ });
220
+
221
+ describe('Environment Variables Validation', () => {
222
+ it('should handle envVars with all possible fields in development', () => {
223
+ mockGetNodeEnv.mockReturnValue('development');
224
+
225
+ const envVars = {
226
+ deploymentEnvironment: 'dev',
227
+ apiKey: 'dev-key',
228
+ awsRegion: 'us-east-1',
229
+ secretName: 'secret-name',
230
+ };
231
+
232
+ const service = getSecretApiKeyService(envVars);
233
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
234
+ });
235
+
236
+ it('should handle envVars with only required fields for production', () => {
237
+ mockGetNodeEnv.mockReturnValue('production');
238
+
239
+ const envVars = {
240
+ deploymentEnvironment: 'prod',
241
+ secretName: 'secret-name',
242
+ awsRegion: 'us-east-1',
243
+ };
244
+
245
+ const service = getSecretApiKeyService(envVars);
246
+ expect(service).toBeInstanceOf(SecretApiKeyService);
247
+ });
248
+
249
+ it('should handle different deployment environments', () => {
250
+ mockGetNodeEnv.mockReturnValue('development');
251
+
252
+ const deploymentEnvs = ['dev', 'development', 'local', 'dev-au', 'dev-us'];
253
+
254
+ deploymentEnvs.forEach((deploymentEnv) => {
255
+ const envVars = {
256
+ deploymentEnvironment: deploymentEnv,
257
+ apiKey: 'test-key',
258
+ };
259
+
260
+ const service = getSecretApiKeyService(envVars);
261
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
262
+ });
263
+ });
264
+ });
265
+
266
+ describe('Real-world Scenarios', () => {
267
+ it('should handle local development scenario', async () => {
268
+ mockGetNodeEnv.mockReturnValue('development');
269
+
270
+ const envVars = {
271
+ deploymentEnvironment: 'dev-au',
272
+ apiKey: 'local-dev-metrics-api-key-12345',
273
+ };
274
+
275
+ const service = getSecretApiKeyService(envVars);
276
+ const apiKey = await service.getApiKey();
277
+
278
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
279
+ expect(apiKey).toBe('local-dev-metrics-api-key-12345');
280
+ });
281
+
282
+ it('should handle production AWS scenario', () => {
283
+ mockGetNodeEnv.mockReturnValue('production');
284
+
285
+ const envVars = {
286
+ deploymentEnvironment: 'prod-us',
287
+ secretName: '/servicekeys-prod-us/feaas-metrics',
288
+ awsRegion: 'us-east-1',
289
+ };
290
+
291
+ const service = getSecretApiKeyService(envVars);
292
+
293
+ expect(service).toBeInstanceOf(SecretApiKeyService);
294
+ });
295
+
296
+ it('should handle Australian region production scenario', () => {
297
+ mockGetNodeEnv.mockReturnValue('production');
298
+
299
+ const envVars = {
300
+ deploymentEnvironment: 'prod-au',
301
+ secretName: '/servicekeys-prod-au/feaas-metrics',
302
+ awsRegion: 'ap-southeast-2',
303
+ };
304
+
305
+ const service = getSecretApiKeyService(envVars);
306
+
307
+ expect(service).toBeInstanceOf(SecretApiKeyService);
308
+ });
309
+
310
+ it('should create different instances for different calls', () => {
311
+ mockGetNodeEnv.mockReturnValue('development');
312
+
313
+ const envVars1 = {
314
+ deploymentEnvironment: 'dev',
315
+ apiKey: 'key-1',
316
+ };
317
+
318
+ const envVars2 = {
319
+ deploymentEnvironment: 'dev',
320
+ apiKey: 'key-2',
321
+ };
322
+
323
+ const service1 = getSecretApiKeyService(envVars1);
324
+ const service2 = getSecretApiKeyService(envVars2);
325
+
326
+ // Should be different instances
327
+ expect(service1).not.toBe(service2);
328
+ });
329
+ });
330
+
331
+ describe('Edge Cases', () => {
332
+ it('should handle very long secret names in production', () => {
333
+ mockGetNodeEnv.mockReturnValue('production');
334
+
335
+ const envVars = {
336
+ deploymentEnvironment: 'prod',
337
+ secretName: '/very/long/secret/path/with/many/segments/'.repeat(10),
338
+ awsRegion: 'us-east-1',
339
+ };
340
+
341
+ const service = getSecretApiKeyService(envVars);
342
+ expect(service).toBeInstanceOf(SecretApiKeyService);
343
+ });
344
+
345
+ it('should handle very long API keys in development', () => {
346
+ mockGetNodeEnv.mockReturnValue('development');
347
+
348
+ const envVars = {
349
+ deploymentEnvironment: 'dev',
350
+ apiKey: 'x'.repeat(1000),
351
+ };
352
+
353
+ const service = getSecretApiKeyService(envVars);
354
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
355
+ });
356
+
357
+ it('should handle special characters in secretName', () => {
358
+ mockGetNodeEnv.mockReturnValue('production');
359
+
360
+ const envVars = {
361
+ deploymentEnvironment: 'prod',
362
+ secretName: '/servicekeys-prod_us/feaas.metrics-api',
363
+ awsRegion: 'us-east-1',
364
+ };
365
+
366
+ const service = getSecretApiKeyService(envVars);
367
+ expect(service).toBeInstanceOf(SecretApiKeyService);
368
+ });
369
+ });
370
+
371
+ describe('Type Safety', () => {
372
+ it('should accept all valid envVars properties', () => {
373
+ mockGetNodeEnv.mockReturnValue('development');
374
+
375
+ const envVars: {
376
+ deploymentEnvironment: string;
377
+ apiKey?: string;
378
+ awsRegion?: string | undefined;
379
+ secretName?: string;
380
+ } = {
381
+ deploymentEnvironment: 'dev',
382
+ apiKey: 'key',
383
+ awsRegion: undefined,
384
+ secretName: 'secret',
385
+ };
386
+
387
+ const service = getSecretApiKeyService(envVars);
388
+ expect(service).toBeInstanceOf(DevSecretApiKeyService);
389
+ });
390
+
391
+ it('should handle optional undefined properties correctly', () => {
392
+ mockGetNodeEnv.mockReturnValue('production');
393
+
394
+ const envVars = {
395
+ deploymentEnvironment: 'prod',
396
+ apiKey: undefined,
397
+ awsRegion: 'us-east-1',
398
+ secretName: 'secret-name',
399
+ };
400
+
401
+ const service = getSecretApiKeyService(envVars);
402
+ expect(service).toBeInstanceOf(SecretApiKeyService);
403
+ });
404
+ });
405
+ });
@@ -0,0 +1,45 @@
1
+ /*!
2
+ * @file getApiKeyService.ts
3
+ * @description Retrieves the API key service based on the environment.
4
+ * @author Squiz
5
+ * @copyright 2025 Squiz
6
+ * @license MIT
7
+ */
8
+
9
+ // Local
10
+ import { getNodeEnv, never } from '../util';
11
+ import { SecretApiKeyService } from './SecretApiKeyService';
12
+ import { DevSecretApiKeyService } from './DevSecretApiKeyService';
13
+
14
+ /**
15
+ * Retrieves the API key service based on the environment.
16
+ * @param envVars - The environment variables.
17
+ * @returns The API key service.
18
+ */
19
+ export function getSecretApiKeyService(envVars: {
20
+ deploymentEnvironment: string;
21
+ apiKey?: string;
22
+ awsRegion?: string | undefined;
23
+ secretName?: string;
24
+ }): SecretApiKeyService | DevSecretApiKeyService {
25
+ const env = getNodeEnv();
26
+
27
+ switch (env) {
28
+ // this the DX team dev environment
29
+ case 'development':
30
+ if (envVars.apiKey === undefined) {
31
+ throw new Error(`Need to specify 'apiKey' for development env`);
32
+ }
33
+ return new DevSecretApiKeyService(envVars.apiKey);
34
+ case 'production':
35
+ if (envVars.secretName === undefined) {
36
+ throw new Error(`Need to specify 'secretName' for production env`);
37
+ }
38
+ if (envVars.awsRegion === undefined) {
39
+ throw new Error(`Need to specify 'awsRegion' for production env`);
40
+ }
41
+ return new SecretApiKeyService(envVars.secretName, envVars.awsRegion);
42
+ default:
43
+ never(env);
44
+ }
45
+ }