@zapier/zapier-sdk 0.18.2 → 0.18.4

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 (85) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +13 -13
  3. package/dist/api/client.d.ts.map +1 -1
  4. package/dist/api/client.js +0 -18
  5. package/dist/api/schemas.d.ts +0 -158
  6. package/dist/api/schemas.d.ts.map +1 -1
  7. package/dist/api/schemas.js +0 -95
  8. package/dist/api/types.d.ts +2 -1
  9. package/dist/api/types.d.ts.map +1 -1
  10. package/dist/index.cjs +400 -808
  11. package/dist/index.d.mts +67 -122
  12. package/dist/index.mjs +401 -809
  13. package/dist/plugins/findFirstAuthentication/index.test.js +2 -2
  14. package/dist/plugins/findUniqueAuthentication/index.test.js +2 -2
  15. package/dist/plugins/listApps/index.d.ts +2 -8
  16. package/dist/plugins/listApps/index.d.ts.map +1 -1
  17. package/dist/plugins/listApps/index.js +4 -6
  18. package/dist/plugins/listApps/index.test.js +62 -82
  19. package/dist/plugins/listApps/schemas.d.ts +35 -14
  20. package/dist/plugins/listApps/schemas.d.ts.map +1 -1
  21. package/dist/plugins/listApps/schemas.js +44 -14
  22. package/dist/plugins/listAuthentications/index.d.ts +4 -10
  23. package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
  24. package/dist/plugins/listAuthentications/index.js +19 -31
  25. package/dist/plugins/listAuthentications/index.test.js +363 -127
  26. package/dist/plugins/listAuthentications/schemas.d.ts +7 -7
  27. package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
  28. package/dist/plugins/listAuthentications/schemas.js +10 -16
  29. package/dist/schemas/App.d.ts +28 -28
  30. package/dist/schemas/App.d.ts.map +1 -1
  31. package/dist/schemas/App.js +3 -8
  32. package/dist/sdk.d.ts +2 -2
  33. package/dist/sdk.test.js +12 -9
  34. package/dist/utils/domain-utils.d.ts +2 -15
  35. package/dist/utils/domain-utils.d.ts.map +1 -1
  36. package/dist/utils/domain-utils.js +0 -38
  37. package/dist/utils/function-utils.d.ts +1 -0
  38. package/dist/utils/function-utils.d.ts.map +1 -1
  39. package/dist/utils/function-utils.js +15 -3
  40. package/package.json +2 -2
  41. package/dist/api/client.integration.test.d.ts +0 -5
  42. package/dist/api/client.integration.test.d.ts.map +0 -1
  43. package/dist/api/client.integration.test.js +0 -318
  44. package/dist/api/client.methods.test.d.ts +0 -2
  45. package/dist/api/client.methods.test.d.ts.map +0 -1
  46. package/dist/api/client.methods.test.js +0 -158
  47. package/dist/api/router.d.ts +0 -16
  48. package/dist/api/router.d.ts.map +0 -1
  49. package/dist/api/router.js +0 -31
  50. package/dist/api/router.test.d.ts +0 -2
  51. package/dist/api/router.test.d.ts.map +0 -1
  52. package/dist/api/router.test.js +0 -103
  53. package/dist/temporary-internal-core/handlers/listApps.d.ts +0 -67
  54. package/dist/temporary-internal-core/handlers/listApps.d.ts.map +0 -1
  55. package/dist/temporary-internal-core/handlers/listApps.js +0 -134
  56. package/dist/temporary-internal-core/handlers/listApps.test.d.ts +0 -2
  57. package/dist/temporary-internal-core/handlers/listApps.test.d.ts.map +0 -1
  58. package/dist/temporary-internal-core/handlers/listApps.test.js +0 -367
  59. package/dist/temporary-internal-core/index.d.ts +0 -18
  60. package/dist/temporary-internal-core/index.d.ts.map +0 -1
  61. package/dist/temporary-internal-core/index.js +0 -18
  62. package/dist/temporary-internal-core/schemas/apps/index.d.ts +0 -175
  63. package/dist/temporary-internal-core/schemas/apps/index.d.ts.map +0 -1
  64. package/dist/temporary-internal-core/schemas/apps/index.js +0 -97
  65. package/dist/temporary-internal-core/schemas/errors/index.d.ts +0 -139
  66. package/dist/temporary-internal-core/schemas/errors/index.d.ts.map +0 -1
  67. package/dist/temporary-internal-core/schemas/errors/index.js +0 -129
  68. package/dist/temporary-internal-core/schemas/implementations/index.d.ts +0 -127
  69. package/dist/temporary-internal-core/schemas/implementations/index.d.ts.map +0 -1
  70. package/dist/temporary-internal-core/schemas/implementations/index.js +0 -79
  71. package/dist/temporary-internal-core/types/handler.d.ts +0 -51
  72. package/dist/temporary-internal-core/types/handler.d.ts.map +0 -1
  73. package/dist/temporary-internal-core/types/handler.js +0 -8
  74. package/dist/temporary-internal-core/types/index.d.ts +0 -5
  75. package/dist/temporary-internal-core/types/index.d.ts.map +0 -1
  76. package/dist/temporary-internal-core/types/index.js +0 -4
  77. package/dist/temporary-internal-core/utils/app-locators.d.ts +0 -34
  78. package/dist/temporary-internal-core/utils/app-locators.d.ts.map +0 -1
  79. package/dist/temporary-internal-core/utils/app-locators.js +0 -39
  80. package/dist/temporary-internal-core/utils/string-utils.d.ts +0 -28
  81. package/dist/temporary-internal-core/utils/string-utils.d.ts.map +0 -1
  82. package/dist/temporary-internal-core/utils/string-utils.js +0 -52
  83. package/dist/temporary-internal-core/utils/transformations.d.ts +0 -32
  84. package/dist/temporary-internal-core/utils/transformations.d.ts.map +0 -1
  85. package/dist/temporary-internal-core/utils/transformations.js +0 -74
