@zapier/zapier-sdk 0.13.5 → 0.13.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/api/schemas.d.ts +38 -38
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.d.mts +170 -177
  5. package/dist/index.mjs +1 -1
  6. package/dist/plugins/fetch/schemas.d.ts +4 -4
  7. package/dist/plugins/getAction/schemas.d.ts +2 -2
  8. package/dist/plugins/listActions/schemas.d.ts +6 -6
  9. package/dist/plugins/listApps/schemas.d.ts +6 -6
  10. package/dist/plugins/listAuthentications/schemas.d.ts +4 -4
  11. package/dist/plugins/listInputFieldChoices/schemas.d.ts +8 -8
  12. package/dist/plugins/listInputFields/schemas.d.ts +8 -8
  13. package/dist/plugins/request/schemas.d.ts +8 -8
  14. package/dist/plugins/runAction/schemas.d.ts +8 -8
  15. package/dist/schemas/Action.d.ts +2 -2
  16. package/dist/schemas/App.d.ts +8 -8
  17. package/dist/types/sdk.d.ts +2 -8
  18. package/dist/types/sdk.d.ts.map +1 -1
  19. package/package.json +8 -3
  20. package/src/api/auth.ts +0 -28
  21. package/src/api/client.ts +0 -491
  22. package/src/api/debug.test.ts +0 -76
  23. package/src/api/debug.ts +0 -154
  24. package/src/api/index.ts +0 -90
  25. package/src/api/polling.test.ts +0 -405
  26. package/src/api/polling.ts +0 -253
  27. package/src/api/schemas.ts +0 -465
  28. package/src/api/types.ts +0 -152
  29. package/src/auth.ts +0 -72
  30. package/src/constants.ts +0 -16
  31. package/src/index.ts +0 -111
  32. package/src/plugins/api/index.ts +0 -43
  33. package/src/plugins/apps/index.ts +0 -203
  34. package/src/plugins/apps/schemas.ts +0 -64
  35. package/src/plugins/eventEmission/builders.ts +0 -115
  36. package/src/plugins/eventEmission/index.test.ts +0 -169
  37. package/src/plugins/eventEmission/index.ts +0 -294
  38. package/src/plugins/eventEmission/transport.test.ts +0 -214
  39. package/src/plugins/eventEmission/transport.ts +0 -135
  40. package/src/plugins/eventEmission/types.ts +0 -58
  41. package/src/plugins/eventEmission/utils.ts +0 -121
  42. package/src/plugins/fetch/index.ts +0 -83
  43. package/src/plugins/fetch/schemas.ts +0 -37
  44. package/src/plugins/findFirstAuthentication/index.test.ts +0 -209
  45. package/src/plugins/findFirstAuthentication/index.ts +0 -68
  46. package/src/plugins/findFirstAuthentication/schemas.ts +0 -47
  47. package/src/plugins/findUniqueAuthentication/index.test.ts +0 -197
  48. package/src/plugins/findUniqueAuthentication/index.ts +0 -77
  49. package/src/plugins/findUniqueAuthentication/schemas.ts +0 -49
  50. package/src/plugins/getAction/index.test.ts +0 -239
  51. package/src/plugins/getAction/index.ts +0 -75
  52. package/src/plugins/getAction/schemas.ts +0 -41
  53. package/src/plugins/getApp/index.test.ts +0 -181
  54. package/src/plugins/getApp/index.ts +0 -60
  55. package/src/plugins/getApp/schemas.ts +0 -33
  56. package/src/plugins/getAuthentication/index.test.ts +0 -294
  57. package/src/plugins/getAuthentication/index.ts +0 -95
  58. package/src/plugins/getAuthentication/schemas.ts +0 -38
  59. package/src/plugins/getProfile/index.ts +0 -60
  60. package/src/plugins/getProfile/schemas.ts +0 -24
  61. package/src/plugins/listActions/index.test.ts +0 -526
  62. package/src/plugins/listActions/index.ts +0 -132
  63. package/src/plugins/listActions/schemas.ts +0 -55
  64. package/src/plugins/listApps/index.test.ts +0 -378
  65. package/src/plugins/listApps/index.ts +0 -159
  66. package/src/plugins/listApps/schemas.ts +0 -41
  67. package/src/plugins/listAuthentications/index.test.ts +0 -739
  68. package/src/plugins/listAuthentications/index.ts +0 -152
  69. package/src/plugins/listAuthentications/schemas.ts +0 -77
  70. package/src/plugins/listInputFieldChoices/index.test.ts +0 -653
  71. package/src/plugins/listInputFieldChoices/index.ts +0 -173
  72. package/src/plugins/listInputFieldChoices/schemas.ts +0 -125
  73. package/src/plugins/listInputFields/index.test.ts +0 -439
  74. package/src/plugins/listInputFields/index.ts +0 -294
  75. package/src/plugins/listInputFields/schemas.ts +0 -68
  76. package/src/plugins/manifest/index.test.ts +0 -776
  77. package/src/plugins/manifest/index.ts +0 -461
  78. package/src/plugins/manifest/schemas.ts +0 -60
  79. package/src/plugins/registry/index.ts +0 -160
  80. package/src/plugins/request/index.test.ts +0 -333
  81. package/src/plugins/request/index.ts +0 -105
  82. package/src/plugins/request/schemas.ts +0 -69
  83. package/src/plugins/runAction/index.test.ts +0 -388
  84. package/src/plugins/runAction/index.ts +0 -215
  85. package/src/plugins/runAction/schemas.ts +0 -60
  86. package/src/resolvers/actionKey.ts +0 -37
  87. package/src/resolvers/actionType.ts +0 -34
  88. package/src/resolvers/appKey.ts +0 -7
  89. package/src/resolvers/authenticationId.ts +0 -54
  90. package/src/resolvers/index.ts +0 -11
  91. package/src/resolvers/inputFieldKey.ts +0 -70
  92. package/src/resolvers/inputs.ts +0 -69
  93. package/src/schemas/Action.ts +0 -52
  94. package/src/schemas/App.ts +0 -45
  95. package/src/schemas/Auth.ts +0 -59
  96. package/src/schemas/Field.ts +0 -169
  97. package/src/schemas/Run.ts +0 -40
  98. package/src/schemas/UserProfile.ts +0 -60
  99. package/src/sdk.test.ts +0 -212
  100. package/src/sdk.ts +0 -178
  101. package/src/types/domain.test.ts +0 -50
  102. package/src/types/domain.ts +0 -66
  103. package/src/types/errors.ts +0 -278
  104. package/src/types/events.ts +0 -43
  105. package/src/types/functions.ts +0 -28
  106. package/src/types/optional-zapier-sdk-cli-login.d.ts +0 -37
  107. package/src/types/plugin.ts +0 -125
  108. package/src/types/properties.ts +0 -80
  109. package/src/types/sdk.ts +0 -117
  110. package/src/types/telemetry-events.ts +0 -85
  111. package/src/utils/array-utils.test.ts +0 -131
  112. package/src/utils/array-utils.ts +0 -41
  113. package/src/utils/domain-utils.test.ts +0 -433
  114. package/src/utils/domain-utils.ts +0 -267
  115. package/src/utils/file-utils.test.ts +0 -73
  116. package/src/utils/file-utils.ts +0 -94
  117. package/src/utils/function-utils.test.ts +0 -141
  118. package/src/utils/function-utils.ts +0 -245
  119. package/src/utils/pagination-utils.test.ts +0 -620
  120. package/src/utils/pagination-utils.ts +0 -242
  121. package/src/utils/schema-utils.ts +0 -207
  122. package/src/utils/string-utils.test.ts +0 -45
  123. package/src/utils/string-utils.ts +0 -54
  124. package/src/utils/validation.test.ts +0 -51
  125. package/src/utils/validation.ts +0 -44
  126. package/tsconfig.build.json +0 -18
  127. package/tsconfig.json +0 -20
  128. package/tsconfig.tsbuildinfo +0 -1
  129. package/tsup.config.ts +0 -23
