@adtrackify/at-service-common 4.0.5 → 4.0.6
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/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.d.ts +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js +646 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js.map +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.d.ts +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js +327 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js.map +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +250 -108
- package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
- package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js +37 -36
- package/dist/cjs/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
- package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.d.ts +1 -0
- package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js +567 -0
- package/dist/cjs/__tests__/identity-cache/identity-gateway-service.spec.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.d.ts +3 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.js +7 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/constants.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.d.ts +14 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.js +251 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/helpers.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.d.ts +24 -0
- package/dist/cjs/services/db/{identity-cache-dynamodb-service.js → identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js} +221 -343
- package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +22 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +375 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/index.d.ts +4 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/index.js +21 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/index.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.d.ts +12 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.js +32 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/key-builders.js.map +1 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/types.d.ts +205 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/types.js +35 -0
- package/dist/cjs/services/db/identity-cache-dynamodb-service/types.js.map +1 -0
- package/dist/cjs/services/db/index.d.ts +4 -1
- package/dist/cjs/services/db/index.js +4 -1
- package/dist/cjs/services/db/index.js.map +1 -1
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.d.ts +1 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js +644 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-db-service-v2-comparison.spec.js.map +1 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.d.ts +1 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js +325 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service-v2-comparison.spec.js.map +1 -0
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +250 -108
- package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
- package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js +37 -36
- package/dist/esm/__tests__/identity-cache/identity-cache-tier-routing.spec.js.map +1 -1
- package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.d.ts +1 -0
- package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js +565 -0
- package/dist/esm/__tests__/identity-cache/identity-gateway-service.spec.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/constants.d.ts +3 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/constants.js +4 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/constants.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.d.ts +14 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.js +234 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/helpers.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.d.ts +24 -0
- package/dist/esm/services/db/{identity-cache-dynamodb-service.js → identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js} +199 -321
- package/dist/esm/services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.d.ts +22 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js +368 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/identity-gateway-service.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/index.d.ts +4 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/index.js +5 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/index.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.d.ts +12 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.js +24 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/key-builders.js.map +1 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/types.d.ts +205 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/types.js +32 -0
- package/dist/esm/services/db/identity-cache-dynamodb-service/types.js.map +1 -0
- package/dist/esm/services/db/index.d.ts +4 -1
- package/dist/esm/services/db/index.js +4 -1
- package/dist/esm/services/db/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/cjs/services/db/identity-cache-dynamodb-service.d.ts +0 -44
- package/dist/cjs/services/db/identity-cache-dynamodb-service.js.map +0 -1
- package/dist/esm/services/db/identity-cache-dynamodb-service.d.ts +0 -44
- package/dist/esm/services/db/identity-cache-dynamodb-service.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const at_tracking_event_types_1 = require("@adtrackify/at-tracking-event-types");
|
|
3
4
|
const mockInvokeFunction = jest.fn();
|
|
4
5
|
jest.mock('../../clients/index.js', () => ({
|
|
5
6
|
DynamoDbClient: {
|
|
@@ -23,10 +24,13 @@ jest.mock('../../helpers/index.js', () => ({
|
|
|
23
24
|
error: jest.fn(),
|
|
24
25
|
},
|
|
25
26
|
}));
|
|
26
|
-
const
|
|
27
|
+
const key_builders_js_1 = require("../../services/db/identity-cache-dynamodb-service/key-builders.js");
|
|
28
|
+
const identity_gateway_service_js_1 = require("../../services/db/identity-cache-dynamodb-service/identity-gateway-service.js");
|
|
29
|
+
const types_js_1 = require("../../services/db/identity-cache-dynamodb-service/types.js");
|
|
30
|
+
const identity_cache_dynamodb_db_service_js_1 = require("../../services/db/identity-cache-dynamodb-service/identity-cache-dynamodb-db-service.js");
|
|
27
31
|
const index_js_1 = require("../../clients/index.js");
|
|
28
32
|
const mockedDynamoDbClient = index_js_1.DynamoDbClient;
|
|
29
|
-
describe('
|
|
33
|
+
describe('IdentityGatewayService', () => {
|
|
30
34
|
beforeEach(() => {
|
|
31
35
|
jest.clearAllMocks();
|
|
32
36
|
mockedDynamoDbClient.safeGet.mockReset();
|
|
@@ -37,7 +41,31 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
37
41
|
mockedDynamoDbClient.safeQueryByGSI.mockReset();
|
|
38
42
|
mockedDynamoDbClient.batchWrite.mockReset();
|
|
39
43
|
mockInvokeFunction.mockReset();
|
|
40
|
-
|
|
44
|
+
identity_gateway_service_js_1.IdentityGatewayService.lambdaInvokeClient = undefined;
|
|
45
|
+
});
|
|
46
|
+
describe('identity cache key builders', () => {
|
|
47
|
+
it('should build primary identity cache keys', () => {
|
|
48
|
+
expect((0, key_builders_js_1.buildIdentityPk)('pixel123', 'identity456')).toBe('identity#pixel123#identity456');
|
|
49
|
+
});
|
|
50
|
+
it('should build lowercase email pointer keys', () => {
|
|
51
|
+
expect((0, key_builders_js_1.buildEmailPk)('pixel123', 'Test@Email.COM')).toBe('email#pixel123#test@email.com');
|
|
52
|
+
});
|
|
53
|
+
it('should build trimmed user id pointer keys', () => {
|
|
54
|
+
expect((0, key_builders_js_1.buildUserIdPk)('pixel123', ' user-1 ')).toBe('user_id#pixel123#user-1');
|
|
55
|
+
});
|
|
56
|
+
it('should build identity map keys', () => {
|
|
57
|
+
expect((0, key_builders_js_1.buildIdentityMapPk)('pixel123', 'identity456')).toBe('identity_map#pixel123#identity456');
|
|
58
|
+
});
|
|
59
|
+
it('should build force purge keys', () => {
|
|
60
|
+
expect((0, key_builders_js_1.buildForcePurgePk)('pixel123', 'identity456')).toBe('force_purge#pixel123#identity456');
|
|
61
|
+
});
|
|
62
|
+
it('should match the installed tracking-event-types key builder behavior during migration', () => {
|
|
63
|
+
expect((0, key_builders_js_1.buildIdentityPk)('pixel123', 'identity456')).toBe((0, at_tracking_event_types_1.buildIdentityPk)('pixel123', 'identity456'));
|
|
64
|
+
expect((0, key_builders_js_1.buildEmailPk)('pixel123', 'Test@Email.COM')).toBe((0, at_tracking_event_types_1.buildEmailPk)('pixel123', 'Test@Email.COM'));
|
|
65
|
+
expect((0, key_builders_js_1.buildUserIdPk)('pixel123', ' user-1 ')).toBe((0, at_tracking_event_types_1.buildUserIdPk)('pixel123', ' user-1 '));
|
|
66
|
+
expect((0, key_builders_js_1.buildIdentityMapPk)('pixel123', 'identity456')).toBe((0, at_tracking_event_types_1.buildIdentityMapPk)('pixel123', 'identity456'));
|
|
67
|
+
expect((0, key_builders_js_1.buildForcePurgePk)('pixel123', 'identity456')).toBe((0, at_tracking_event_types_1.buildForcePurgePk)('pixel123', 'identity456'));
|
|
68
|
+
});
|
|
41
69
|
});
|
|
42
70
|
describe('getIdentityWithCaching', () => {
|
|
43
71
|
const pixelId = 'pixel123';
|
|
@@ -59,7 +87,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
59
87
|
updatedAt: new Date().toISOString(),
|
|
60
88
|
};
|
|
61
89
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
62
|
-
const result = await
|
|
90
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
63
91
|
expect(result).toBeDefined();
|
|
64
92
|
expect(result?.identityId).toBe('identity456');
|
|
65
93
|
expect(mockInvokeFunction).not.toHaveBeenCalled();
|
|
@@ -81,7 +109,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
81
109
|
},
|
|
82
110
|
}),
|
|
83
111
|
});
|
|
84
|
-
const result = await
|
|
112
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
85
113
|
expect(mockInvokeFunction).toHaveBeenCalledWith(lambdaArn, {
|
|
86
114
|
pixelId,
|
|
87
115
|
context: { identity: incomingIdentity },
|
|
@@ -99,7 +127,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
99
127
|
statusCode: 200,
|
|
100
128
|
body: JSON.stringify({ identity: undefined }),
|
|
101
129
|
});
|
|
102
|
-
const result = await
|
|
130
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
103
131
|
expect(result).toEqual(incomingIdentity);
|
|
104
132
|
});
|
|
105
133
|
});
|
|
@@ -129,7 +157,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
129
157
|
},
|
|
130
158
|
}),
|
|
131
159
|
});
|
|
132
|
-
const result = await
|
|
160
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
133
161
|
expect(mockInvokeFunction).toHaveBeenCalled();
|
|
134
162
|
expect(result?.identityId).toBe('identity456');
|
|
135
163
|
expect(result?.traits?.emails).toContain('new@email.com');
|
|
@@ -154,7 +182,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
154
182
|
statusCode: 500,
|
|
155
183
|
body: 'Internal Server Error',
|
|
156
184
|
});
|
|
157
|
-
const result = await
|
|
185
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
158
186
|
expect(result).toBeDefined();
|
|
159
187
|
expect(result?.identityId).toBe('identity456');
|
|
160
188
|
});
|
|
@@ -174,7 +202,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
174
202
|
},
|
|
175
203
|
}),
|
|
176
204
|
});
|
|
177
|
-
await
|
|
205
|
+
await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
178
206
|
expect(mockedDynamoDbClient.safeBatchWrite).toHaveBeenCalled();
|
|
179
207
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
180
208
|
const identityPks = writtenItems.map((item) => item.pk);
|
|
@@ -189,18 +217,18 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
189
217
|
statusCode: 200,
|
|
190
218
|
body: JSON.stringify({ identity: undefined }),
|
|
191
219
|
});
|
|
192
|
-
await
|
|
220
|
+
await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
193
221
|
expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
|
|
194
222
|
});
|
|
195
223
|
});
|
|
196
224
|
describe('error handling (fail-open)', () => {
|
|
197
225
|
it('should return undefined when pixelId is missing', async () => {
|
|
198
|
-
const result = await
|
|
226
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(null, { identityId: 'test-id' }, lambdaArn);
|
|
199
227
|
expect(result).toBeUndefined();
|
|
200
228
|
expect(mockInvokeFunction).not.toHaveBeenCalled();
|
|
201
229
|
});
|
|
202
230
|
it('should return undefined when incomingIdentity is missing', async () => {
|
|
203
|
-
const result = await
|
|
231
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, null, lambdaArn);
|
|
204
232
|
expect(result).toBeUndefined();
|
|
205
233
|
expect(mockInvokeFunction).not.toHaveBeenCalled();
|
|
206
234
|
});
|
|
@@ -211,7 +239,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
211
239
|
};
|
|
212
240
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
213
241
|
mockInvokeFunction.mockRejectedValueOnce(new Error('Lambda invocation failed'));
|
|
214
|
-
const result = await
|
|
242
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
215
243
|
expect(result).toEqual(incomingIdentity);
|
|
216
244
|
});
|
|
217
245
|
it('should not throw when cache write fails after Neptune success', async () => {
|
|
@@ -229,7 +257,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
229
257
|
}),
|
|
230
258
|
});
|
|
231
259
|
mockedDynamoDbClient.safeBatchWrite.mockRejectedValueOnce(new Error('DynamoDB write failed'));
|
|
232
|
-
const result = await
|
|
260
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
233
261
|
expect(result?.identityId).toBe('neptune-resolved-id');
|
|
234
262
|
});
|
|
235
263
|
});
|
|
@@ -240,7 +268,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
240
268
|
const pixelId = 'pixel123';
|
|
241
269
|
const identityId = 'identity456';
|
|
242
270
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
243
|
-
await
|
|
271
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, { identityId });
|
|
244
272
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `identity#${pixelId}#${identityId}`);
|
|
245
273
|
});
|
|
246
274
|
});
|
|
@@ -249,7 +277,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
249
277
|
const pixelId = 'pixel123';
|
|
250
278
|
const email = 'Test@Email.COM';
|
|
251
279
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
252
|
-
await
|
|
280
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
253
281
|
traits: { emails: [email] },
|
|
254
282
|
});
|
|
255
283
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
|
|
@@ -260,7 +288,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
260
288
|
const pixelId = 'pixel123';
|
|
261
289
|
const userId = ' user789 ';
|
|
262
290
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
263
|
-
await
|
|
291
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
264
292
|
traits: { userIds: [userId] },
|
|
265
293
|
});
|
|
266
294
|
expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
|
|
@@ -283,7 +311,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
283
311
|
updatedAt: new Date().toISOString(),
|
|
284
312
|
};
|
|
285
313
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
286
|
-
const result = await
|
|
314
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
287
315
|
identityId,
|
|
288
316
|
traits: { emails: ['test@email.com'] },
|
|
289
317
|
});
|
|
@@ -293,7 +321,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
293
321
|
});
|
|
294
322
|
it('should return cache miss when identity not found', async () => {
|
|
295
323
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
296
|
-
const result = await
|
|
324
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
297
325
|
identityId: 'unknown-id',
|
|
298
326
|
});
|
|
299
327
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -301,7 +329,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
301
329
|
});
|
|
302
330
|
it('should NOT fallback to secondary keys when identityId lookup misses', async () => {
|
|
303
331
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
304
|
-
await
|
|
332
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
305
333
|
identityId: 'unknown-id',
|
|
306
334
|
traits: { emails: ['test@email.com'] },
|
|
307
335
|
});
|
|
@@ -320,7 +348,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
320
348
|
updatedAt: new Date().toISOString(),
|
|
321
349
|
};
|
|
322
350
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
323
|
-
const result = await
|
|
351
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
324
352
|
identityId,
|
|
325
353
|
traits: { emails: ['old@email.com', 'new@email.com'] },
|
|
326
354
|
});
|
|
@@ -338,7 +366,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
338
366
|
updatedAt: new Date().toISOString(),
|
|
339
367
|
};
|
|
340
368
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
|
|
341
|
-
const result = await
|
|
369
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
342
370
|
traits: { emails: ['test@email.com'], userIds: ['user123'] },
|
|
343
371
|
});
|
|
344
372
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
|
|
@@ -356,7 +384,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
356
384
|
updatedAt: new Date().toISOString(),
|
|
357
385
|
};
|
|
358
386
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
|
|
359
|
-
const result = await
|
|
387
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
360
388
|
traits: { emails: ['test@email.com'], userIds: ['user123'] },
|
|
361
389
|
});
|
|
362
390
|
expect(result.resolvedIdentity).toBeDefined();
|
|
@@ -365,14 +393,14 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
365
393
|
});
|
|
366
394
|
it('should return cache miss when no secondary keys match', async () => {
|
|
367
395
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
368
|
-
const result = await
|
|
396
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
369
397
|
traits: { emails: ['unknown@email.com'] },
|
|
370
398
|
});
|
|
371
399
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
372
400
|
expect(result.isCacheStale).toBe(true);
|
|
373
401
|
});
|
|
374
402
|
it('should return cache miss when no emails to lookup', async () => {
|
|
375
|
-
const result = await
|
|
403
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
376
404
|
traits: { userIds: ['user123'] },
|
|
377
405
|
});
|
|
378
406
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -380,7 +408,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
380
408
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
381
409
|
});
|
|
382
410
|
it('should return cache miss when empty traits', async () => {
|
|
383
|
-
const result = await
|
|
411
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
384
412
|
traits: {},
|
|
385
413
|
});
|
|
386
414
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -390,7 +418,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
390
418
|
describe('error handling (fail-open)', () => {
|
|
391
419
|
it('should return cache miss on safeGet error', async () => {
|
|
392
420
|
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
393
|
-
const result = await
|
|
421
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
394
422
|
identityId: 'test-id',
|
|
395
423
|
});
|
|
396
424
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -398,21 +426,21 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
398
426
|
});
|
|
399
427
|
it('should return cache miss on safeGet error for email lookup', async () => {
|
|
400
428
|
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
401
|
-
const result = await
|
|
429
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
402
430
|
traits: { emails: ['test@email.com'] },
|
|
403
431
|
});
|
|
404
432
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
405
433
|
expect(result.isCacheStale).toBe(true);
|
|
406
434
|
});
|
|
407
435
|
it('should return cache miss for null pixelId', async () => {
|
|
408
|
-
const result = await
|
|
436
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(null, {
|
|
409
437
|
identityId: 'test-id',
|
|
410
438
|
});
|
|
411
439
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
412
440
|
expect(result.isCacheStale).toBe(true);
|
|
413
441
|
});
|
|
414
442
|
it('should return cache miss for null incomingIdentity', async () => {
|
|
415
|
-
const result = await
|
|
443
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, null);
|
|
416
444
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
417
445
|
expect(result.isCacheStale).toBe(true);
|
|
418
446
|
});
|
|
@@ -428,7 +456,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
428
456
|
userIds: ['user1', 'user2'],
|
|
429
457
|
},
|
|
430
458
|
};
|
|
431
|
-
await
|
|
459
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
432
460
|
expect(mockedDynamoDbClient.safeBatchWrite).toHaveBeenCalledTimes(1);
|
|
433
461
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
434
462
|
expect(writtenItems).toHaveLength(3);
|
|
@@ -440,12 +468,12 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
440
468
|
expect(pks).not.toContain(`user_id#${pixelId}#user2`);
|
|
441
469
|
const identityItem = writtenItems.find((item) => item.pk.startsWith('identity#'));
|
|
442
470
|
expect(identityItem).toBeDefined();
|
|
443
|
-
expect(identityItem
|
|
444
|
-
expect(identityItem
|
|
471
|
+
expect(identityItem?.response).toBeDefined();
|
|
472
|
+
expect(identityItem?.response.identityId).toBe('identity456');
|
|
445
473
|
const emailItem = writtenItems.find((item) => item.pk.startsWith('email#'));
|
|
446
474
|
expect(emailItem).toBeDefined();
|
|
447
|
-
expect(emailItem
|
|
448
|
-
expect(emailItem
|
|
475
|
+
expect(emailItem?.response).toBeUndefined();
|
|
476
|
+
expect(emailItem?.identityId).toBe('identity456');
|
|
449
477
|
});
|
|
450
478
|
it('should not throw on safeBatchWrite error (fail-open)', async () => {
|
|
451
479
|
mockedDynamoDbClient.safeBatchWrite.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
@@ -453,14 +481,14 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
453
481
|
identityId: 'identity456',
|
|
454
482
|
traits: { emails: ['test@email.com'] },
|
|
455
483
|
};
|
|
456
|
-
await expect(
|
|
484
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity)).resolves.toBeUndefined();
|
|
457
485
|
});
|
|
458
486
|
it('should return early for missing identityId', async () => {
|
|
459
|
-
await
|
|
487
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, {});
|
|
460
488
|
expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
|
|
461
489
|
});
|
|
462
490
|
it('should return early for missing pixelId', async () => {
|
|
463
|
-
await
|
|
491
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(null, {
|
|
464
492
|
identityId: 'test-id',
|
|
465
493
|
});
|
|
466
494
|
expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
|
|
@@ -469,7 +497,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
469
497
|
const identity = {
|
|
470
498
|
identityId: 'identity456',
|
|
471
499
|
};
|
|
472
|
-
await
|
|
500
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
473
501
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
474
502
|
expect(writtenItems).toHaveLength(1);
|
|
475
503
|
});
|
|
@@ -478,7 +506,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
478
506
|
identityId: 'identity456',
|
|
479
507
|
traits: { emails: ['test@email.com'] },
|
|
480
508
|
};
|
|
481
|
-
await
|
|
509
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
482
510
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
483
511
|
for (const item of writtenItems) {
|
|
484
512
|
expect(item.gsi1pk).toBe('identity456');
|
|
@@ -495,33 +523,33 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
495
523
|
updatedAt: new Date().toISOString(),
|
|
496
524
|
};
|
|
497
525
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(mockMap);
|
|
498
|
-
const result = await
|
|
526
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('pixel123', 'identity456');
|
|
499
527
|
expect(result).toEqual(mockMap);
|
|
500
528
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', 'identity_map#pixel123#identity456');
|
|
501
529
|
});
|
|
502
530
|
it('should return undefined when not found', async () => {
|
|
503
531
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
504
|
-
const result = await
|
|
532
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('pixel123', 'identity456');
|
|
505
533
|
expect(result).toBeUndefined();
|
|
506
534
|
});
|
|
507
535
|
it('should return undefined for missing params (fail-open)', async () => {
|
|
508
|
-
const result = await
|
|
536
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('', '');
|
|
509
537
|
expect(result).toBeUndefined();
|
|
510
538
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
511
539
|
});
|
|
512
540
|
it('should return undefined when only pixelId is missing', async () => {
|
|
513
|
-
const result = await
|
|
541
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('', 'identity456');
|
|
514
542
|
expect(result).toBeUndefined();
|
|
515
543
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
516
544
|
});
|
|
517
545
|
it('should return undefined when only identityId is missing', async () => {
|
|
518
|
-
const result = await
|
|
546
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('pixel123', '');
|
|
519
547
|
expect(result).toBeUndefined();
|
|
520
548
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
521
549
|
});
|
|
522
550
|
it('should return undefined on safeGet error (fail-open)', async () => {
|
|
523
551
|
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
524
|
-
const result = await
|
|
552
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityMap('pixel123', 'identity456');
|
|
525
553
|
expect(result).toBeUndefined();
|
|
526
554
|
});
|
|
527
555
|
});
|
|
@@ -530,28 +558,28 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
530
558
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce({
|
|
531
559
|
pk: 'force_purge#pixel123#identity456',
|
|
532
560
|
});
|
|
533
|
-
const result = await
|
|
561
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getForcePurgeFlag('pixel123', 'identity456');
|
|
534
562
|
expect(result).toBe(true);
|
|
535
563
|
});
|
|
536
564
|
it('should return false when flag not found', async () => {
|
|
537
565
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
538
|
-
const result = await
|
|
566
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getForcePurgeFlag('pixel123', 'identity456');
|
|
539
567
|
expect(result).toBe(false);
|
|
540
568
|
});
|
|
541
569
|
it('should return false on error (fail-open)', async () => {
|
|
542
570
|
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
543
|
-
const result = await
|
|
571
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getForcePurgeFlag('pixel123', 'identity456');
|
|
544
572
|
expect(result).toBe(false);
|
|
545
573
|
});
|
|
546
574
|
it('should return false for missing params', async () => {
|
|
547
|
-
const result = await
|
|
575
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getForcePurgeFlag('', '');
|
|
548
576
|
expect(result).toBe(false);
|
|
549
577
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
550
578
|
});
|
|
551
579
|
});
|
|
552
580
|
describe('setForcePurgeFlag', () => {
|
|
553
581
|
it('should set flag with TTL', async () => {
|
|
554
|
-
await
|
|
582
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('pixel123', 'identity456');
|
|
555
583
|
expect(mockedDynamoDbClient.safePut).toHaveBeenCalledTimes(1);
|
|
556
584
|
const putItem = mockedDynamoDbClient.safePut.mock.calls[0][1];
|
|
557
585
|
expect(putItem.pk).toBe('force_purge#pixel123#identity456');
|
|
@@ -560,13 +588,13 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
560
588
|
expect(putItem.ttl).toBeGreaterThan(0);
|
|
561
589
|
});
|
|
562
590
|
it('should accept custom TTL', async () => {
|
|
563
|
-
await
|
|
591
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('pixel123', 'identity456', 3600);
|
|
564
592
|
const putItem = mockedDynamoDbClient.safePut.mock.calls[0][1];
|
|
565
593
|
expect(putItem.ttl).toBeGreaterThan(0);
|
|
566
594
|
});
|
|
567
595
|
it('should include createdAt timestamp', async () => {
|
|
568
596
|
const before = new Date().toISOString();
|
|
569
|
-
await
|
|
597
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('pixel123', 'identity456');
|
|
570
598
|
const after = new Date().toISOString();
|
|
571
599
|
const putItem = mockedDynamoDbClient.safePut.mock.calls[0][1];
|
|
572
600
|
expect(putItem.createdAt).toBeDefined();
|
|
@@ -574,41 +602,41 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
574
602
|
expect(putItem.createdAt <= after).toBe(true);
|
|
575
603
|
});
|
|
576
604
|
it('should return early when pixelId is missing', async () => {
|
|
577
|
-
await
|
|
605
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('', 'identity456');
|
|
578
606
|
expect(mockedDynamoDbClient.safePut).not.toHaveBeenCalled();
|
|
579
607
|
});
|
|
580
608
|
it('should return early when identityId is missing', async () => {
|
|
581
|
-
await
|
|
609
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('pixel123', '');
|
|
582
610
|
expect(mockedDynamoDbClient.safePut).not.toHaveBeenCalled();
|
|
583
611
|
});
|
|
584
612
|
it('should not throw on error (fail-open)', async () => {
|
|
585
613
|
mockedDynamoDbClient.safePut.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
586
|
-
await expect(
|
|
614
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.setForcePurgeFlag('pixel123', 'identity456')).resolves.toBeUndefined();
|
|
587
615
|
});
|
|
588
616
|
});
|
|
589
617
|
describe('putIdentityMap', () => {
|
|
590
618
|
it('should write identity map', async () => {
|
|
591
|
-
await
|
|
619
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('pixel123', 'identity456', ['linked1', 'linked2']);
|
|
592
620
|
expect(mockedDynamoDbClient.safePut).toHaveBeenCalledTimes(1);
|
|
593
621
|
const putItem = mockedDynamoDbClient.safePut.mock.calls[0][1];
|
|
594
622
|
expect(putItem.pk).toBe('identity_map#pixel123#identity456');
|
|
595
623
|
expect(putItem.linkedIdentities).toEqual(['linked1', 'linked2']);
|
|
596
624
|
});
|
|
597
625
|
it('should return early for missing params', async () => {
|
|
598
|
-
await
|
|
626
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('', '', []);
|
|
599
627
|
expect(mockedDynamoDbClient.safePut).not.toHaveBeenCalled();
|
|
600
628
|
});
|
|
601
629
|
it('should return early when only pixelId is missing', async () => {
|
|
602
|
-
await
|
|
630
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('', 'identity456', ['linked1']);
|
|
603
631
|
expect(mockedDynamoDbClient.safePut).not.toHaveBeenCalled();
|
|
604
632
|
});
|
|
605
633
|
it('should return early when only identityId is missing', async () => {
|
|
606
|
-
await
|
|
634
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('pixel123', '', ['linked1']);
|
|
607
635
|
expect(mockedDynamoDbClient.safePut).not.toHaveBeenCalled();
|
|
608
636
|
});
|
|
609
637
|
it('should include updatedAt timestamp', async () => {
|
|
610
638
|
const before = new Date().toISOString();
|
|
611
|
-
await
|
|
639
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('pixel123', 'identity456', ['linked1']);
|
|
612
640
|
const after = new Date().toISOString();
|
|
613
641
|
const putItem = mockedDynamoDbClient.safePut.mock.calls[0][1];
|
|
614
642
|
expect(putItem.updatedAt).toBeDefined();
|
|
@@ -617,51 +645,51 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
617
645
|
});
|
|
618
646
|
it('should not throw on safePut error (fail-open)', async () => {
|
|
619
647
|
mockedDynamoDbClient.safePut.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
620
|
-
await expect(
|
|
648
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.putIdentityMap('pixel123', 'identity456', ['linked1'])).resolves.toBeUndefined();
|
|
621
649
|
});
|
|
622
650
|
});
|
|
623
651
|
describe('deleteIdentityMap', () => {
|
|
624
652
|
it('should delete identity map', async () => {
|
|
625
|
-
await
|
|
653
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityMap('pixel123', 'identity456');
|
|
626
654
|
expect(mockedDynamoDbClient.safeDelete).toHaveBeenCalledWith(expect.any(String), 'pk', 'identity_map#pixel123#identity456');
|
|
627
655
|
});
|
|
628
656
|
it('should return early for missing params', async () => {
|
|
629
|
-
await
|
|
657
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityMap('', '');
|
|
630
658
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
631
659
|
});
|
|
632
660
|
it('should return early when only pixelId is missing', async () => {
|
|
633
|
-
await
|
|
661
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityMap('', 'identity456');
|
|
634
662
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
635
663
|
});
|
|
636
664
|
it('should return early when only identityId is missing', async () => {
|
|
637
|
-
await
|
|
665
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityMap('pixel123', '');
|
|
638
666
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
639
667
|
});
|
|
640
668
|
it('should not throw on safeDelete error (fail-open)', async () => {
|
|
641
669
|
mockedDynamoDbClient.safeDelete.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
642
|
-
await expect(
|
|
670
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityMap('pixel123', 'identity456')).resolves.toBeUndefined();
|
|
643
671
|
});
|
|
644
672
|
});
|
|
645
673
|
describe('deleteForcePurgeFlag', () => {
|
|
646
674
|
it('should delete force purge flag', async () => {
|
|
647
|
-
await
|
|
675
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteForcePurgeFlag('pixel123', 'identity456');
|
|
648
676
|
expect(mockedDynamoDbClient.safeDelete).toHaveBeenCalledWith(expect.any(String), 'pk', 'force_purge#pixel123#identity456');
|
|
649
677
|
});
|
|
650
678
|
it('should return early for missing params', async () => {
|
|
651
|
-
await
|
|
679
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteForcePurgeFlag('', '');
|
|
652
680
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
653
681
|
});
|
|
654
682
|
it('should return early when only pixelId is missing', async () => {
|
|
655
|
-
await
|
|
683
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteForcePurgeFlag('', 'identity456');
|
|
656
684
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
657
685
|
});
|
|
658
686
|
it('should return early when only identityId is missing', async () => {
|
|
659
|
-
await
|
|
687
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteForcePurgeFlag('pixel123', '');
|
|
660
688
|
expect(mockedDynamoDbClient.safeDelete).not.toHaveBeenCalled();
|
|
661
689
|
});
|
|
662
690
|
it('should not throw on safeDelete error (fail-open)', async () => {
|
|
663
691
|
mockedDynamoDbClient.safeDelete.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
664
|
-
await expect(
|
|
692
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteForcePurgeFlag('pixel123', 'identity456')).resolves.toBeUndefined();
|
|
665
693
|
});
|
|
666
694
|
});
|
|
667
695
|
describe('deleteIdentityCache', () => {
|
|
@@ -680,12 +708,12 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
680
708
|
identityId: 'identity456',
|
|
681
709
|
traits: { emails: ['test@email.com', 'other@email.com'] },
|
|
682
710
|
};
|
|
683
|
-
await
|
|
711
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, incomingIdentity, resolvedIdentity);
|
|
684
712
|
expect(mockedDynamoDbClient.safeQueryByGSI).toHaveBeenCalled();
|
|
685
713
|
expect(mockedDynamoDbClient.batchWrite).toHaveBeenCalled();
|
|
686
714
|
});
|
|
687
715
|
it('should return early for missing pixelId', async () => {
|
|
688
|
-
await
|
|
716
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(null, {}, {});
|
|
689
717
|
expect(mockedDynamoDbClient.safeQueryByGSI).not.toHaveBeenCalled();
|
|
690
718
|
});
|
|
691
719
|
it('should handle different identityIds in incoming vs resolved', async () => {
|
|
@@ -699,19 +727,19 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
699
727
|
identityId: 'resolved-id',
|
|
700
728
|
traits: { emails: ['test@email.com'] },
|
|
701
729
|
};
|
|
702
|
-
await
|
|
730
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, incomingIdentity, resolvedIdentity);
|
|
703
731
|
expect(mockedDynamoDbClient.safeQueryByGSI).toHaveBeenCalledTimes(2);
|
|
704
732
|
});
|
|
705
733
|
it('should not throw on batchWrite error (fail-open)', async () => {
|
|
706
734
|
mockedDynamoDbClient.safeQueryByGSI.mockResolvedValue([{ pk: `identity#${pixelId}#identity456`, pixelId }]);
|
|
707
735
|
mockedDynamoDbClient.batchWrite.mockRejectedValueOnce(new Error('DynamoDB error'));
|
|
708
|
-
await expect(
|
|
736
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456' }, { identityId: 'identity456' })).resolves.toBeUndefined();
|
|
709
737
|
});
|
|
710
738
|
it('should deduplicate keys when same items returned from GSI query', async () => {
|
|
711
739
|
const duplicateItem = { pk: `identity#${pixelId}#identity456`, pixelId };
|
|
712
740
|
mockedDynamoDbClient.safeQueryByGSI.mockResolvedValue([duplicateItem, duplicateItem]);
|
|
713
741
|
mockedDynamoDbClient.batchWrite.mockResolvedValue({ $metadata: {} });
|
|
714
|
-
await
|
|
742
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456' }, { identityId: 'identity456' });
|
|
715
743
|
expect(mockedDynamoDbClient.batchWrite).toHaveBeenCalled();
|
|
716
744
|
const batchWriteCall = mockedDynamoDbClient.batchWrite.mock.calls[0];
|
|
717
745
|
expect(batchWriteCall).toBeDefined();
|
|
@@ -734,7 +762,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
734
762
|
updatedAt: new Date().toISOString(),
|
|
735
763
|
};
|
|
736
764
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
737
|
-
const result = await
|
|
765
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
738
766
|
identityId: 'identity456',
|
|
739
767
|
traits: {
|
|
740
768
|
emails: ['incoming@email.com'],
|
|
@@ -758,7 +786,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
758
786
|
updatedAt: new Date().toISOString(),
|
|
759
787
|
};
|
|
760
788
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
761
|
-
const result = await
|
|
789
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
762
790
|
identityId: 'identity456',
|
|
763
791
|
traits: {
|
|
764
792
|
emails: ['cached@email.com', 'new@email.com'],
|
|
@@ -781,7 +809,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
781
809
|
updatedAt: new Date().toISOString(),
|
|
782
810
|
};
|
|
783
811
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
784
|
-
const result = await
|
|
812
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
785
813
|
identityId: 'identity456',
|
|
786
814
|
traits: {
|
|
787
815
|
emails: ['a@email.com'],
|
|
@@ -799,7 +827,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
799
827
|
emails: ['first@email.com', 'second@email.com', 'third@email.com', 'fourth@email.com'],
|
|
800
828
|
},
|
|
801
829
|
};
|
|
802
|
-
await
|
|
830
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
803
831
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
804
832
|
expect(writtenItems).toHaveLength(5);
|
|
805
833
|
const emailPks = writtenItems
|
|
@@ -817,7 +845,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
817
845
|
emails: ['valid@email.com', null, undefined, '', 'another@email.com'],
|
|
818
846
|
},
|
|
819
847
|
};
|
|
820
|
-
await
|
|
848
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
821
849
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
822
850
|
const emailItems = writtenItems.filter((item) => item.pk.startsWith('email#'));
|
|
823
851
|
expect(emailItems).toHaveLength(2);
|
|
@@ -829,7 +857,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
829
857
|
identityId: 'identity456',
|
|
830
858
|
traits: { emails: [] },
|
|
831
859
|
};
|
|
832
|
-
await
|
|
860
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
833
861
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
834
862
|
expect(writtenItems).toHaveLength(1);
|
|
835
863
|
expect(writtenItems[0].pk).toBe(`identity#${pixelId}#identity456`);
|
|
@@ -841,7 +869,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
841
869
|
emails: ['UPPERCASE@EMAIL.COM', 'MixedCase@Email.Com'],
|
|
842
870
|
},
|
|
843
871
|
};
|
|
844
|
-
await
|
|
872
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
845
873
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
846
874
|
const emailPks = writtenItems
|
|
847
875
|
.filter((item) => item.pk.startsWith('email#'))
|
|
@@ -857,7 +885,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
857
885
|
identityId: 'identity456',
|
|
858
886
|
traits: null,
|
|
859
887
|
};
|
|
860
|
-
await
|
|
888
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
861
889
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
862
890
|
expect(writtenItems).toHaveLength(1);
|
|
863
891
|
});
|
|
@@ -866,12 +894,12 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
866
894
|
identityId: 'identity456',
|
|
867
895
|
traits: undefined,
|
|
868
896
|
};
|
|
869
|
-
await
|
|
897
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
870
898
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
871
899
|
expect(writtenItems).toHaveLength(1);
|
|
872
900
|
});
|
|
873
901
|
it('should return cache miss for identity with null traits in lookup', async () => {
|
|
874
|
-
const result = await
|
|
902
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
875
903
|
traits: null,
|
|
876
904
|
});
|
|
877
905
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -879,7 +907,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
879
907
|
expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
|
|
880
908
|
});
|
|
881
909
|
it('should handle identity with undefined emails in traits', async () => {
|
|
882
|
-
const result = await
|
|
910
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
883
911
|
traits: { userIds: ['user123'] },
|
|
884
912
|
});
|
|
885
913
|
expect(result.resolvedIdentity).toBeUndefined();
|
|
@@ -901,7 +929,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
901
929
|
updatedAt: new Date().toISOString(),
|
|
902
930
|
};
|
|
903
931
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(oldFormatEmailRecord);
|
|
904
|
-
const result = await
|
|
932
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
905
933
|
traits: { emails: ['test@email.com'] },
|
|
906
934
|
});
|
|
907
935
|
expect(result.resolvedIdentity).toBeDefined();
|
|
@@ -917,7 +945,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
917
945
|
updatedAt: new Date().toISOString(),
|
|
918
946
|
};
|
|
919
947
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(newFormatPointerRecord);
|
|
920
|
-
const result = await
|
|
948
|
+
const result = await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
921
949
|
traits: { emails: ['test@email.com'] },
|
|
922
950
|
});
|
|
923
951
|
expect(result.resolvedIdentity).toBeDefined();
|
|
@@ -943,7 +971,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
943
971
|
},
|
|
944
972
|
}),
|
|
945
973
|
});
|
|
946
|
-
const result = await
|
|
974
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, { traits: { emails: ['test@email.com'] } }, lambdaArn);
|
|
947
975
|
expect(mockInvokeFunction).toHaveBeenCalledWith(lambdaArn, expect.objectContaining({
|
|
948
976
|
pixelId,
|
|
949
977
|
context: {
|
|
@@ -970,7 +998,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
970
998
|
identityId: 'identity456',
|
|
971
999
|
traits: { emails: ['resolved1@email.com', 'resolved2@email.com', 'resolved3@email.com'] },
|
|
972
1000
|
};
|
|
973
|
-
await
|
|
1001
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, incomingIdentity, resolvedIdentity);
|
|
974
1002
|
expect(mockedDynamoDbClient.batchWrite).toHaveBeenCalled();
|
|
975
1003
|
const batchWriteCall = mockedDynamoDbClient.batchWrite.mock.calls[0][0];
|
|
976
1004
|
const deleteRequests = batchWriteCall.RequestItems[Object.keys(batchWriteCall.RequestItems)[0]];
|
|
@@ -987,7 +1015,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
987
1015
|
{ pk: `identity#${pixelId}#identity456`, pixelId },
|
|
988
1016
|
]);
|
|
989
1017
|
mockedDynamoDbClient.batchWrite.mockResolvedValue({ $metadata: {} });
|
|
990
|
-
await
|
|
1018
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456', traits: { emails: [] } }, { identityId: 'identity456' });
|
|
991
1019
|
expect(mockedDynamoDbClient.batchWrite).toHaveBeenCalled();
|
|
992
1020
|
});
|
|
993
1021
|
it('should handle missing traits in delete', async () => {
|
|
@@ -995,7 +1023,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
995
1023
|
{ pk: `identity#${pixelId}#identity456`, pixelId },
|
|
996
1024
|
]);
|
|
997
1025
|
mockedDynamoDbClient.batchWrite.mockResolvedValue({ $metadata: {} });
|
|
998
|
-
await
|
|
1026
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456' }, { identityId: 'identity456' });
|
|
999
1027
|
expect(mockedDynamoDbClient.batchWrite).toHaveBeenCalled();
|
|
1000
1028
|
});
|
|
1001
1029
|
it('should not delete items from other pixelIds', async () => {
|
|
@@ -1005,7 +1033,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1005
1033
|
{ pk: `identity#${otherPixelId}#identity456`, pixelId: otherPixelId },
|
|
1006
1034
|
]);
|
|
1007
1035
|
mockedDynamoDbClient.batchWrite.mockResolvedValue({ $metadata: {} });
|
|
1008
|
-
await
|
|
1036
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456' }, { identityId: 'identity456' });
|
|
1009
1037
|
const batchWriteCall = mockedDynamoDbClient.batchWrite.mock.calls[0][0];
|
|
1010
1038
|
const deleteRequests = batchWriteCall.RequestItems[Object.keys(batchWriteCall.RequestItems)[0]];
|
|
1011
1039
|
const deletedPks = deleteRequests.map((req) => req.DeleteRequest.Key.pk);
|
|
@@ -1024,24 +1052,24 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1024
1052
|
];
|
|
1025
1053
|
mockedDynamoDbClient.safeQueryByGSI.mockResolvedValue(mockResults);
|
|
1026
1054
|
mockedDynamoDbClient.batchWrite.mockResolvedValue({ $metadata: {} });
|
|
1027
|
-
await
|
|
1055
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId }, { identityId });
|
|
1028
1056
|
expect(mockedDynamoDbClient.safeQueryByGSI).toHaveBeenCalledWith(expect.any(String), expect.any(String), 'gsi1pk', identityId);
|
|
1029
1057
|
});
|
|
1030
1058
|
it('should handle GSI1 query returning empty results', async () => {
|
|
1031
1059
|
mockedDynamoDbClient.safeQueryByGSI.mockResolvedValue([]);
|
|
1032
|
-
await
|
|
1060
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'unknown-id' }, { identityId: 'unknown-id' });
|
|
1033
1061
|
expect(mockedDynamoDbClient.batchWrite).not.toHaveBeenCalled();
|
|
1034
1062
|
});
|
|
1035
1063
|
it('should handle GSI1 query error gracefully (fail-open)', async () => {
|
|
1036
1064
|
mockedDynamoDbClient.safeQueryByGSI.mockRejectedValueOnce(new Error('GSI query failed'));
|
|
1037
|
-
await expect(
|
|
1065
|
+
await expect(identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.deleteIdentityCache(pixelId, { identityId: 'identity456' }, { identityId: 'identity456' })).resolves.toBeUndefined();
|
|
1038
1066
|
});
|
|
1039
1067
|
});
|
|
1040
1068
|
describe('email pointer lookups', () => {
|
|
1041
1069
|
const pixelId = 'pixel123';
|
|
1042
1070
|
it('should only lookup first email (optimization)', async () => {
|
|
1043
1071
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
1044
|
-
await
|
|
1072
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
1045
1073
|
traits: { emails: ['first@email.com', 'second@email.com', 'third@email.com'] },
|
|
1046
1074
|
});
|
|
1047
1075
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledTimes(1);
|
|
@@ -1049,7 +1077,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1049
1077
|
});
|
|
1050
1078
|
it('should handle whitespace in email during lookup', async () => {
|
|
1051
1079
|
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
1052
|
-
await
|
|
1080
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.getIdentityFromCache(pixelId, {
|
|
1053
1081
|
traits: { emails: [' test@email.com '] },
|
|
1054
1082
|
});
|
|
1055
1083
|
expect(mockedDynamoDbClient.safeGet).toHaveBeenCalled();
|
|
@@ -1065,7 +1093,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1065
1093
|
};
|
|
1066
1094
|
mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB down'));
|
|
1067
1095
|
mockInvokeFunction.mockRejectedValueOnce(new Error('Lambda down'));
|
|
1068
|
-
const result = await
|
|
1096
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
1069
1097
|
expect(result).toEqual(incomingIdentity);
|
|
1070
1098
|
});
|
|
1071
1099
|
it('should continue when cache write fails after Neptune success', async () => {
|
|
@@ -1077,7 +1105,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1077
1105
|
}),
|
|
1078
1106
|
});
|
|
1079
1107
|
mockedDynamoDbClient.safeBatchWrite.mockRejectedValueOnce(new Error('Write failed'));
|
|
1080
|
-
const result = await
|
|
1108
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, { traits: { emails: ['test@email.com'] } }, lambdaArn);
|
|
1081
1109
|
expect(result?.identityId).toBe('resolved-id');
|
|
1082
1110
|
});
|
|
1083
1111
|
it('should return incoming identity when Neptune returns non-200', async () => {
|
|
@@ -1090,7 +1118,7 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1090
1118
|
statusCode: 500,
|
|
1091
1119
|
body: 'Internal Server Error',
|
|
1092
1120
|
});
|
|
1093
|
-
const result = await
|
|
1121
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
1094
1122
|
expect(result).toEqual(incomingIdentity);
|
|
1095
1123
|
});
|
|
1096
1124
|
it('should return incoming identity when Neptune returns malformed JSON (fail-open)', async () => {
|
|
@@ -1103,14 +1131,128 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1103
1131
|
statusCode: 200,
|
|
1104
1132
|
body: 'not valid json',
|
|
1105
1133
|
});
|
|
1106
|
-
const result = await
|
|
1134
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(pixelId, incomingIdentity, lambdaArn);
|
|
1107
1135
|
expect(result).toEqual(incomingIdentity);
|
|
1108
1136
|
});
|
|
1109
1137
|
it('should return undefined only when both inputs are invalid', async () => {
|
|
1110
|
-
const result = await
|
|
1138
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCaching(null, null, lambdaArn);
|
|
1111
1139
|
expect(result).toBeUndefined();
|
|
1112
1140
|
});
|
|
1113
1141
|
});
|
|
1142
|
+
describe('getIdentityWithCachingV2', () => {
|
|
1143
|
+
const pixelId = 'pixel123';
|
|
1144
|
+
const lambdaArn = 'arn:aws:lambda:us-east-1:123456789:function:identity-private';
|
|
1145
|
+
it('should return invalid input summary without resolving', async () => {
|
|
1146
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCachingV2('', { identityId: 'identity456' }, lambdaArn);
|
|
1147
|
+
expect(result.identity).toBeUndefined();
|
|
1148
|
+
expect(result.summary).toEqual(expect.objectContaining({
|
|
1149
|
+
outcome: types_js_1.IDENTITY_CACHE_WITH_CACHING_OUTCOME.INVALID_INPUT,
|
|
1150
|
+
didReadCache: false,
|
|
1151
|
+
didCallNeptune: false,
|
|
1152
|
+
didWriteCache: false,
|
|
1153
|
+
}));
|
|
1154
|
+
expect(mockInvokeFunction).not.toHaveBeenCalled();
|
|
1155
|
+
expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
|
|
1156
|
+
});
|
|
1157
|
+
it('should return fresh cache hit summary without invoking Neptune', async () => {
|
|
1158
|
+
const incomingIdentity = {
|
|
1159
|
+
identityId: 'identity456',
|
|
1160
|
+
traits: { emails: ['test@email.com'] },
|
|
1161
|
+
};
|
|
1162
|
+
const cachedResponse = {
|
|
1163
|
+
pk: `identity#${pixelId}#identity456`,
|
|
1164
|
+
pixelId,
|
|
1165
|
+
identityId: 'identity456',
|
|
1166
|
+
response: {
|
|
1167
|
+
identityId: 'identity456',
|
|
1168
|
+
traits: { emails: ['test@email.com'] },
|
|
1169
|
+
},
|
|
1170
|
+
updatedAt: new Date().toISOString(),
|
|
1171
|
+
};
|
|
1172
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
1173
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCachingV2(pixelId, incomingIdentity, lambdaArn);
|
|
1174
|
+
expect(result.identity?.identityId).toBe('identity456');
|
|
1175
|
+
expect(result.summary).toEqual(expect.objectContaining({
|
|
1176
|
+
outcome: types_js_1.IDENTITY_CACHE_WITH_CACHING_OUTCOME.FRESH_CACHE_HIT,
|
|
1177
|
+
pixelId,
|
|
1178
|
+
identityId: 'identity456',
|
|
1179
|
+
didReadCache: true,
|
|
1180
|
+
didCallNeptune: false,
|
|
1181
|
+
didWriteCache: false,
|
|
1182
|
+
cachedIdentity: cachedResponse.response,
|
|
1183
|
+
incomingIdentity,
|
|
1184
|
+
resolvedIdentity: expect.objectContaining({ identityId: 'identity456' }),
|
|
1185
|
+
}));
|
|
1186
|
+
expect(mockInvokeFunction).not.toHaveBeenCalled();
|
|
1187
|
+
});
|
|
1188
|
+
it('should return Neptune write-back summary on cache miss', async () => {
|
|
1189
|
+
const incomingIdentity = {
|
|
1190
|
+
identityId: 'identity456',
|
|
1191
|
+
traits: { emails: ['test@email.com'] },
|
|
1192
|
+
};
|
|
1193
|
+
const neptuneIdentity = {
|
|
1194
|
+
identityId: 'resolved-identity',
|
|
1195
|
+
traits: { emails: ['test@email.com', 'resolved@email.com'] },
|
|
1196
|
+
};
|
|
1197
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
|
|
1198
|
+
mockInvokeFunction.mockResolvedValueOnce({
|
|
1199
|
+
statusCode: 200,
|
|
1200
|
+
body: JSON.stringify({ identity: neptuneIdentity }),
|
|
1201
|
+
});
|
|
1202
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCachingV2(pixelId, incomingIdentity, lambdaArn);
|
|
1203
|
+
expect(result.identity).toEqual(neptuneIdentity);
|
|
1204
|
+
expect(result.summary).toEqual(expect.objectContaining({
|
|
1205
|
+
outcome: types_js_1.IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_WRITE_BACK,
|
|
1206
|
+
pixelId,
|
|
1207
|
+
identityId: 'resolved-identity',
|
|
1208
|
+
didReadCache: true,
|
|
1209
|
+
didCallNeptune: true,
|
|
1210
|
+
didWriteCache: true,
|
|
1211
|
+
incomingIdentity,
|
|
1212
|
+
identityForNeptune: incomingIdentity,
|
|
1213
|
+
neptuneIdentity,
|
|
1214
|
+
traitChanges: { reason: { resolved: 'no cached identity' } },
|
|
1215
|
+
}));
|
|
1216
|
+
expect(mockedDynamoDbClient.safeBatchWrite).toHaveBeenCalled();
|
|
1217
|
+
});
|
|
1218
|
+
it('should return Neptune skip-write summary when Neptune matches cached identity', async () => {
|
|
1219
|
+
const incomingIdentity = {
|
|
1220
|
+
identityId: 'identity456',
|
|
1221
|
+
traits: { emails: ['old@email.com', 'new@email.com'] },
|
|
1222
|
+
};
|
|
1223
|
+
const cachedIdentity = {
|
|
1224
|
+
identityId: 'identity456',
|
|
1225
|
+
traits: { emails: ['old@email.com'] },
|
|
1226
|
+
};
|
|
1227
|
+
const cachedResponse = {
|
|
1228
|
+
pk: `identity#${pixelId}#identity456`,
|
|
1229
|
+
pixelId,
|
|
1230
|
+
identityId: 'identity456',
|
|
1231
|
+
response: cachedIdentity,
|
|
1232
|
+
updatedAt: new Date().toISOString(),
|
|
1233
|
+
};
|
|
1234
|
+
mockedDynamoDbClient.safeGet.mockResolvedValueOnce(cachedResponse);
|
|
1235
|
+
mockInvokeFunction.mockResolvedValueOnce({
|
|
1236
|
+
statusCode: 200,
|
|
1237
|
+
body: JSON.stringify({ identity: cachedIdentity }),
|
|
1238
|
+
});
|
|
1239
|
+
const result = await identity_gateway_service_js_1.IdentityGatewayService.getIdentityWithCachingV2(pixelId, incomingIdentity, lambdaArn);
|
|
1240
|
+
expect(result.identity).toEqual(cachedIdentity);
|
|
1241
|
+
expect(result.summary).toEqual(expect.objectContaining({
|
|
1242
|
+
outcome: types_js_1.IDENTITY_CACHE_WITH_CACHING_OUTCOME.NEPTUNE_SKIP_WRITE,
|
|
1243
|
+
pixelId,
|
|
1244
|
+
identityId: 'identity456',
|
|
1245
|
+
didReadCache: true,
|
|
1246
|
+
didCallNeptune: true,
|
|
1247
|
+
didWriteCache: false,
|
|
1248
|
+
cachedIdentity,
|
|
1249
|
+
incomingIdentity,
|
|
1250
|
+
identityForNeptune: expect.objectContaining({ identityId: 'identity456' }),
|
|
1251
|
+
neptuneIdentity: cachedIdentity,
|
|
1252
|
+
}));
|
|
1253
|
+
expect(mockedDynamoDbClient.safeBatchWrite).not.toHaveBeenCalled();
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1114
1256
|
describe('gsi1pk attribute on all records', () => {
|
|
1115
1257
|
const pixelId = 'pixel123';
|
|
1116
1258
|
it('should include gsi1pk on identity record', async () => {
|
|
@@ -1118,18 +1260,18 @@ describe('IdentityCacheDynamoDbService', () => {
|
|
|
1118
1260
|
identityId: 'identity456',
|
|
1119
1261
|
traits: { emails: ['test@email.com'] },
|
|
1120
1262
|
};
|
|
1121
|
-
await
|
|
1263
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
1122
1264
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
1123
1265
|
const identityItem = writtenItems.find((item) => item.pk.startsWith('identity#'));
|
|
1124
1266
|
expect(identityItem).toBeDefined();
|
|
1125
|
-
expect(identityItem
|
|
1267
|
+
expect(identityItem?.gsi1pk).toBe('identity456');
|
|
1126
1268
|
});
|
|
1127
1269
|
it('should include gsi1pk on all email pointer records', async () => {
|
|
1128
1270
|
const identity = {
|
|
1129
1271
|
identityId: 'identity456',
|
|
1130
1272
|
traits: { emails: ['email1@test.com', 'email2@test.com'] },
|
|
1131
1273
|
};
|
|
1132
|
-
await
|
|
1274
|
+
await identity_cache_dynamodb_db_service_js_1.IdentityCacheDynamoDbDbService.updateIdentityCache(pixelId, identity);
|
|
1133
1275
|
const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
|
|
1134
1276
|
const emailItems = writtenItems.filter((item) => item.pk.startsWith('email#'));
|
|
1135
1277
|
for (const emailItem of emailItems) {
|