@@ -3,38 +3,49 @@ import { ZapierValidationError, ZapierAuthenticationError, } from "../../types/e
3
3
  import { listAuthenticationsPlugin } from "./index";
4
4
  import { createSdk } from "../../sdk";
5
5
  import { eventEmissionPlugin } from "../eventEmission";
6
+ // Mock transport for testing - prevents real HTTP telemetry requests
7
+ const mockTransport = {
8
+ emit: vi.fn().mockResolvedValue(undefined),
9
+ close: vi.fn().mockResolvedValue(undefined),
10
+ };
11
+ vi.mock("../eventEmission/transport", () => ({
12
+ createTransport: vi.fn(() => mockTransport),
13
+ }));
14
+ // Mock CLI login package - prevents real token resolution requests
15
+ vi.mock("@zapier/zapier-sdk-cli-login", () => ({
16
+ getToken: vi.fn().mockResolvedValue(undefined),
17
+ }));
18
+ // Mock response in the new API format (data array, not results)
6
19
  const mockAuthenticationsResponse = {
7
- results: [
20
+ data: [
8
21
  {
9
- id: 123,
22
+ id: "123",
10
23
  date: "2021-01-01",
11
- account_id: 456,
12
- selected_api: "SlackCLIAPI@1.21.1",
24
+ account_id: "456",
25
+ implementation_id: "SlackCLIAPI@1.21.1",
13
26
  is_invite_only: false,
14
27
  is_private: false,
15
28
  shared_with_all: false,
16
- is_stale: "false",
17
- marked_stale_at: null,
29
+ is_expired: "false",
30
+ expired_at: null,
18
31
  label: "My Slack Workspace",
19
32
  title: "My Slack Workspace",
20
33
  },
21
34
  {
22
- id: 789,
35
+ id: "789",
23
36
  date: "2021-02-01",
24
- account_id: 456,
25
- selected_api: "GitHubAPI@2.1.0",
37
+ account_id: "456",
38
+ implementation_id: "GitHubAPI@2.1.0",
26
39
  is_invite_only: false,
27
40
  is_private: false,
28
41
  shared_with_all: true,
29
- is_stale: "true",
30
- marked_stale_at: "2021-06-01",
42
+ is_expired: "true",
43
+ expired_at: "2021-06-01",
31
44
  label: "GitHub Integration",
32
45
  title: "GitHub Integration",
33
46
  },
34
47
  ],
35
- count: 2,
36
- next: null,
37
- previous: null,
48
+ nextCursor: undefined,
38
49
  };
39
50
  describe("listAuthentications plugin", () => {
40
51
  let mockApiClient;
@@ -205,40 +216,45 @@ describe("listAuthentications plugin", () => {
205
216
  });
206
217
  });
207
218
  describe("data mapping", () => {
208
- it("should map is_stale to is_expired and marked_stale_at to expired_at", async () => {
219
+ it("should pass through is_expired and expired_at fields", async () => {
209
220
  const sdk = createTestSdk();
210
221
  const result = await sdk.listAuthentications();
222
+ // API returns these fields directly (no client-side transformation)
211
223
  expect(result.data[0].is_expired).toBe("false");
212
224
  expect(result.data[0].expired_at).toBe(null);
213
225
  expect(result.data[1].is_expired).toBe("true");
214
226
  expect(result.data[1].expired_at).toBe("2021-06-01");
215
227
  });
216
- it("should coerce title from label when title is missing", async () => {
228
+ it("should pass through label field when title is missing", async () => {
217
229
  const responseWithoutTitle = {
218
230
  ...mockAuthenticationsResponse,
219
- results: [
231
+ data: [
220
232
  {
221
- ...mockAuthenticationsResponse.results[0],
233
+ ...mockAuthenticationsResponse.data[0],
222
234
  title: undefined,
223
235
  label: "Label Only Auth",
224
236
  },
225
237
  ],
238
+ nextCursor: undefined,
226
239
  };
227
240
  mockApiClient.get = vi.fn().mockResolvedValue(responseWithoutTitle);
228
241
  const sdk = createTestSdk();
229
242
  const result = await sdk.listAuthentications({});
230
- expect(result.data[0].title).toBe("Label Only Auth");
243
+ // New API passes through fields directly - title coercion happens server-side
244
+ expect(result.data[0].title).toBeUndefined();
245
+ expect(result.data[0].label).toBe("Label Only Auth");
231
246
  });
232
247
  it("should handle authentications with neither title nor label", async () => {
233
248
  const responseWithoutTitleOrLabel = {
234
249
  ...mockAuthenticationsResponse,
235
- results: [
250
+ data: [
236
251
  {
237
- ...mockAuthenticationsResponse.results[0],
252
+ ...mockAuthenticationsResponse.data[0],
238
253
  title: undefined,
239
254
  label: undefined,
240
255
  },
241
256
  ],
257
+ nextCursor: undefined,
242
258
  };
243
259
  mockApiClient.get = vi
244
260
  .fn()
@@ -249,85 +265,90 @@ describe("listAuthentications plugin", () => {
249
265
  });
250
266
  });
251
267
  describe("filtering", () => {
252
- it("should filter by exact title match", async () => {
253
- const sdk = createTestSdk();
254
- const result = await sdk.listAuthentications({
255
- title: "My Slack Workspace",
256
- });
257
- // Should filter to exact match
258
- expect(result.data).toHaveLength(1);
259
- expect(result.data[0].title).toBe("My Slack Workspace");
260
- });
261
- it("should use search parameter when provided", async () => {
268
+ it("should pass search parameter to API when provided", async () => {
262
269
  const sdk = createTestSdk();
263
270
  await sdk.listAuthentications({ search: "workspace" });
264
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
271
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
265
272
  searchParams: expect.objectContaining({
266
273
  search: "workspace",
267
274
  }),
268
275
  }));
269
276
  });
270
- it("should use title as search when search not provided", async () => {
277
+ it("should pass title parameter separately to API", async () => {
271
278
  const sdk = createTestSdk();
272
279
  await sdk.listAuthentications({ title: "My Slack Workspace" });
273
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
280
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
274
281
  searchParams: expect.objectContaining({
275
- search: "My Slack Workspace",
282
+ title: "My Slack Workspace",
276
283
  }),
277
284
  }));
278
285
  });
279
- it("should prefer search over title when both provided", async () => {
286
+ it("should pass both search and title when both provided", async () => {
280
287
  const sdk = createTestSdk();
281
288
  await sdk.listAuthentications({
282
289
  search: "explicit search",
283
290
  title: "My Title",
284
291
  });
285
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
292
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
286
293
  searchParams: expect.objectContaining({
287
294
  search: "explicit search",
295
+ title: "My Title",
288
296
  }),
289
297
  }));
290
298
  });
291
299
  it("should pass accountId filter to API", async () => {
292
300
  const sdk = createTestSdk();
293
301
  await sdk.listAuthentications({ accountId: "acc_123" });
294
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
302
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
295
303
  searchParams: expect.objectContaining({
296
- account_id: "acc_123",
304
+ accountId: "acc_123",
297
305
  }),
298
306
  }));
299
307
  });
300
308
  it("should pass owner filter to API", async () => {
301
309
  const sdk = createTestSdk();
302
310
  await sdk.listAuthentications({ owner: "me" });
303
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
311
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
304
312
  searchParams: expect.objectContaining({
305
313
  owner: "me",
306
314
  }),
307
315
  }));
308
316
  });
309
- it("should pass authenticationIds as ids parameter to API", async () => {
317
+ it("should pass authenticationIds as comma-separated string to API", async () => {
310
318
  const sdk = createTestSdk();
311
319
  await sdk.listAuthentications({
312
320
  authenticationIds: ["123", "456", "789"],
313
321
  });
314
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
322
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
315
323
  searchParams: expect.objectContaining({
316
- ids: "123,456,789",
324
+ authenticationIds: "123,456,789",
317
325
  }),
318
326
  }));
319
327
  });
320
- it("should not include ids parameter when authenticationIds is empty", async () => {
328
+ it("should not include authenticationIds parameter when array is empty", async () => {
321
329
  const sdk = createTestSdk();
322
330
  await sdk.listAuthentications({
323
331
  authenticationIds: [],
324
332
  });
325
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
333
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
326
334
  searchParams: expect.not.objectContaining({
327
- ids: expect.anything(),
335
+ authenticationIds: expect.anything(),
328
336
  }),
329
337
  }));
330
338
  });