@@ -1,776 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import {
3
- manifestPlugin,
4
- readManifestFromFile,
5
- findManifestEntry,
6
- getPreferredManifestEntryKey,
7
- } from "./index";
8
- import { createSdk } from "../../sdk";
9
- import type { Manifest } from "./schemas";
10
-
11
- // Mock file-utils module
12
- vi.mock("../../utils/file-utils", () => ({
13
- readFile: vi.fn(),
14
- writeFile: vi.fn(),
15
- resolve: vi.fn((path) => Promise.resolve(`/resolved/${path}`)),
16
- }));
17
-
18
- import { readFile, resolve } from "../../utils/file-utils";
19
- import type { ApiClient } from "../../api";
20
-
21
- const mockReadFile = vi.mocked(readFile);
22
- const mockResolve = vi.mocked(resolve);
23
- const mockResolveAppKeys = vi.fn();
24
-
25
- describe("manifestPlugin", () => {
26
- const mockManifest = {
27
- apps: {
28
- slack: {
29
- implementationName: "SlackCLIAPI",
30
- version: "1.21.1",
31
- },
32
- "google-sheets": {
33
- implementationName: "GoogleSheetsCLIAPI",
34
- version: "2.0.0",
35
- },
36
- },
37
- };
38
-
39
- const mockManifestContent = JSON.stringify(mockManifest);
40
-
41
- let mockApiClient: ApiClient;
42
- let mockSdk: any;
43
-
44
- beforeEach(() => {
45
- vi.clearAllMocks();
46
- vi.spyOn(console, "warn").mockImplementation(() => {});
47
-
48
- mockApiClient = {
49
- get: vi.fn().mockImplementation((url: string, options?: any) => {
50
- if (url === "/api/v4/implementations/") {
51
- // Mock for manifest entries (versioned)
52
- return Promise.resolve({
53
- count: 1,
54
- next: null,
55
- previous: null,
56
- results: [
57
- {
58
- selected_api: "SlackCLIAPI@1.29.0",
59
- app_id: null,
60
- service_id: null,
61
- auth_type: "oauth",
62
- auth_fields: [
63
- {
64
- key: "access_token",
65
- required: true,
66
- type: "unicode",
67
- computed: true,
68
- },
69
- ],
70
- is_deprecated: false,
71
- is_private_only: false,
72
- is_invite_only: false,
73
- is_beta: false,
74
- is_premium: false,
75
- is_hidden: false,
76
- name: "Slack (1.29.0)",
77
- name_clean: "Slack",
78
- version: "1.29.0",
79
- slug: null,
80
- images: {
81
- url_16x16:
82
- "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=16&ixlib=python-3.0.0&q=50&w=16",
83
- url_32x32:
84
- "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=32&ixlib=python-3.0.0&q=50&w=32",
85
- url_64x64:
86
- "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=64&ixlib=python-3.0.0&q=50&w=64",
87
- url_128x128:
88
- "https://zapier-images.imgix.net/storage/services/6cf3f5a461feadfba7abc93c4c395b33_2.png?auto=format%2Ccompress&fit=crop&h=128&ixlib=python-3.0.0&q=50&w=128",
89
- },
90
- primary_color: null,
91
- secondary_color: null,
92
- classification: "third-party",
93
- current_implementation: "SlackCLIAPI@1.30.0",
94
- is_adoptable: true,
95
- is_usable: true,
96
- update_cta_level: "info",
97
- update_cta_message:
98
- "This version is legacy. Consider updating to the latest version for better support.",
99
- badges: [
100
- {
101
- label: "Legacy",
102
- color: "primary",
103
- tooltip_markdown:
104
- "This version is legacy. Consider updating to the latest version for better support.",
105
- },
106
- ],
107
- },
108
- ],
109
- });
110
- } else if (url === "/api/v4/implementations-meta/lookup/") {
111
- // Mock for implementations-meta/lookup fallback
112
- const searchParams = options?.searchParams || {};
113
-
114
- // Check if we're looking for a nonexistent app
115
- if (
116
- searchParams.slugs?.includes("nonexistent") ||
117
- searchParams.selected_apis?.includes("nonexistent")
118
- ) {
119
- return Promise.resolve({
120
- count: 0,
121
- next: null,
122
- previous: null,
123
- results: [],
124
- });
125
- }
126
-
127
- // Return successful response for known apps (like slack)
128
- return Promise.resolve({
129
- count: 1,
130
- next: null,
131
- previous: null,
132
- results: [
133
- {
134
- id: "SlackCLIAPI@1.30.0",
135
- name: "Slack",
136
- slug: "slack",
137
- images: {},
138
- },
139
- ],
140
- });
141
- }
142
- return Promise.resolve({ count: 0, results: [] });
143
- }),
144
- } as Partial<ApiClient> as ApiClient;
145
-
146
- mockSdk = {
147
- listApps: vi.fn().mockReturnValue({
148
- items: vi.fn().mockReturnValue(
149
- [
150
- {
151
- title: "Slack",
152
- key: "slack",
153
- current_implementation_id: "SlackCLIAPI@1.30.0",
154
- description: "Team communication platform",
155
- },
156
- ][Symbol.iterator](),
157
- ),
158
- }),
159
- };
160
- });
161
-
162
- afterEach(() => {
163
- vi.restoreAllMocks();
164
- });
165
-
166
- const apiPlugin = () => ({
167
- context: {
168
- api: mockApiClient,
169
- },
170
- });
171
-
172
- const listAppsMockPlugin = () => ({
173
- listApps: mockSdk.listApps,
174
- context: {
175
- meta: {
176
- listApps: {
177
- inputSchema: {} as any,
178
- },
179
- },
180
- },
181
- });
182
-
183
- function createTestSdk(options: any = {}) {
184
- return createSdk(options).addPlugin(apiPlugin).addPlugin(manifestPlugin);
185
- }
186
-
187
- describe("plugin initialization", () => {
188
- it("should provide helper functions with direct manifest", () => {
189
- const sdk = createTestSdk({ manifest: mockManifest });
190
- const context = sdk.getContext();
191
-
192
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
193
- });
194
-
195
- it("should provide helper functions with manifestPath", async () => {
196
- mockReadFile.mockResolvedValue(mockManifestContent);
197
-
198
- const sdk = createTestSdk({ manifestPath: "manifest.json" });
199
- const context = sdk.getContext();
200
-
201
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
202
-
203
- // Verify file operations happen when functions are called
204
- await context.getVersionedImplementationId("slack");
205
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
206
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
207
- });
208
-
209
- it("should provide helper functions when no manifest or path provided", () => {
210
- const sdk = createTestSdk();
211
- const context = sdk.getContext();
212
-
213
- expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
214
-
215
- // Manifest functions are available even when no manifest is provided
216
- });
217
-
218
- it("should prioritize direct manifest over manifestPath", () => {
219
- mockReadFile.mockResolvedValue(
220
- '{"apps": {"other": {"implementationName": "Other", "version": "1.0.0"}}}',
221
- );
222
-
223
- createTestSdk({
224
- manifest: mockManifest,
225
- manifestPath: "manifest.json",
226
- });
227
-
228
- // Verify that direct manifest is used and file loading is not needed
229
- expect(mockReadFile).not.toHaveBeenCalled();
230
- });
231
- });
232
-
233
- describe("getVersionedImplementationId function", () => {
234
- it("should return versioned implementation ID when app exists in manifest", async () => {
235
- const sdk = createTestSdk({ manifest: mockManifest });
236
- const context = sdk.getContext();
237
-
238
- const result = await context.getVersionedImplementationId("slack");
239
- expect(result).toBe("SlackCLIAPI@1.21.1");
240
- });
241
-
242
- it("should fetch and return versioned implementation ID when app not in manifest", async () => {
243
- const sdk = createTestSdk();
244
- const context = sdk.getContext();
245
-
246
- const result = await context.getVersionedImplementationId("slack");
247
- expect(result).toBe("SlackCLIAPI@1.30.0");
248
- });
249
-
250
- it("should return null when app does not exist", async () => {
251
- mockSdk.listApps = vi.fn().mockReturnValue({
252
- items: vi.fn().mockReturnValue([][Symbol.iterator]()),
253
- });
254
-
255
- const sdk = createTestSdk();
256
- const context = sdk.getContext();
257
-
258
- const result = await context.getVersionedImplementationId("nonexistent");
259
- expect(result).toBeNull();
260
- });
261
- });
262
-
263
- describe("manifest parsing", () => {
264
- it("should parse valid manifest content", () => {
265
- createTestSdk({ manifest: mockManifest });
266
-
267
- // Verify manifest is parsed correctly for other functions to use
268
- });
269
-
270
- it("should handle manifest with missing version", () => {
271
- const manifestWithoutVersion: Manifest = {
272
- apps: {
273
- slack: {
274
- implementationName: "SlackCLIAPI",
275
- },
276
- },
277
- };
278
-
279
- createTestSdk({ manifest: manifestWithoutVersion });
280
-
281
- // Verify manifest with missing version is handled correctly
282
- });
283
-
284
- it("should handle empty manifest", () => {
285
- createTestSdk({ manifest: {} });
286
-
287
- // Verify empty manifest is handled correctly
288
- });
289
- });
290
-
291
- describe("file loading", () => {
292
- it("should load manifest from file successfully", async () => {
293
- mockReadFile.mockResolvedValue(mockManifestContent);
294
-
295
- const sdk = createTestSdk({ manifestPath: "manifest.json" });
296
- const context = sdk.getContext();
297
-
298
- // Verify that file operations happen when functions are called
299
- await context.getVersionedImplementationId("slack");
300
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
301
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
302
- });
303
-
304
- it("should handle file read errors gracefully", async () => {
305
- mockReadFile.mockRejectedValue(new Error("File not found"));
306
-
307
- const sdk = createTestSdk({ manifestPath: "nonexistent.json" });
308
- const context = sdk.getContext();
309
-
310
- // Trigger file loading by calling a function
311
- await context.getVersionedImplementationId("slack");
312
-
313
- // Verify file loading errors are handled gracefully
314
- expect(console.warn).toHaveBeenCalledWith(
315
- expect.stringContaining(
316
- "Failed to read manifest from nonexistent.json",
317
- ),
318
- );
319
- });
320
-
321
- it("should handle invalid JSON gracefully", async () => {
322
- mockReadFile.mockResolvedValue("invalid json content");
323
-
324
- const sdk = createTestSdk({ manifestPath: "invalid.json" });
325
- const context = sdk.getContext();
326
-
327
- // Trigger file loading by calling a function
328
- await context.getVersionedImplementationId("slack");
329
-
330
- // Verify JSON parsing errors are handled gracefully
331
- expect(console.warn).toHaveBeenCalledWith(
332
- expect.stringContaining(
333
- "Failed to parse manifest from /resolved/invalid.json",
334
- ),
335
- expect.any(SyntaxError),
336
- );
337
- });
338
-
339
- it("should handle JSON without apps property", () => {
340
- mockReadFile.mockResolvedValue('{"other": "data"}');
341
-
342
- createTestSdk({ manifestPath: "no-apps.json" });
343
-
344
- // Verify missing apps property is handled correctly
345
- });
346
-
347
- it("should handle JSON with invalid apps format", () => {
348
- mockReadFile.mockResolvedValue('{"apps": "not an object"}');
349
-
350
- createTestSdk({ manifestPath: "invalid-apps.json" });
351
-
352
- // Verify invalid apps format is handled correctly
353
- });
354
- });
355
-
356
- describe("integration with SDK", () => {
357
- it("should work with createSdk", async () => {
358
- const sdk = createSdk({ manifest: mockManifest })
359
- .addPlugin(apiPlugin)
360
- .addPlugin(listAppsMockPlugin)
361
- .addPlugin(manifestPlugin);
362
- const context = sdk.getContext();
363
-
364
- // Verify that the manifest data is used correctly by other functions
365
-
366
- const versionedId = await context.getVersionedImplementationId("slack");
367
- expect(versionedId).toBe("SlackCLIAPI@1.21.1");
368
- });
369
- });
370
-
371
- describe("findManifestEntry function", () => {
372
- it("should find entry by direct key match", () => {
373
- const manifest = {
374
- apps: {
375
- slack: { implementationName: "SlackCLIAPI", version: "1.21.1" },
376
- gmail: { implementationName: "GmailCLIAPI", version: "2.0.0" },
377
- },
378
- };
379
-
380
- const result = findManifestEntry({ appKey: "slack", manifest });
381
-
382
- expect(result).toEqual([
383
- "slack",
384
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
385
- ]);
386
- });
387
-
388
- it("should find entry by implementation name match", () => {
389
- const manifest = {
390
- apps: {
391
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
392
- GmailCLIAPI: { implementationName: "GmailCLIAPI", version: "2.0.0" },
393
- },
394
- };
395
-
396
- const result = findManifestEntry({ appKey: "SlackCLIAPI", manifest });
397
-
398
- expect(result).toEqual([
399
- "SlackCLIAPI",
400
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
401
- ]);
402
- });
403
-
404
- it("should find existing key when searching by implementation name", () => {
405
- const manifest = {
406
- apps: {
407
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
408
- },
409
- };
410
-
411
- // This is the key test: searching by implementation name should find existing entry
412
- const result = findManifestEntry({ appKey: "SlackCLIAPI", manifest });
413
-
414
- expect(result).toEqual([
415
- "SlackCLIAPI",
416
- { implementationName: "SlackCLIAPI", version: "1.21.1" },
417
- ]);
418
- });
419
-
420
- it("should return null when no match found", () => {
421
- const manifest = {
422
- apps: {
423
- gmail: { implementationName: "GmailCLIAPI", version: "2.0.0" },
424
- },
425
- };
426
-
427
- const result = findManifestEntry({ appKey: "slack", manifest });
428
-
429
- expect(result).toBeNull();
430
- });
431
-
432
- it("should handle snake-cased slugs", () => {
433
- const manifest = {
434
- apps: {
435
- "google-sheets": {
436
- implementationName: "GoogleSheetsCLIAPI",
437
- version: "1.0.0",
438
- },
439
- },
440
- };
441
-
442
- const result = findManifestEntry({ appKey: "google_sheets", manifest });
443
-
444
- expect(result).toEqual([
445
- "google-sheets",
446
- { implementationName: "GoogleSheetsCLIAPI", version: "1.0.0" },
447
- ]);
448
- });
449
-
450
- it("should NOT find entry when searching slug against implementation name key (current limitation)", () => {
451
- const manifest = {
452
- apps: {
453
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
454
- },
455
- };
456
-
457
- // This will fail - we search by "slack" but can't find "SlackCLIAPI" entry
458
- // This is the core issue we need to solve
459
- const result = findManifestEntry({ appKey: "slack", manifest });
460
-
461
- expect(result).toBeNull(); // Current behavior - doesn't find it
462
- // We need to fix this so it CAN find existing implementation name entries
463
- });
464
- });
465
-
466
- describe("updateManifestEntry with slug-to-implementation matching", () => {
467
- it("should find existing implementation name entry when input is slug", async () => {
468
- // Mock existing manifest with implementation name as key
469
- mockReadFile.mockResolvedValue(
470
- JSON.stringify({
471
- apps: {
472
- SlackCLIAPI: {
473
- implementationName: "SlackCLIAPI",
474
- version: "1.21.1",
475
- },
476
- },
477
- }),
478
- );
479
-
480
- // Mock resolveAppKeys to resolve "slack" to "SlackCLIAPI"
481
- mockResolveAppKeys.mockResolvedValue([
482
- {
483
- slug: "slack",
484
- implementationName: "SlackCLIAPI",
485
- version: "1.30.0",
486
- lookupAppKey: "slack",
487
- },
488
- ]);
489
-
490
- const sdk = createTestSdk();
491
- const context = sdk.getContext();
492
-
493
- // This should now find the existing "SlackCLIAPI" entry
494
- const [manifestKey] = await context.updateManifestEntry({
495
- appKey: "slack", // Input is slug
496
- entry: { implementationName: "SlackCLIAPI", version: "1.30.0" },
497
- });
498
-
499
- // Should return the existing key, not create a new one
500
- expect(manifestKey).toBe("SlackCLIAPI");
501
- });
502
- });
503
-
504
- describe("resolveAppKeys function", () => {
505
- it("should resolve slug to manifest entry even when manifest uses implementation name as key", async () => {
506
- const manifest = {
507
- apps: {
508
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
509
- },
510
- };
511
-
512
- // Mock API to return slack app data when looking up "slack"
513
- mockApiClient.get = vi.fn().mockResolvedValue({
514
- count: 1,
515
- next: null,
516
- previous: null,
517
- results: [
518
- {
519
- id: "SlackCLIAPI@1.30.0",
520
- name: "Slack",
521
- slug: "slack",
522
- key: "SlackCLIAPI", // This is the implementation name
523
- version: "1.30.0",
524
- },
525
- ],
526
- });
527
-
528
- // Test resolveAppKeys directly using the manifest plugin
529
- const sdk = createSdk({ manifest })
530
- .addPlugin(apiPlugin)
531
- .addPlugin(manifestPlugin);
532
- const context = sdk.getContext();
533
-
534
- const resolved = await context.resolveAppKeys({ appKeys: ["slack"] });
535
-
536
- // Should find the manifest entry and use its version (1.21.1) instead of latest (1.30.0)
537
- expect(resolved).toHaveLength(1);
538
- expect(resolved[0]).toEqual({
539
- slug: "slack",
540
- implementationName: "SlackCLIAPI",
541
- version: "1.21.1", // Should use manifest version, not API version
542
- lookupAppKey: "slack",
543
- });
544
- });
545
-
546
- it("should fall back to API version when no manifest entry found", async () => {
547
- const manifest = { apps: {} }; // Empty manifest
548
-
549
- // Mock API to return slack app data
550
- mockApiClient.get = vi.fn().mockResolvedValue({
551
- count: 1,
552
- next: null,
553
- previous: null,
554
- results: [
555
- {
556
- id: "SlackCLIAPI@1.30.0",
557
- name: "Slack",
558
- slug: "slack",
559
- key: "SlackCLIAPI",
560
- version: "1.30.0",
561
- },
562
- ],
563
- });
564
-
565
- const sdk = createSdk({ manifest })
566
- .addPlugin(apiPlugin)
567
- .addPlugin(manifestPlugin);
568
- const context = sdk.getContext();
569
-
570
- const resolved = await context.resolveAppKeys({ appKeys: ["slack"] });
571
-
572
- // Should use API version since no manifest entry found
573
- expect(resolved).toHaveLength(1);
574
- expect(resolved[0]).toEqual({
575
- slug: "slack",
576
- implementationName: "SlackCLIAPI",
577
- version: "1.30.0", // Should use API version
578
- lookupAppKey: "slack",
579
- });
580
- });
581
-
582
- it("should always use version from input when specified (slug@version)", async () => {
583
- const manifest = {
584
- apps: {
585
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
586
- },
587
- };
588
-
589
- // Mock API to return slack app data
590
- mockApiClient.get = vi.fn().mockResolvedValue({
591
- count: 1,
592
- next: null,
593
- previous: null,
594
- results: [
595
- {
596
- id: "SlackCLIAPI@1.30.0",
597
- name: "Slack",
598
- slug: "slack",
599
- key: "SlackCLIAPI",
600
- version: "1.30.0",
601
- },
602
- ],
603
- });
604
-
605
- const sdk = createSdk({ manifest })
606
- .addPlugin(apiPlugin)
607
- .addPlugin(manifestPlugin);
608
- const context = sdk.getContext();
609
-
610
- // Input version should take precedence over both manifest (1.21.1) and API (1.30.0)
611
- const resolved = await context.resolveAppKeys({
612
- appKeys: ["slack@1.2.3"],
613
- });
614
-
615
- expect(resolved).toHaveLength(1);
616
- expect(resolved[0]).toEqual({
617
- slug: "slack",
618
- implementationName: "SlackCLIAPI",
619
- version: "1.2.3", // Should use input version, not manifest or API
620
- lookupAppKey: "slack",
621
- });
622
- });
623
-
624
- it("should always use version from input when specified (implementationName@version)", async () => {
625
- const manifest = {
626
- apps: {
627
- SlackCLIAPI: { implementationName: "SlackCLIAPI", version: "1.21.1" },
628
- },
629
- };
630
-
631
- const sdk = createSdk({ manifest })
632
- .addPlugin(apiPlugin)
633
- .addPlugin(manifestPlugin);
634
- const context = sdk.getContext();
635
-
636
- // Input version should take precedence over manifest version
637
- const resolved = await context.resolveAppKeys({
638
- appKeys: ["SlackCLIAPI@1.2.3"],
639
- });
640
-
641
- expect(resolved).toHaveLength(1);
642
- expect(resolved[0]).toEqual({
643
- implementationName: "SlackCLIAPI",
644
- version: "1.2.3", // Should use input version, not manifest version
645
- lookupAppKey: "SlackCLIAPI",
646
- });
647
- });
648
- });
649
- });
650
-
651
- describe("readManifestFromFile", () => {
652
- const mockManifestContent = JSON.stringify({
653
- apps: {
654
- slack: {
655
- implementationName: "SlackCLIAPI",
656
- version: "1.21.1",
657
- },
658
- "google-sheets": {
659
- implementationName: "GoogleSheetsCLIAPI",
660
- version: "2.0.0",
661
- },
662
- },
663
- });
664
-
665
- beforeEach(() => {
666
- vi.clearAllMocks();
667
- });
668
-
669
- it("should read and parse manifest from file", async () => {
670
- mockReadFile.mockResolvedValue(mockManifestContent);
671
-
672
- const result = await readManifestFromFile("manifest.json");
673
-
674
- expect(mockResolve).toHaveBeenCalledWith("manifest.json");
675
- expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
676
- expect(result).toEqual({
677
- apps: {
678
- slack: {
679
- implementationName: "SlackCLIAPI",
680
- version: "1.21.1",
681
- },
682
- "google-sheets": {
683
- implementationName: "GoogleSheetsCLIAPI",
684
- version: "2.0.0",
685
- },
686
- },
687
- });
688
- });
689
-
690
- it("should handle file read errors", async () => {
691
- mockReadFile.mockRejectedValue(new Error("File not found"));
692
-
693
- const result = await readManifestFromFile("nonexistent.json");
694
-
695
- expect(result).toBeNull();
696
- });
697
-
698
- it("should handle invalid JSON content", async () => {
699
- mockReadFile.mockResolvedValue("invalid json");
700
-
701
- const result = await readManifestFromFile("invalid.json");
702
-
703
- expect(result).toBeNull();
704
- });
705
-
706
- describe("getPreferredManifestEntryKey", () => {
707
- let mockApi: ApiClient;
708
-
709
- beforeEach(() => {
710
- mockApi = {
711
- get: vi.fn(),
712
- } as any;
713
- });
714
-
715
- it("should prefer slug when available", async () => {
716
- const result = await getPreferredManifestEntryKey({
717
- appKey: "slack",
718
- api: mockApi,
719
- });
720
-
721
- expect(result).toBe("slack");
722
- });
723
-
724
- it("should look up slug for implementation name", async () => {
725
- // Mock API response for implementation name lookup
726
- mockApi.get = vi.fn().mockResolvedValueOnce({
727
- results: [
728
- {
729
- key: "SlackCLIAPI",
730
- slug: "slack",
731
- version: "1.0.0",
732
- current_implementation_id: "SlackCLIAPI@1.0.0",
733
- },
734
- ],
735
- next: null,
736
- });
737
-
738
- const result = await getPreferredManifestEntryKey({
739
- appKey: "SlackCLIAPI",
740
- api: mockApi,
741
- });
742
-
743
- expect(result).toBe("slack");
744
- expect(mockApi.get).toHaveBeenCalledWith(
745
- "/api/v4/implementations-meta/lookup/",
746
- {
747
- searchParams: { selected_apis: "SlackCLIAPI" },
748
- },
749
- );
750
- });
751
-
752
- it("should fall back to implementation name if slug lookup fails", async () => {
753
- // Mock API to return empty results
754
- mockApi.get = vi.fn().mockResolvedValueOnce({
755
- results: [],
756
- next: null,
757
- });
758
-
759
- const result = await getPreferredManifestEntryKey({
760
- appKey: "SlackCLIAPI",
761
- api: mockApi,
762
- });
763
-
764
- expect(result).toBe("SlackCLIAPI");
765
- });
766
-
767
- it("should fall back to original key if no slug or implementation name", async () => {
768
- const result = await getPreferredManifestEntryKey({
769
- appKey: "some-random-key",
770
- api: mockApi,
771
- });
772
-
773
- expect(result).toBe("some-random-key");
774
- });
775
- });
776
- });