@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.
- package/CHANGELOG.md +12 -0
- package/README.md +13 -13
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +0 -18
- package/dist/api/schemas.d.ts +0 -158
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +0 -95
- package/dist/api/types.d.ts +2 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/index.cjs +400 -808
- package/dist/index.d.mts +67 -122
- package/dist/index.mjs +401 -809
- package/dist/plugins/findFirstAuthentication/index.test.js +2 -2
- package/dist/plugins/findUniqueAuthentication/index.test.js +2 -2
- package/dist/plugins/listApps/index.d.ts +2 -8
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +4 -6
- package/dist/plugins/listApps/index.test.js +62 -82
- package/dist/plugins/listApps/schemas.d.ts +35 -14
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +44 -14
- package/dist/plugins/listAuthentications/index.d.ts +4 -10
- package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/index.js +19 -31
- package/dist/plugins/listAuthentications/index.test.js +363 -127
- package/dist/plugins/listAuthentications/schemas.d.ts +7 -7
- package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/schemas.js +10 -16
- package/dist/schemas/App.d.ts +28 -28
- package/dist/schemas/App.d.ts.map +1 -1
- package/dist/schemas/App.js +3 -8
- package/dist/sdk.d.ts +2 -2
- package/dist/sdk.test.js +12 -9
- package/dist/utils/domain-utils.d.ts +2 -15
- package/dist/utils/domain-utils.d.ts.map +1 -1
- package/dist/utils/domain-utils.js +0 -38
- package/dist/utils/function-utils.d.ts +1 -0
- package/dist/utils/function-utils.d.ts.map +1 -1
- package/dist/utils/function-utils.js +15 -3
- package/package.json +2 -2
- package/dist/api/client.integration.test.d.ts +0 -5
- package/dist/api/client.integration.test.d.ts.map +0 -1
- package/dist/api/client.integration.test.js +0 -318
- package/dist/api/client.methods.test.d.ts +0 -2
- package/dist/api/client.methods.test.d.ts.map +0 -1
- package/dist/api/client.methods.test.js +0 -158
- package/dist/api/router.d.ts +0 -16
- package/dist/api/router.d.ts.map +0 -1
- package/dist/api/router.js +0 -31
- package/dist/api/router.test.d.ts +0 -2
- package/dist/api/router.test.d.ts.map +0 -1
- package/dist/api/router.test.js +0 -103
- package/dist/temporary-internal-core/handlers/listApps.d.ts +0 -67
- package/dist/temporary-internal-core/handlers/listApps.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.js +0 -134
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts +0 -2
- package/dist/temporary-internal-core/handlers/listApps.test.d.ts.map +0 -1
- package/dist/temporary-internal-core/handlers/listApps.test.js +0 -367
- package/dist/temporary-internal-core/index.d.ts +0 -18
- package/dist/temporary-internal-core/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/index.js +0 -18
- package/dist/temporary-internal-core/schemas/apps/index.d.ts +0 -175
- package/dist/temporary-internal-core/schemas/apps/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/apps/index.js +0 -97
- package/dist/temporary-internal-core/schemas/errors/index.d.ts +0 -139
- package/dist/temporary-internal-core/schemas/errors/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/errors/index.js +0 -129
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts +0 -127
- package/dist/temporary-internal-core/schemas/implementations/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/schemas/implementations/index.js +0 -79
- package/dist/temporary-internal-core/types/handler.d.ts +0 -51
- package/dist/temporary-internal-core/types/handler.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/handler.js +0 -8
- package/dist/temporary-internal-core/types/index.d.ts +0 -5
- package/dist/temporary-internal-core/types/index.d.ts.map +0 -1
- package/dist/temporary-internal-core/types/index.js +0 -4
- package/dist/temporary-internal-core/utils/app-locators.d.ts +0 -34
- package/dist/temporary-internal-core/utils/app-locators.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/app-locators.js +0 -39
- package/dist/temporary-internal-core/utils/string-utils.d.ts +0 -28
- package/dist/temporary-internal-core/utils/string-utils.d.ts.map +0 -1
- package/dist/temporary-internal-core/utils/string-utils.js +0 -52
- package/dist/temporary-internal-core/utils/transformations.d.ts +0 -32
- package/dist/temporary-internal-core/utils/transformations.d.ts.map +0 -1
- 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
|
-
|
|
20
|
+
data: [
|
|
8
21
|
{
|
|
9
|
-
id: 123,
|
|
22
|
+
id: "123",
|
|
10
23
|
date: "2021-01-01",
|
|
11
|
-
account_id: 456,
|
|
12
|
-
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
228
|
+
it("should pass through label field when title is missing", async () => {
|
|
217
229
|
const responseWithoutTitle = {
|
|
218
230
|
...mockAuthenticationsResponse,
|
|
219
|
-
|
|
231
|
+
data: [
|
|
220
232
|
{
|
|
221
|
-
...mockAuthenticationsResponse.
|
|
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
|
-
|
|
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
|
-
|
|
250
|
+
data: [
|
|
236
251
|
{
|
|
237
|
-
...mockAuthenticationsResponse.
|
|
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
|
|
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("/
|
|
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
|
|
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("/
|
|
280
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
274
281
|
searchParams: expect.objectContaining({
|
|
275
|
-
|
|
282
|
+
title: "My Slack Workspace",
|
|
276
283
|
}),
|
|
277
284
|
}));
|
|
278
285
|
});
|
|
279
|
-
it("should
|
|
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("/
|
|
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("/
|
|
302
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
295
303
|
searchParams: expect.objectContaining({
|
|
296
|
-
|
|
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("/
|
|
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
|
|
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("/
|
|
322
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
315
323
|
searchParams: expect.objectContaining({
|
|
316
|
-
|
|
324
|
+
authenticationIds: "123,456,789",
|
|
317
325
|
}),
|
|
318
326
|
}));
|
|
319
327
|
});
|
|
320
|
-
it("should not include
|
|
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("/
|
|
333
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
326
334
|
searchParams: expect.not.objectContaining({
|
|
327
|
-
|
|
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
|
-
|
|
339
|
-
|
|
340
|
-
next: "http://example.com/api/v4/authentications/?offset=2",
|
|
359
|
+
data: mockAuthenticationsResponse.data,
|
|
360
|
+
nextCursor: "2",
|
|
341
361
|
})
|
|
342
362
|
.mockResolvedValueOnce({
|
|
343
|
-
|
|
363
|
+
data: [
|
|
344
364
|
{
|
|
345
|
-
id: 999,
|
|
365
|
+
id: "999",
|
|
346
366
|
date: "2021-03-01",
|
|
347
|
-
account_id: 456,
|
|
348
|
-
|
|
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
|
-
|
|
353
|
-
|
|
372
|
+
is_expired: "false",
|
|
373
|
+
expired_at: null,
|
|
354
374
|
title: "Dropbox Integration",
|
|
355
375
|
},
|
|
356
376
|
],
|
|
357
|
-
|
|
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
|
-
|
|
374
|
-
|
|
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
|
-
|
|
388
|
-
|
|
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
|
-
|
|
393
|
-
|
|
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
|
|
437
|
+
it("should set pageSize in searchParams for API calls", async () => {
|
|
422
438
|
const sdk = createTestSdk();
|
|
423
|
-
await sdk.listAuthentications({ pageSize:
|
|
424
|
-
expect(mockApiClient.get).toHaveBeenCalledWith("/
|
|
439
|
+
await sdk.listAuthentications({ pageSize: 15 });
|
|
440
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
425
441
|
searchParams: expect.objectContaining({
|
|
426
|
-
|
|
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("/
|
|
489
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.any(Object));
|
|
436
490
|
});
|
|
437
|
-
it("should pass pageSize as
|
|
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("/
|
|
494
|
+
expect(mockApiClient.get).toHaveBeenCalledWith("/api/v0/authentications", expect.objectContaining({
|
|
441
495
|
searchParams: expect.objectContaining({
|
|
442
|
-
|
|
496
|
+
pageSize: "25",
|
|
443
497
|
}),
|
|
444
498
|
}));
|
|
445
499
|
});
|
|
446
|
-
it("should handle cursor-based pagination
|
|
500
|
+
it("should handle cursor-based pagination with nextCursor", async () => {
|
|
447
501
|
mockApiClient.get = vi
|
|
448
502
|
.fn()
|
|
449
503
|
.mockResolvedValueOnce({
|
|
450
|
-
|
|
451
|
-
|
|
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
|
-
|
|
456
|
-
|
|
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
|
|
470
|
-
expect(mockApiClient.get).toHaveBeenNthCalledWith(2, "/
|
|
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
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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
|
|
579
|
+
it("should handle empty data array", async () => {
|
|
501
580
|
const emptyResponse = {
|
|
502
|
-
|
|
503
|
-
|
|
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
|
|
513
|
-
const
|
|
514
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
530
|
-
expect(mockApiClient.get).toHaveBeenCalledWith("/
|
|
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
|
-
|
|
605
|
+
appKey: expect.any(String),
|
|
533
606
|
}),
|
|
534
607
|
}));
|
|
535
608
|
});
|
|
536
|
-
it("should extract versionless
|
|
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("/
|
|
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
|
-
|
|
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
|
|
641
|
+
it("should preserve all authentication fields from new API response", async () => {
|
|
549
642
|
const authWithAllFields = {
|
|
550
|
-
|
|
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
|
-
|
|
557
|
-
|
|
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
|
-
|
|
655
|
+
is_expired: "true",
|
|
563
656
|
is_shared: "false",
|
|
564
|
-
|
|
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
|
-
|
|
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
|
|
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
|
});
|