339
+ it("should not include undefined optional parameters in searchParams", async () => {
340
+ const sdk = createTestSdk();
341
+ await sdk.listAuthentications({});
342
+ const callArgs = mockApiClient.get.mock.calls[0];
343
+ const searchParams = callArgs[1].searchParams;
344
+ // Should only have pageSize (default), not undefined params
345
+ expect(searchParams).not.toHaveProperty("search");
346
+ expect(searchParams).not.toHaveProperty("title");
347
+ expect(searchParams).not.toHaveProperty("accountId");
348
+ expect(searchParams).not.toHaveProperty("owner");
349
+ expect(searchParams).not.toHaveProperty("authenticationIds");
350
+ expect(searchParams).not.toHaveProperty("appKey");
351
+ });
331
352
  });
332
353
  describe("pagination", () => {
333
354
  it("should handle pagination with maxItems by collecting all items", async () => {
@@ -335,27 +356,25 @@ describe("listAuthentications plugin", () => {
335
356
  mockApiClient.get = vi
336
357
  .fn()
337
358
  .mockResolvedValueOnce({
338
- results: mockAuthenticationsResponse.results,
339
- count: 3,
340
- next: "http://example.com/api/v4/authentications/?offset=2",
359
+ data: mockAuthenticationsResponse.data,
360
+ nextCursor: "2",
341
361
  })
342
362
  .mockResolvedValueOnce({
343
- results: [
363
+ data: [
344
364
  {
345
- id: 999,
365
+ id: "999",
346
366
  date: "2021-03-01",
347
- account_id: 456,
348
- selected_api: "DropboxAPI@3.0.0",
367
+ account_id: "456",
368
+ implementation_id: "DropboxAPI@3.0.0",
349
369
  is_invite_only: false,
350
370
  is_private: false,
351
371
  shared_with_all: false,
352
- is_stale: "false",
353
- marked_stale_at: null,
372
+ is_expired: "false",
373
+ expired_at: null,
354
374
  title: "Dropbox Integration",
355
375
  },
356
376
  ],
357
- count: 3,
358
- next: null,
377
+ nextCursor: undefined,
359
378
  });
360
379
  const sdk = createTestSdk();
361
380
  // Collect all items using the items() iterator
@@ -370,9 +389,8 @@ describe("listAuthentications plugin", () => {
370
389
  });
371
390
  it("should return first page when awaited", async () => {
372
391
  mockApiClient.get = vi.fn().mockResolvedValue({
373
- results: mockAuthenticationsResponse.results,
374
- count: 2,
375
- next: null, // No more pages to prevent automatic fetching
392
+ data: mockAuthenticationsResponse.data,
393
+ nextCursor: undefined,
376
394
  });
377
395
  const sdk = createTestSdk();
378
396
  const result = await sdk.listAuthentications({});
@@ -384,14 +402,12 @@ describe("listAuthentications plugin", () => {
384
402
  mockApiClient.get = vi
385
403
  .fn()
386
404
  .mockResolvedValueOnce({
387
- results: mockAuthenticationsResponse.results.slice(0, 1),
388
- count: 2,
389
- next: "http://example.com/api/v4/authentications/?offset=1",
405
+ data: mockAuthenticationsResponse.data.slice(0, 1),
406
+ nextCursor: "1",
390
407
  })
391
408
  .mockResolvedValueOnce({
392
- results: mockAuthenticationsResponse.results.slice(1, 2),
393
- count: 2,
394
- next: null,
409
+ data: mockAuthenticationsResponse.data.slice(1, 2),
410
+ nextCursor: undefined,
395
411
  });
396
412
  const sdk = createTestSdk();
397
413
  const pages = [];
@@ -418,43 +434,79 @@ describe("listAuthentications plugin", () => {
418
434
  expect(items[0].id).toBe("123");
419
435
  expect(items[1].id).toBe("789");
420
436
  });
421
- it("should set appropriate pageSize for API calls", async () => {
437
+ it("should set pageSize in searchParams for API calls", async () => {
422
438
  const sdk = createTestSdk();
423
- await sdk.listAuthentications({ pageSize: 1 });
424
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
439
+ await sdk.listAuthentications({ pageSize: 15 });
440
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
425
441
  searchParams: expect.objectContaining({
426
- limit: "1",
442
+ pageSize: "15",
427
443
  }),
428
444
  }));
429
445
  });
446
+ it("should pass cursor as offset parameter", async () => {
447
+ mockApiClient.get = vi
448
+ .fn()
449
+ .mockResolvedValueOnce({
450
+ data: mockAuthenticationsResponse.data.slice(0, 1),
451
+ nextCursor: "cursor-abc-123",
452
+ })
453
+ .mockResolvedValueOnce({
454
+ data: mockAuthenticationsResponse.data.slice(1, 2),
455
+ nextCursor: undefined,
456
+ });
457
+ const sdk = createTestSdk();
458
+ const pages = [];
459
+ for await (const page of sdk.listAuthentications({ pageSize: 1 })) {
460
+ pages.push(page);
461
+ if (pages.length >= 2)
462
+ break;
463
+ }
464
+ // Second call should include the cursor as offset
465
+ expect(mockApiClient.get).toHaveBeenNthCalledWith(2, "/api/v0/authentications", expect.objectContaining({
466
+ searchParams: expect.objectContaining({
467
+ offset: "cursor-abc-123",
468
+ }),
469
+ }));
470
+ });
471
+ it("should stop pagination when nextCursor is undefined", async () => {
472
+ mockApiClient.get = vi.fn().mockResolvedValue({
473
+ data: mockAuthenticationsResponse.data,
474
+ nextCursor: undefined,
475
+ });
476
+ const sdk = createTestSdk();
477
+ const pages = [];
478
+ for await (const page of sdk.listAuthentications({})) {
479
+ pages.push(page);
480
+ }
481
+ expect(pages).toHaveLength(1);
482
+ expect(mockApiClient.get).toHaveBeenCalledTimes(1);
483
+ });
430
484
  });
431
485
  describe("API integration", () => {
432
486
  it("should call the correct API endpoint", async () => {
433
487
  const sdk = createTestSdk();
434
488
  await sdk.listAuthentications({});
435
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.any(Object));
489
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.any(Object));
436
490
  });
437
- it("should pass pageSize as limit parameter", async () => {
491
+ it("should pass pageSize as string in searchParams", async () => {
438
492
  const sdk = createTestSdk();
439
493
  await sdk.listAuthentications({ pageSize: 25 });
440
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
494
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
441
495
  searchParams: expect.objectContaining({
442
- limit: "25",
496
+ pageSize: "25",
443
497
  }),
444
498
  }));
445
499
  });
446
- it("should handle cursor-based pagination internally", async () => {
500
+ it("should handle cursor-based pagination with nextCursor", async () => {
447
501
  mockApiClient.get = vi
448
502
  .fn()
449
503
  .mockResolvedValueOnce({
450
- results: mockAuthenticationsResponse.results.slice(0, 1),
451
- count: 2,
452
- next: "http://example.com/api/v4/authentications/?offset=1",
504
+ data: mockAuthenticationsResponse.data.slice(0, 1),
505
+ nextCursor: "offset-1",
453
506
  })
454
507
  .mockResolvedValueOnce({
455
- results: mockAuthenticationsResponse.results.slice(1, 2),
456
- count: 2,
457
- next: null,
508
+ data: mockAuthenticationsResponse.data.slice(1, 2),
509
+ nextCursor: undefined,
458
510
  });
459
511
  const sdk = createTestSdk();
460
512
  // Test that the function can handle multiple pages
@@ -466,42 +518,68 @@ describe("listAuthentications plugin", () => {
466
518
  }
467
519
  expect(pageCount).toBe(2);
468
520
  expect(mockApiClient.get).toHaveBeenCalledTimes(2);
469
- // The second call should include the offset from the cursor
470
- expect(mockApiClient.get).toHaveBeenNthCalledWith(2, "/zapier/api/v4/authentications/", expect.objectContaining({
521
+ // The second call should include the offset from the nextCursor
522
+ expect(mockApiClient.get).toHaveBeenNthCalledWith(2, "/api/v0/authentications", expect.objectContaining({
471
523
  searchParams: expect.objectContaining({
472
- offset: "1",
524
+ offset: "offset-1",
473
525
  }),
474
526
  }));
475
527
  });
528
+ it("should set authRequired to true in API call options", async () => {
529
+ const sdk = createTestSdk();
530
+ await sdk.listAuthentications({});
531
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
532
+ authRequired: true,
533
+ }));
534
+ });
535
+ it("should include customErrorHandler in API call options", async () => {
536
+ const sdk = createTestSdk();
537
+ await sdk.listAuthentications({});
538
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
539
+ customErrorHandler: expect.any(Function),
540
+ }));
541
+ });
476
542
  });
477
543
  describe("error handling", () => {
478
544
  it("should throw ZapierAuthenticationError for 401 responses", async () => {
479
- const customErrorHandler = vi
480
- .fn()
481
- .mockReturnValue(new ZapierAuthenticationError("Authentication failed. Your token may not have permission to access authentications or may be expired. (HTTP 401)", { statusCode: 401 }));
482
- mockApiClient.get = vi.fn().mockImplementation(() => {
483
- const error = customErrorHandler({ status: 401 });
484
- throw error;
545
+ mockApiClient.get = vi.fn().mockImplementation((_url, options) => {
546
+ // Simulate the API client calling the customErrorHandler
547
+ const error = options.customErrorHandler({ status: 401 });
548
+ if (error)
549
+ throw error;
485
550
  });
486
551
  const sdk = createTestSdk();
487
552
  await expect(sdk.listAuthentications({})).rejects.toThrow(ZapierAuthenticationError);
553
+ await expect(sdk.listAuthentications({})).rejects.toThrow(/Authentication failed.*HTTP 401/);
488
554
  });
489
555
  it("should throw ZapierAuthenticationError for 403 responses", async () => {
490
- const customErrorHandler = vi
491
- .fn()
492
- .mockReturnValue(new ZapierAuthenticationError("Access forbidden. Your token may not have the required scopes to list authentications. (HTTP 403)", { statusCode: 403 }));
493
- mockApiClient.get = vi.fn().mockImplementation(() => {
494
- const error = customErrorHandler({ status: 403 });
495
- throw error;
556
+ mockApiClient.get = vi.fn().mockImplementation((_url, options) => {
557
+ // Simulate the API client calling the customErrorHandler
558
+ const error = options.customErrorHandler({ status: 403 });
559
+ if (error)
560
+ throw error;
496
561
  });
497
562
  const sdk = createTestSdk();
498
563
  await expect(sdk.listAuthentications({})).rejects.toThrow(ZapierAuthenticationError);
564
+ await expect(sdk.listAuthentications({})).rejects.toThrow(/Access forbidden.*HTTP 403/);
565
+ });
566
+ it("should return undefined from customErrorHandler for non-401/403 errors", async () => {
567
+ let customErrorHandlerResult;
568
+ mockApiClient.get = vi.fn().mockImplementation((_url, options) => {
569
+ // Capture the customErrorHandler result for 500
570
+ customErrorHandlerResult = options.customErrorHandler({ status: 500 });
571
+ // Return valid data since we're just testing the handler
572
+ return mockAuthenticationsResponse;
573
+ });
574
+ const sdk = createTestSdk();
575
+ await sdk.listAuthentications({});
576
+ // The customErrorHandler should return undefined for other status codes
577
+ expect(customErrorHandlerResult).toBeUndefined();
499
578
  });
500
- it("should handle empty results", async () => {
579
+ it("should handle empty data array", async () => {
501
580
  const emptyResponse = {
502
- results: [],
503
- count: 0,
504
- next: null,
581
+ data: [],
582
+ nextCursor: undefined,
505
583
  };
506
584
  mockApiClient.get = vi.fn().mockResolvedValue(emptyResponse);
507
585
  const sdk = createTestSdk();
@@ -509,59 +587,74 @@ describe("listAuthentications plugin", () => {
509
587
  expect(result.data).toHaveLength(0);
510
588
  expect(Array.isArray(result.data)).toBe(true);
511
589
  });
512
- it("should handle missing results field", async () => {
513
- const responseWithoutResults = {
514
- count: 0,
515
- next: null,
516
- };
517
- mockApiClient.get = vi.fn().mockResolvedValue(responseWithoutResults);
590
+ it("should handle API errors gracefully", async () => {
591
+ const networkError = new Error("Network error");
592
+ mockApiClient.get = vi.fn().mockRejectedValue(networkError);
518
593
  const sdk = createTestSdk();
519
- const result = await sdk.listAuthentications({});
520
- expect(result.data).toHaveLength(0);
521
- expect(Array.isArray(result.data)).toBe(true);
594
+ await expect(sdk.listAuthentications({})).rejects.toThrow("Network error");
522
595
  });
523
596
  });
524
597
  describe("app key integration", () => {
525
- it("should handle app without current_implementation_id", async () => {
598
+ it("should not add appKey parameter when getVersionedImplementationId returns null", async () => {
526
599
  mockGetVersionedImplementationId.mockResolvedValue(null);
527
600
  const sdk = createTestSdk();
528
601
  await sdk.listAuthentications({ appKey: "slack" });
529
- // Should not add versionless_selected_api parameter
530
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
602
+ // Should not add appKey parameter when implementation can't be resolved
603
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
531
604
  searchParams: expect.not.objectContaining({
532
- versionless_selected_api: expect.any(String),
605
+ appKey: expect.any(String),
533
606
  }),
534
607
  }));
535
608
  });
536
- it("should extract versionless API from versioned implementation ID", async () => {
609
+ it("should extract versionless app key from versioned implementation ID", async () => {
537
610
  mockGetVersionedImplementationId.mockResolvedValue("SlackCLIAPI@2.1.3");
538
611
  const sdk = createTestSdk();
539
612
  await sdk.listAuthentications({ appKey: "slack" });
540
- expect(mockApiClient.get).toHaveBeenCalledWith("/zapier/api/v4/authentications/", expect.objectContaining({
613
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
614
+ searchParams: expect.objectContaining({
615
+ appKey: "SlackCLIAPI",
616
+ }),
617
+ }));
618
+ });
619
+ it("should handle implementation ID without version", async () => {
620
+ mockGetVersionedImplementationId.mockResolvedValue("SlackCLIAPI");
621
+ const sdk = createTestSdk();
622
+ await sdk.listAuthentications({ appKey: "slack" });
623
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
541
624
  searchParams: expect.objectContaining({
542
- versionless_selected_api: "SlackCLIAPI",
625
+ appKey: "SlackCLIAPI",
543
626
  }),
544
627
  }));
545
628
  });
629
+ it("should call getVersionedImplementationId with the provided appKey", async () => {
630
+ const sdk = createTestSdk();
631
+ await sdk.listAuthentications({ appKey: "my-custom-app" });
632
+ expect(mockGetVersionedImplementationId).toHaveBeenCalledWith("my-custom-app");
633
+ });
634
+ it("should not call getVersionedImplementationId when appKey is not provided", async () => {
635
+ const sdk = createTestSdk();
636
+ await sdk.listAuthentications({});
637
+ expect(mockGetVersionedImplementationId).not.toHaveBeenCalled();
638
+ });
546
639
  });
547
640
  describe("response transformation", () => {
548
- it("should preserve all original authentication fields", async () => {
641
+ it("should preserve all authentication fields from new API response", async () => {
549
642
  const authWithAllFields = {
550
- results: [
643
+ data: [
551
644
  {
552
- id: 123,
645
+ id: "123",
553
646
  date: "2021-01-01",
554
647
  lastchanged: "2021-01-02",
555
- account_id: 456,
556
- customuser_id: 789,
557
- selected_api: "SlackCLIAPI@1.21.1",
648
+ account_id: "456",
649
+ profile_id: "789",
650
+ implementation_id: "SlackCLIAPI@1.21.1",
558
651
  destination_selected_api: "SlackDestAPI@1.0.0",
559
652
  is_invite_only: true,
560
653
  is_private: true,
561
654
  shared_with_all: false,
562
- is_stale: "true",
655
+ is_expired: "true",
563
656
  is_shared: "false",
564
- marked_stale_at: "2021-06-01",
657
+ expired_at: "2021-06-01",
565
658
  label: "Auth Label",
566
659
  title: "Auth Title",
567
660
  identifier: "auth-identifier",
@@ -569,15 +662,17 @@ describe("listAuthentications plugin", () => {
569
662
  groups: "group1,group2",
570
663
  members: "user1,user2",
571
664
  permissions: { read: true, write: false },
665
+ app_key: "SlackCLIAPI",
666
+ app_version: "1.21.1",
572
667
  },
573
668
  ],
574
- count: 1,
669
+ nextCursor: undefined,
575
670
  };
576
671
  mockApiClient.get = vi.fn().mockResolvedValue(authWithAllFields);
577
672
  const sdk = createTestSdk();
578
673
  const result = await sdk.listAuthentications({});
579
674
  const auth = result.data[0];
580
- // Verify original fields are preserved (IDs are converted to strings)
675
+ // Verify all fields are passed through from the new API
581
676
  expect(auth.id).toBe("123");
582
677
  expect(auth.date).toBe("2021-01-01");
583
678
  expect(auth.lastchanged).toBe("2021-01-02");
@@ -596,9 +691,29 @@ describe("listAuthentications plugin", () => {
596
691
  expect(auth.groups).toBe("group1,group2");
597
692
  expect(auth.members).toBe("user1,user2");
598
693
  expect(auth.permissions).toEqual({ read: true, write: false });
599
- // Verify mapped fields
600
694
  expect(auth.is_expired).toBe("true");
601
695
  expect(auth.expired_at).toBe("2021-06-01");
696
+ expect(auth.app_key).toBe("SlackCLIAPI");
697
+ expect(auth.app_version).toBe("1.21.1");
698
+ });
699
+ it("should handle authentications with minimal fields", async () => {
700
+ const minimalAuth = {
701
+ data: [
702
+ {
703
+ id: "100",
704
+ account_id: "200",
705
+ implementation_id: "TestAPI@1.0.0",
706
+ },
707
+ ],
708
+ nextCursor: undefined,
709
+ };
710
+ mockApiClient.get = vi.fn().mockResolvedValue(minimalAuth);
711
+ const sdk = createTestSdk();
712
+ const result = await sdk.listAuthentications({});
713
+ const auth = result.data[0];
714
+ expect(auth.id).toBe("100");
715
+ expect(auth.account_id).toBe("200");
716
+ expect(auth.implementation_id).toBe("TestAPI@1.0.0");
602
717
  });
603
718
  });
604
719
  describe("context and metadata", () => {
@@ -608,5 +723,126 @@ describe("listAuthentications plugin", () => {
608
723
  expect(context.meta.listAuthentications).toBeDefined();
609
724
  expect(context.meta.listAuthentications.inputSchema).toBeDefined();
610
725
  });
726
+ it("should include all required metadata properties", () => {
727
+ const sdk = createTestSdk();
728
+ const context = sdk.getContext();
729
+ const meta = context.meta.listAuthentications;
730
+ expect(meta.categories).toContain("authentication");
731
+ expect(meta.type).toBe("list");
732
+ expect(meta.itemType).toBe("Authentication");
733
+ expect(meta.inputSchema).toBeDefined();
734
+ expect(meta.outputSchema).toBeDefined();
735
+ expect(meta.resolvers).toBeDefined();
736
+ expect(meta.resolvers.appKey).toBeDefined();
737
+ });
738
+ });
739
+ describe("edge cases", () => {
740
+ it("should handle special characters in search parameter", async () => {
741
+ const sdk = createTestSdk();
742
+ await sdk.listAuthentications({ search: "test@email.com" });
743
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
744
+ searchParams: expect.objectContaining({
745
+ search: "test@email.com",
746
+ }),
747
+ }));
748
+ });
749
+ it("should handle special characters in title parameter", async () => {
750
+ const sdk = createTestSdk();
751
+ await sdk.listAuthentications({ title: "My Auth (Test) #1" });
752
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
753
+ searchParams: expect.objectContaining({
754
+ title: "My Auth (Test) #1",
755
+ }),
756
+ }));
757
+ });
758
+ it("should handle single authenticationId in array", async () => {
759
+ const sdk = createTestSdk();
760
+ await sdk.listAuthentications({ authenticationIds: ["single-id"] });
761
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
762
+ searchParams: expect.objectContaining({
763
+ authenticationIds: "single-id",
764
+ }),
765
+ }));
766
+ });
767
+ it("should handle multiple filters combined", async () => {
768
+ const sdk = createTestSdk();
769
+ await sdk.listAuthentications({
770
+ appKey: "slack",
771
+ search: "workspace",
772
+ title: "My Workspace",
773
+ accountId: "acc_123",
774
+ owner: "me",
775
+ authenticationIds: ["id1", "id2"],
776
+ pageSize: 10,
777
+ });
778
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
779
+ searchParams: expect.objectContaining({
780
+ appKey: "SlackCLIAPI",
781
+ search: "workspace",
782
+ title: "My Workspace",
783
+ accountId: "acc_123",
784
+ owner: "me",
785
+ authenticationIds: "id1,id2",
786
+ pageSize: "10",
787
+ }),
788
+ }));
789
+ });
790
+ it("should handle maxItems less than pageSize", async () => {
791
+ mockApiClient.get = vi.fn().mockResolvedValue({
792
+ data: mockAuthenticationsResponse.data,
793
+ nextCursor: "next-page",
794
+ });
795
+ const sdk = createTestSdk();
796
+ const items = [];
797
+ for await (const item of sdk
798
+ .listAuthentications({ pageSize: 10, maxItems: 1 })
799
+ .items()) {
800
+ items.push(item);
801
+ }
802
+ expect(items).toHaveLength(1);
803
+ // Should only make one API call even though there's a next page
804
+ expect(mockApiClient.get).toHaveBeenCalledTimes(1);
805
+ });
806
+ it("should handle owner filter with user ID instead of 'me'", async () => {
807
+ const sdk = createTestSdk();
808
+ await sdk.listAuthentications({ owner: "user_12345" });
809
+ expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
810
+ searchParams: expect.objectContaining({
811
+ owner: "user_12345",
812
+ }),
813
+ }));
814
+ });
815
+ it("should pass through nextCursor in response", async () => {
816
+ mockApiClient.get = vi.fn().mockResolvedValue({
817
+ data: mockAuthenticationsResponse.data,
818
+ nextCursor: "cursor-for-next-page",
819
+ });
820
+ const sdk = createTestSdk();
821
+ const result = await sdk.listAuthentications({});
822
+ expect(result.nextCursor).toBe("cursor-for-next-page");
823
+ });
824
+ it("should handle undefined nextCursor in response", async () => {
825
+ mockApiClient.get = vi.fn().mockResolvedValue({
826
+ data: mockAuthenticationsResponse.data,
827
+ nextCursor: undefined,
828
+ });
829
+ const sdk = createTestSdk();
830
+ const result = await sdk.listAuthentications({});
831
+ expect(result.nextCursor).toBeUndefined();
832
+ });
833
+ });
834
+ describe("concurrent requests", () => {
835
+ it("should handle multiple concurrent listAuthentications calls", async () => {
836
+ const sdk = createTestSdk();
837
+ const [result1, result2, result3] = await Promise.all([
838
+ sdk.listAuthentications({ search: "first" }),
839
+ sdk.listAuthentications({ search: "second" }),
840
+ sdk.listAuthentications({ search: "third" }),
841
+ ]);
842
+ expect(result1.data).toHaveLength(2);
843
+ expect(result2.data).toHaveLength(2);
844
+ expect(result3.data).toHaveLength(2);
845
+ expect(mockApiClient.get).toHaveBeenCalledTimes(3);
846
+ });
611
847
  });
612
848
  });