@adtrackify/at-service-common 4.0.5 → 4.0.7

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