@adtrackify/at-service-common 3.19.17 → 3.19.19

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 (51) hide show
  1. package/dist/cjs/__tests__/clients/dynamodb-client.spec.js +161 -121
  2. package/dist/cjs/__tests__/clients/dynamodb-client.spec.js.map +1 -1
  3. package/dist/cjs/__tests__/db/destinations-db-service.spec.d.ts +1 -0
  4. package/dist/cjs/__tests__/db/destinations-db-service.spec.js +126 -0
  5. package/dist/cjs/__tests__/db/destinations-db-service.spec.js.map +1 -0
  6. package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -0
  7. package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.js +105 -0
  8. package/dist/cjs/__tests__/db/shopify-app-installs-db-service.spec.js.map +1 -0
  9. package/dist/cjs/__tests__/db/subscriptions-db-service.spec.d.ts +1 -0
  10. package/dist/cjs/__tests__/db/subscriptions-db-service.spec.js +96 -0
  11. package/dist/cjs/__tests__/db/subscriptions-db-service.spec.js.map +1 -0
  12. package/dist/cjs/__tests__/db/user-accounts-db-service.spec.d.ts +1 -0
  13. package/dist/cjs/__tests__/db/user-accounts-db-service.spec.js +77 -0
  14. package/dist/cjs/__tests__/db/user-accounts-db-service.spec.js.map +1 -0
  15. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +56 -47
  16. package/dist/cjs/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
  17. package/dist/cjs/clients/generic/dynamodb-client.js +10 -16
  18. package/dist/cjs/clients/generic/dynamodb-client.js.map +1 -1
  19. package/dist/cjs/services/db/destinations-db-service.js +2 -8
  20. package/dist/cjs/services/db/destinations-db-service.js.map +1 -1
  21. package/dist/cjs/services/db/identity-cache-dynamodb-service.d.ts +3 -3
  22. package/dist/cjs/services/db/identity-cache-dynamodb-service.js +55 -86
  23. package/dist/cjs/services/db/identity-cache-dynamodb-service.js.map +1 -1
  24. package/dist/cjs/services/db/shopify-app-installs-db-service.js +4 -10
  25. package/dist/cjs/services/db/shopify-app-installs-db-service.js.map +1 -1
  26. package/dist/esm/__tests__/clients/dynamodb-client.spec.js +161 -121
  27. package/dist/esm/__tests__/clients/dynamodb-client.spec.js.map +1 -1
  28. package/dist/esm/__tests__/db/destinations-db-service.spec.d.ts +1 -0
  29. package/dist/esm/__tests__/db/destinations-db-service.spec.js +124 -0
  30. package/dist/esm/__tests__/db/destinations-db-service.spec.js.map +1 -0
  31. package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.d.ts +1 -0
  32. package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.js +103 -0
  33. package/dist/esm/__tests__/db/shopify-app-installs-db-service.spec.js.map +1 -0
  34. package/dist/esm/__tests__/db/subscriptions-db-service.spec.d.ts +1 -0
  35. package/dist/esm/__tests__/db/subscriptions-db-service.spec.js +94 -0
  36. package/dist/esm/__tests__/db/subscriptions-db-service.spec.js.map +1 -0
  37. package/dist/esm/__tests__/db/user-accounts-db-service.spec.d.ts +1 -0
  38. package/dist/esm/__tests__/db/user-accounts-db-service.spec.js +75 -0
  39. package/dist/esm/__tests__/db/user-accounts-db-service.spec.js.map +1 -0
  40. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js +56 -47
  41. package/dist/esm/__tests__/identity-cache/identity-cache-dynamodb-service.spec.js.map +1 -1
  42. package/dist/esm/clients/generic/dynamodb-client.js +10 -16
  43. package/dist/esm/clients/generic/dynamodb-client.js.map +1 -1
  44. package/dist/esm/services/db/destinations-db-service.js +2 -8
  45. package/dist/esm/services/db/destinations-db-service.js.map +1 -1
  46. package/dist/esm/services/db/identity-cache-dynamodb-service.d.ts +3 -3
  47. package/dist/esm/services/db/identity-cache-dynamodb-service.js +56 -87
  48. package/dist/esm/services/db/identity-cache-dynamodb-service.js.map +1 -1
  49. package/dist/esm/services/db/shopify-app-installs-db-service.js +4 -10
  50. package/dist/esm/services/db/shopify-app-installs-db-service.js.map +1 -1
  51. package/package.json +1 -1
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock('../../clients/generic/dynamodb-client.js', () => ({
4
+ DynamoDbClient: {
5
+ safeGet: jest.fn(),
6
+ safeQueryByGSI: jest.fn(),
7
+ safeBatchGet: jest.fn(),
8
+ queryAll: jest.fn(),
9
+ },
10
+ }));
11
+ jest.mock('../../helpers/index.js', () => ({
12
+ Logger: {
13
+ info: jest.fn(),
14
+ warn: jest.fn(),
15
+ error: jest.fn(),
16
+ debug: jest.fn(),
17
+ },
18
+ }));
19
+ const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
20
+ const shopify_app_installs_db_service_js_1 = require("../../services/db/shopify-app-installs-db-service.js");
21
+ const mockSafeBatchGet = dynamodb_client_js_1.DynamoDbClient.safeBatchGet;
22
+ const mockSafeGet = dynamodb_client_js_1.DynamoDbClient.safeGet;
23
+ describe('ShopifyAppInstallsDbService', () => {
24
+ const TABLE_NAME = 'test-shopify-app-installs-table';
25
+ const TABLE_KEY = 'id';
26
+ const service = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService(TABLE_NAME, TABLE_KEY);
27
+ beforeEach(() => {
28
+ jest.clearAllMocks();
29
+ });
30
+ afterEach(() => {
31
+ jest.restoreAllMocks();
32
+ });
33
+ describe('batchGetByIds', () => {
34
+ it('returns empty array for empty ids input', async () => {
35
+ mockSafeBatchGet.mockResolvedValueOnce([]);
36
+ const result = await service.batchGetByIds([]);
37
+ expect(result).toEqual([]);
38
+ expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, []);
39
+ });
40
+ it('returns installs for single id', async () => {
41
+ const mockInstall = {
42
+ id: 'install-1',
43
+ shop: 'test-shop.myshopify.com',
44
+ pixelId: 'px-1',
45
+ };
46
+ mockSafeBatchGet.mockResolvedValueOnce([mockInstall]);
47
+ const result = await service.batchGetByIds(['install-1']);
48
+ expect(result).toEqual([mockInstall]);
49
+ expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [{ id: 'install-1' }]);
50
+ });
51
+ it('returns installs for multiple ids', async () => {
52
+ const mockInstalls = [
53
+ { id: 'install-1', shop: 'shop1.myshopify.com', pixelId: 'px-1' },
54
+ { id: 'install-2', shop: 'shop2.myshopify.com', pixelId: 'px-2' },
55
+ { id: 'install-3', shop: 'shop3.myshopify.com', pixelId: 'px-3' },
56
+ ];
57
+ mockSafeBatchGet.mockResolvedValueOnce(mockInstalls);
58
+ const result = await service.batchGetByIds(['install-1', 'install-2', 'install-3']);
59
+ expect(result).toEqual(mockInstalls);
60
+ expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [
61
+ { id: 'install-1' },
62
+ { id: 'install-2' },
63
+ { id: 'install-3' },
64
+ ]);
65
+ });
66
+ it('returns empty array when safeBatchGet returns null', async () => {
67
+ mockSafeBatchGet.mockResolvedValueOnce(null);
68
+ const result = await service.batchGetByIds(['install-1']);
69
+ expect(result).toEqual([]);
70
+ });
71
+ it('uses custom table key from constructor', async () => {
72
+ const customService = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService(TABLE_NAME, 'customKey');
73
+ mockSafeBatchGet.mockResolvedValueOnce([]);
74
+ await customService.batchGetByIds(['install-1', 'install-2']);
75
+ expect(mockSafeBatchGet).toHaveBeenCalledWith(TABLE_NAME, [
76
+ { customKey: 'install-1' },
77
+ { customKey: 'install-2' },
78
+ ]);
79
+ });
80
+ it('uses default table key "id" when not specified', async () => {
81
+ const defaultKeyService = new shopify_app_installs_db_service_js_1.ShopifyAppInstallsDbService('another-table');
82
+ mockSafeBatchGet.mockResolvedValueOnce([]);
83
+ await defaultKeyService.batchGetByIds(['install-1']);
84
+ expect(mockSafeBatchGet).toHaveBeenCalledWith('another-table', [{ id: 'install-1' }]);
85
+ });
86
+ });
87
+ describe('getShopifyAppInstallById', () => {
88
+ it('returns install when found', async () => {
89
+ const mockInstall = {
90
+ id: 'install-1',
91
+ shop: 'test-shop.myshopify.com',
92
+ };
93
+ mockSafeGet.mockResolvedValueOnce(mockInstall);
94
+ const result = await service.getShopifyAppInstallById('id', 'install-1');
95
+ expect(result).toEqual(mockInstall);
96
+ expect(mockSafeGet).toHaveBeenCalledWith(TABLE_NAME, 'id', 'install-1');
97
+ });
98
+ it('returns null when not found', async () => {
99
+ mockSafeGet.mockResolvedValueOnce(null);
100
+ const result = await service.getShopifyAppInstallById('id', 'nonexistent');
101
+ expect(result).toBeNull();
102
+ });
103
+ });
104
+ });
105
+ //# sourceMappingURL=shopify-app-installs-db-service.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shopify-app-installs-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/shopify-app-installs-db-service.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock('../../clients/generic/dynamodb-client.js', () => ({
4
+ DynamoDbClient: {
5
+ safeGet: jest.fn(),
6
+ safeQueryByGSI: jest.fn(),
7
+ safeBatchQueryByGSI: jest.fn(),
8
+ },
9
+ }));
10
+ jest.mock('../../helpers/index.js', () => ({
11
+ Logger: {
12
+ info: jest.fn(),
13
+ warn: jest.fn(),
14
+ error: jest.fn(),
15
+ debug: jest.fn(),
16
+ },
17
+ }));
18
+ const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
19
+ const subscriptions_db_service_js_1 = require("../../services/db/subscriptions-db-service.js");
20
+ const mockSafeBatchQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeBatchQueryByGSI;
21
+ const mockSafeQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeQueryByGSI;
22
+ describe('SubscriptionsDbService', () => {
23
+ const TABLE_NAME = 'test-subscriptions-table';
24
+ const service = new subscriptions_db_service_js_1.SubscriptionsDbService(TABLE_NAME);
25
+ beforeEach(() => {
26
+ jest.clearAllMocks();
27
+ });
28
+ afterEach(() => {
29
+ jest.restoreAllMocks();
30
+ });
31
+ describe('batchGetByAccountIds', () => {
32
+ const INDEX_NAME = 'GSI_ACCOUNT_ID';
33
+ const ACCOUNT_ID_KEY = 'accountId';
34
+ it('returns empty array for empty accountIds input', async () => {
35
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
36
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, []);
37
+ expect(result).toEqual([]);
38
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, [], 10);
39
+ });
40
+ it('returns subscriptions for single accountId', async () => {
41
+ const mockSubscription = {
42
+ id: 'sub-1',
43
+ accountId: 'acc-1',
44
+ plan: 'pro',
45
+ };
46
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([mockSubscription]);
47
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
48
+ expect(result).toEqual([mockSubscription]);
49
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
50
+ });
51
+ it('returns subscriptions for multiple accountIds', async () => {
52
+ const mockSubscriptions = [
53
+ { id: 'sub-1', accountId: 'acc-1', plan: 'pro' },
54
+ { id: 'sub-2', accountId: 'acc-2', plan: 'starter' },
55
+ { id: 'sub-3', accountId: 'acc-2', plan: 'addon' },
56
+ ];
57
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce(mockSubscriptions);
58
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2']);
59
+ expect(result).toEqual(mockSubscriptions);
60
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2'], 10);
61
+ });
62
+ it('uses custom concurrency parameter', async () => {
63
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
64
+ await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
65
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
66
+ });
67
+ it('calls safeBatchQueryByGSI with correct table name from constructor', async () => {
68
+ const customService = new subscriptions_db_service_js_1.SubscriptionsDbService('custom-table');
69
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
70
+ await customService.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
71
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith('custom-table', INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
72
+ });
73
+ });
74
+ describe('getSubscriptionByAccountId', () => {
75
+ it('returns first subscription when found', async () => {
76
+ const mockSubscriptions = [
77
+ { id: 'sub-1', accountId: 'acc-1' },
78
+ { id: 'sub-2', accountId: 'acc-1' },
79
+ ];
80
+ mockSafeQueryByGSI.mockResolvedValueOnce(mockSubscriptions);
81
+ const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
82
+ expect(result).toEqual({ id: 'sub-1', accountId: 'acc-1' });
83
+ });
84
+ it('returns null when no subscriptions found', async () => {
85
+ mockSafeQueryByGSI.mockResolvedValueOnce([]);
86
+ const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
87
+ expect(result).toBeNull();
88
+ });
89
+ it('returns null when query returns null', async () => {
90
+ mockSafeQueryByGSI.mockResolvedValueOnce(null);
91
+ const result = await service.getSubscriptionByAccountId('GSI', 'accountId', 'acc-1');
92
+ expect(result).toBeNull();
93
+ });
94
+ });
95
+ });
96
+ //# sourceMappingURL=subscriptions-db-service.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriptions-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/subscriptions-db-service.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ jest.mock('../../clients/generic/dynamodb-client.js', () => ({
4
+ DynamoDbClient: {
5
+ safeBatchQueryByGSI: jest.fn(),
6
+ },
7
+ }));
8
+ const dynamodb_client_js_1 = require("../../clients/generic/dynamodb-client.js");
9
+ const user_accounts_db_service_js_1 = require("../../services/db/user-accounts-db-service.js");
10
+ const mockSafeBatchQueryByGSI = dynamodb_client_js_1.DynamoDbClient.safeBatchQueryByGSI;
11
+ describe('UserAccountsDbService', () => {
12
+ const TABLE_NAME = 'test-user-accounts-table';
13
+ const service = new user_accounts_db_service_js_1.UserAccountsDbService(TABLE_NAME);
14
+ beforeEach(() => {
15
+ jest.clearAllMocks();
16
+ });
17
+ afterEach(() => {
18
+ jest.restoreAllMocks();
19
+ });
20
+ describe('batchGetByAccountIds', () => {
21
+ const INDEX_NAME = 'GSI_ACCOUNT_ID';
22
+ const ACCOUNT_ID_KEY = 'accountId';
23
+ it('returns empty array for empty accountIds input', async () => {
24
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
25
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, []);
26
+ expect(result).toEqual([]);
27
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, [], 10);
28
+ });
29
+ it('returns user-account records for single accountId', async () => {
30
+ const mockRecord = {
31
+ userId: 'user-1',
32
+ accountId: 'acc-1',
33
+ role: 'admin',
34
+ };
35
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([mockRecord]);
36
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
37
+ expect(result).toEqual([mockRecord]);
38
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
39
+ });
40
+ it('returns user-account records for multiple accountIds', async () => {
41
+ const mockRecords = [
42
+ { userId: 'user-1', accountId: 'acc-1', role: 'admin' },
43
+ { userId: 'user-2', accountId: 'acc-1', role: 'member' },
44
+ { userId: 'user-3', accountId: 'acc-2', role: 'owner' },
45
+ ];
46
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce(mockRecords);
47
+ const result = await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2']);
48
+ expect(result).toEqual(mockRecords);
49
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2'], 10);
50
+ });
51
+ it('uses custom concurrency parameter', async () => {
52
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
53
+ await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
54
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 5);
55
+ });
56
+ it('uses default concurrency of 10', async () => {
57
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
58
+ await service.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2', 'acc-3']);
59
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith(TABLE_NAME, INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1', 'acc-2', 'acc-3'], 10);
60
+ });
61
+ it('calls safeBatchQueryByGSI with correct table name from constructor', async () => {
62
+ const customService = new user_accounts_db_service_js_1.UserAccountsDbService('custom-table');
63
+ mockSafeBatchQueryByGSI.mockResolvedValueOnce([]);
64
+ await customService.batchGetByAccountIds(INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1']);
65
+ expect(mockSafeBatchQueryByGSI).toHaveBeenCalledWith('custom-table', INDEX_NAME, ACCOUNT_ID_KEY, ['acc-1'], 10);
66
+ });
67
+ it('uses default table key "userId" when not specified', async () => {
68
+ const defaultKeyService = new user_accounts_db_service_js_1.UserAccountsDbService('another-table');
69
+ expect(defaultKeyService.TABLE_KEY).toBe('userId');
70
+ });
71
+ it('uses custom table key when specified', async () => {
72
+ const customKeyService = new user_accounts_db_service_js_1.UserAccountsDbService('another-table', 'customKey');
73
+ expect(customKeyService.TABLE_KEY).toBe('customKey');
74
+ });
75
+ });
76
+ });
77
+ //# sourceMappingURL=user-accounts-db-service.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user-accounts-db-service.spec.js","sourceRoot":"","sources":["../../../../src/__tests__/db/user-accounts-db-service.spec.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,15 @@ const mockedDynamoDbClient = index_js_1.DynamoDbClient;
29
29
  describe('IdentityCacheDynamoDbService', () => {
30
30
  beforeEach(() => {
31
31
  jest.clearAllMocks();
32
+ mockedDynamoDbClient.safeGet.mockReset();
33
+ mockedDynamoDbClient.safePut.mockReset();
34
+ mockedDynamoDbClient.safeDelete.mockReset();
35
+ mockedDynamoDbClient.safeBatchGet.mockReset();
36
+ mockedDynamoDbClient.safeBatchWrite.mockReset();
37
+ mockedDynamoDbClient.safeQueryByGSI.mockReset();
38
+ mockedDynamoDbClient.batchWrite.mockReset();
39
+ mockInvokeFunction.mockReset();
40
+ identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.lambdaInvokeClient = undefined;
32
41
  });
33
42
  describe('getIdentityWithCaching', () => {
34
43
  const pixelId = 'pixel123';
@@ -155,7 +164,7 @@ describe('IdentityCacheDynamoDbService', () => {
155
164
  const incomingIdentity = {
156
165
  traits: { emails: ['test@email.com'] },
157
166
  };
158
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
167
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
159
168
  mockInvokeFunction.mockResolvedValueOnce({
160
169
  statusCode: 200,
161
170
  body: JSON.stringify({
@@ -175,7 +184,7 @@ describe('IdentityCacheDynamoDbService', () => {
175
184
  const incomingIdentity = {
176
185
  traits: { emails: ['test@email.com'] },
177
186
  };
178
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
187
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
179
188
  mockInvokeFunction.mockResolvedValueOnce({
180
189
  statusCode: 200,
181
190
  body: JSON.stringify({ identity: undefined }),
@@ -209,7 +218,7 @@ describe('IdentityCacheDynamoDbService', () => {
209
218
  const incomingIdentity = {
210
219
  traits: { emails: ['test@email.com'] },
211
220
  };
212
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
221
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
213
222
  mockInvokeFunction.mockResolvedValueOnce({
214
223
  statusCode: 200,
215
224
  body: JSON.stringify({
@@ -239,26 +248,22 @@ describe('IdentityCacheDynamoDbService', () => {
239
248
  it('should build correct pk for email lookup (lowercase)', async () => {
240
249
  const pixelId = 'pixel123';
241
250
  const email = 'Test@Email.COM';
242
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
251
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
243
252
  await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
244
253
  traits: { emails: [email] },
245
254
  });
246
- expect(mockedDynamoDbClient.safeBatchGet).toHaveBeenCalledWith(expect.any(String), [
247
- { pk: `email#${pixelId}#test@email.com` },
248
- ]);
255
+ expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
249
256
  });
250
257
  });
251
- describe('buildUserIdPk', () => {
252
- it('should build correct pk for userId lookup (trimmed)', async () => {
258
+ describe('buildUserIdPk (no longer used for lookup)', () => {
259
+ it('should NOT lookup by userId (optimization: email-only secondary lookup)', async () => {
253
260
  const pixelId = 'pixel123';
254
261
  const userId = ' user789 ';
255
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
262
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
256
263
  await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
257
264
  traits: { userIds: [userId] },
258
265
  });
259
- expect(mockedDynamoDbClient.safeBatchGet).toHaveBeenCalledWith(expect.any(String), [
260
- { pk: `user_id#${pixelId}#user789` },
261
- ]);
266
+ expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
262
267
  });
263
268
  });
264
269
  });
@@ -324,66 +329,62 @@ describe('IdentityCacheDynamoDbService', () => {
324
329
  });
325
330
  });
326
331
  describe('without identityId (secondary lookup)', () => {
327
- it('should use batch get for email and userId lookups', async () => {
328
- const cachedResponse = {
332
+ it('should use single get for email lookup (pointer-based)', async () => {
333
+ const pointerRecord = {
329
334
  pk: `email#${pixelId}#test@email.com`,
330
335
  pixelId,
331
336
  identityId: 'resolved-id',
332
- response: {
333
- identityId: 'resolved-id',
334
- traits: { emails: ['test@email.com'] },
335
- },
337
+ gsi1pk: 'resolved-id',
336
338
  updatedAt: new Date().toISOString(),
337
339
  };
338
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([cachedResponse]);
340
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
339
341
  const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
340
342
  traits: { emails: ['test@email.com'], userIds: ['user123'] },
341
343
  });
342
- expect(mockedDynamoDbClient.safeBatchGet).toHaveBeenCalledWith(expect.any(String), [
343
- { pk: `email#${pixelId}#test@email.com` },
344
- { pk: `user_id#${pixelId}#user123` },
345
- ]);
344
+ expect(mockedDynamoDbClient.safeGet).toHaveBeenCalledWith(expect.any(String), 'pk', `email#${pixelId}#test@email.com`);
345
+ expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
346
346
  expect(result.resolvedIdentity).toBeDefined();
347
347
  expect(result.resolvedIdentity?.traits?.emails).toContain('test@email.com');
348
348
  expect(result.isCacheStale).toBe(true);
349
349
  });
350
- it('should detect conflict when multiple identityIds found', async () => {
351
- const response1 = {
350
+ it('should return discovered identityId with stale flag for Neptune resolution', async () => {
351
+ const pointerRecord = {
352
352
  pk: `email#${pixelId}#test@email.com`,
353
353
  pixelId,
354
- identityId: 'identity-1',
355
- response: { identityId: 'identity-1' },
356
- updatedAt: new Date().toISOString(),
357
- };
358
- const response2 = {
359
- pk: `user_id#${pixelId}#user123`,
360
- pixelId,
361
- identityId: 'identity-2',
362
- response: { identityId: 'identity-2' },
354
+ identityId: 'discovered-id',
355
+ gsi1pk: 'discovered-id',
363
356
  updatedAt: new Date().toISOString(),
364
357
  };
365
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([response1, response2]);
358
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(pointerRecord);
366
359
  const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
367
360
  traits: { emails: ['test@email.com'], userIds: ['user123'] },
368
361
  });
369
- expect(result.resolvedIdentity).toBeUndefined();
362
+ expect(result.resolvedIdentity).toBeDefined();
363
+ expect(result.resolvedIdentity?.identityId).toBe('discovered-id');
370
364
  expect(result.isCacheStale).toBe(true);
371
365
  });
372
366
  it('should return cache miss when no secondary keys match', async () => {
373
- mockedDynamoDbClient.safeBatchGet.mockResolvedValueOnce([]);
367
+ mockedDynamoDbClient.safeGet.mockResolvedValueOnce(null);
374
368
  const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
375
369
  traits: { emails: ['unknown@email.com'] },
376
370
  });
377
371
  expect(result.resolvedIdentity).toBeUndefined();
378
372
  expect(result.isCacheStale).toBe(true);
379
373
  });
380
- it('should return cache miss when no secondary keys to lookup', async () => {
374
+ it('should return cache miss when no emails to lookup', async () => {
375
+ const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
376
+ traits: { userIds: ['user123'] },
377
+ });
378
+ expect(result.resolvedIdentity).toBeUndefined();
379
+ expect(result.isCacheStale).toBe(true);
380
+ expect(mockedDynamoDbClient.safeGet).not.toHaveBeenCalled();
381
+ });
382
+ it('should return cache miss when empty traits', async () => {
381
383
  const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
382
384
  traits: {},
383
385
  });
384
386
  expect(result.resolvedIdentity).toBeUndefined();
385
387
  expect(result.isCacheStale).toBe(true);
386
- expect(mockedDynamoDbClient.safeBatchGet).not.toHaveBeenCalled();
387
388
  });
388
389
  });
389
390
  describe('error handling (fail-open)', () => {
@@ -395,8 +396,8 @@ describe('IdentityCacheDynamoDbService', () => {
395
396
  expect(result.resolvedIdentity).toBeUndefined();
396
397
  expect(result.isCacheStale).toBe(true);
397
398
  });
398
- it('should return cache miss on safeBatchGet error', async () => {
399
- mockedDynamoDbClient.safeBatchGet.mockRejectedValueOnce(new Error('DynamoDB error'));
399
+ it('should return cache miss on safeGet error for email lookup', async () => {
400
+ mockedDynamoDbClient.safeGet.mockRejectedValueOnce(new Error('DynamoDB error'));
400
401
  const result = await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.getIdentityFromCache(pixelId, {
401
402
  traits: { emails: ['test@email.com'] },
402
403
  });
@@ -419,7 +420,7 @@ describe('IdentityCacheDynamoDbService', () => {
419
420
  });
420
421
  describe('updateIdentityCache', () => {
421
422
  const pixelId = 'pixel123';
422
- it('should write identity with all secondary keys', async () => {
423
+ it('should write identity with email pointer (no full blob on secondary keys)', async () => {
423
424
  const identity = {
424
425
  identityId: 'identity456',
425
426
  traits: {
@@ -430,13 +431,21 @@ describe('IdentityCacheDynamoDbService', () => {
430
431
  await identity_cache_dynamodb_service_js_1.IdentityCacheDynamoDbService.updateIdentityCache(pixelId, identity);
431
432
  expect(mockedDynamoDbClient.safeBatchWrite).toHaveBeenCalledTimes(1);
432
433
  const writtenItems = mockedDynamoDbClient.safeBatchWrite.mock.calls[0][1];
433
- expect(writtenItems).toHaveLength(5);
434
+ expect(writtenItems).toHaveLength(2);
434
435
  const pks = writtenItems.map((item) => item.pk);
435
436
  expect(pks).toContain(`identity#${pixelId}#identity456`);
436
437
  expect(pks).toContain(`email#${pixelId}#test@email.com`);
437
- expect(pks).toContain(`email#${pixelId}#other@email.com`);
438
- expect(pks).toContain(`user_id#${pixelId}#user1`);
439
- expect(pks).toContain(`user_id#${pixelId}#user2`);
438
+ expect(pks).not.toContain(`user_id#${pixelId}#user1`);
439
+ expect(pks).not.toContain(`user_id#${pixelId}#user2`);
440
+ expect(pks).not.toContain(`email#${pixelId}#other@email.com`);
441
+ const identityItem = writtenItems.find((item) => item.pk.startsWith('identity#'));
442
+ expect(identityItem).toBeDefined();
443
+ expect(identityItem.response).toBeDefined();
444
+ expect(identityItem.response.identityId).toBe('identity456');
445
+ const emailItem = writtenItems.find((item) => item.pk.startsWith('email#'));
446
+ expect(emailItem).toBeDefined();
447
+ expect(emailItem.response).toBeUndefined();
448
+ expect(emailItem.identityId).toBe('identity456');
440
449
  });
441
450
  it('should not throw on safeBatchWrite error (fail-open)', async () => {
442
451
  mockedDynamoDbClient.safeBatchWrite.mockRejectedValueOnce(new Error('DynamoDB error'));