@zapier/zapier-sdk 0.8.3 → 0.10.0
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 +18 -0
- package/README.md +17 -40
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +14 -0
- package/dist/api/debug.d.ts +1 -0
- package/dist/api/debug.d.ts.map +1 -1
- package/dist/api/debug.js +42 -1
- package/dist/api/debug.test.d.ts +2 -0
- package/dist/api/debug.test.d.ts.map +1 -0
- package/dist/api/debug.test.js +59 -0
- package/dist/api/schemas.d.ts +451 -251
- package/dist/api/schemas.d.ts.map +1 -1
- package/dist/api/schemas.js +51 -29
- package/dist/index.cjs +1149 -751
- package/dist/index.d.mts +2359 -2161
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/index.mjs +1143 -743
- package/dist/plugins/apps/index.d.ts +4 -0
- package/dist/plugins/apps/index.d.ts.map +1 -1
- package/dist/plugins/findFirstAuthentication/index.d.ts +1 -1
- package/dist/plugins/findFirstAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/findFirstAuthentication/index.js +9 -1
- package/dist/plugins/findFirstAuthentication/index.test.js +3 -4
- package/dist/plugins/findFirstAuthentication/schemas.d.ts +5 -3
- package/dist/plugins/findFirstAuthentication/schemas.d.ts.map +1 -1
- package/dist/plugins/findUniqueAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/findUniqueAuthentication/index.js +4 -0
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts +5 -3
- package/dist/plugins/findUniqueAuthentication/schemas.d.ts.map +1 -1
- package/dist/plugins/getAction/index.d.ts.map +1 -1
- package/dist/plugins/getAction/index.js +10 -0
- package/dist/plugins/getAction/schemas.d.ts +5 -3
- package/dist/plugins/getAction/schemas.d.ts.map +1 -1
- package/dist/plugins/getApp/index.d.ts +2 -7
- package/dist/plugins/getApp/index.d.ts.map +1 -1
- package/dist/plugins/getApp/index.js +17 -9
- package/dist/plugins/getApp/index.test.js +3 -3
- package/dist/plugins/getApp/schemas.d.ts +3 -1
- package/dist/plugins/getApp/schemas.d.ts.map +1 -1
- package/dist/plugins/getApp/schemas.js +2 -4
- package/dist/plugins/getAuthentication/index.d.ts.map +1 -1
- package/dist/plugins/getAuthentication/index.js +8 -0
- package/dist/plugins/getAuthentication/index.test.js +1 -1
- package/dist/plugins/getProfile/index.d.ts.map +1 -1
- package/dist/plugins/getProfile/index.js +4 -0
- package/dist/plugins/getProfile/schemas.d.ts.map +1 -1
- package/dist/plugins/getProfile/schemas.js +4 -3
- package/dist/plugins/listActions/index.d.ts +2 -4
- package/dist/plugins/listActions/index.d.ts.map +1 -1
- package/dist/plugins/listActions/index.js +10 -1
- package/dist/plugins/listActions/index.test.js +4 -4
- package/dist/plugins/listActions/schemas.d.ts +5 -3
- package/dist/plugins/listActions/schemas.d.ts.map +1 -1
- package/dist/plugins/listActions/schemas.js +2 -4
- package/dist/plugins/listApps/index.d.ts +4 -7
- package/dist/plugins/listApps/index.d.ts.map +1 -1
- package/dist/plugins/listApps/index.js +37 -17
- package/dist/plugins/listApps/index.test.js +23 -3
- package/dist/plugins/listApps/schemas.d.ts.map +1 -1
- package/dist/plugins/listApps/schemas.js +3 -9
- package/dist/plugins/listAuthentications/index.d.ts +2 -4
- package/dist/plugins/listAuthentications/index.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/index.js +12 -0
- package/dist/plugins/listAuthentications/index.test.js +39 -13
- package/dist/plugins/listAuthentications/schemas.d.ts +8 -3
- package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -1
- package/dist/plugins/listAuthentications/schemas.js +4 -0
- package/dist/plugins/listInputFieldChoices/index.d.ts.map +1 -1
- package/dist/plugins/listInputFieldChoices/index.js +14 -2
- package/dist/plugins/listInputFieldChoices/schemas.d.ts +5 -3
- package/dist/plugins/listInputFieldChoices/schemas.d.ts.map +1 -1
- package/dist/plugins/listInputFieldChoices/schemas.js +10 -19
- package/dist/plugins/listInputFields/index.d.ts.map +1 -1
- package/dist/plugins/listInputFields/index.js +14 -2
- package/dist/plugins/listInputFields/index.test.js +5 -9
- package/dist/plugins/listInputFields/schemas.d.ts +5 -3
- package/dist/plugins/listInputFields/schemas.d.ts.map +1 -1
- package/dist/plugins/manifest/index.d.ts +25 -9
- package/dist/plugins/manifest/index.d.ts.map +1 -1
- package/dist/plugins/manifest/index.js +239 -67
- package/dist/plugins/manifest/index.test.js +426 -171
- package/dist/plugins/manifest/schemas.d.ts +5 -1
- package/dist/plugins/manifest/schemas.d.ts.map +1 -1
- package/dist/plugins/manifest/schemas.js +1 -0
- package/dist/plugins/registry/index.d.ts.map +1 -1
- package/dist/plugins/registry/index.js +8 -2
- package/dist/plugins/request/index.d.ts.map +1 -1
- package/dist/plugins/request/index.js +1 -0
- package/dist/plugins/runAction/index.d.ts.map +1 -1
- package/dist/plugins/runAction/index.js +12 -0
- package/dist/plugins/runAction/schemas.d.ts +5 -3
- package/dist/plugins/runAction/schemas.d.ts.map +1 -1
- package/dist/resolvers/actionKey.d.ts +13 -7
- package/dist/resolvers/actionKey.d.ts.map +1 -1
- package/dist/resolvers/actionType.d.ts +8 -7
- package/dist/resolvers/actionType.d.ts.map +1 -1
- package/dist/resolvers/appKey.d.ts +2 -6
- package/dist/resolvers/appKey.d.ts.map +1 -1
- package/dist/resolvers/authenticationId.d.ts +7 -7
- package/dist/resolvers/authenticationId.d.ts.map +1 -1
- package/dist/resolvers/authenticationId.js +16 -7
- package/dist/resolvers/index.d.ts +3 -35
- package/dist/resolvers/index.d.ts.map +1 -1
- package/dist/resolvers/index.js +4 -87
- package/dist/resolvers/inputFieldKey.d.ts +11 -0
- package/dist/resolvers/inputFieldKey.d.ts.map +1 -0
- package/dist/resolvers/inputFieldKey.js +23 -0
- package/dist/resolvers/inputs.d.ts +11 -6
- package/dist/resolvers/inputs.d.ts.map +1 -1
- package/dist/resolvers/inputs.js +17 -0
- package/dist/schemas/Action.d.ts +8 -8
- package/dist/schemas/Action.d.ts.map +1 -1
- package/dist/schemas/Action.js +8 -3
- package/dist/schemas/App.d.ts +183 -11
- package/dist/schemas/App.d.ts.map +1 -1
- package/dist/schemas/App.js +7 -9
- package/dist/schemas/Auth.d.ts +12 -12
- package/dist/schemas/Auth.js +1 -1
- package/dist/schemas/Field.d.ts +5 -98
- package/dist/schemas/Field.d.ts.map +1 -1
- package/dist/schemas/Field.js +24 -52
- package/dist/schemas/Run.d.ts +3 -0
- package/dist/schemas/Run.d.ts.map +1 -0
- package/dist/schemas/Run.js +31 -0
- package/dist/schemas/UserProfile.d.ts +11 -11
- package/dist/schemas/UserProfile.d.ts.map +1 -1
- package/dist/schemas/UserProfile.js +21 -7
- package/dist/sdk.d.ts +15 -14
- package/dist/sdk.d.ts.map +1 -1
- package/dist/sdk.js +1 -4
- package/dist/types/plugin.d.ts +6 -0
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/properties.d.ts +3 -1
- package/dist/types/properties.d.ts.map +1 -1
- package/dist/types/sdk.d.ts +11 -3
- package/dist/types/sdk.d.ts.map +1 -1
- package/dist/utils/domain-utils.d.ts +17 -16
- package/dist/utils/domain-utils.d.ts.map +1 -1
- package/dist/utils/domain-utils.js +53 -78
- package/dist/utils/domain-utils.test.js +157 -3
- package/dist/utils/file-utils.d.ts +4 -0
- package/dist/utils/file-utils.d.ts.map +1 -0
- package/dist/utils/file-utils.js +74 -0
- package/dist/utils/file-utils.test.d.ts +2 -0
- package/dist/utils/file-utils.test.d.ts.map +1 -0
- package/dist/utils/file-utils.test.js +51 -0
- package/dist/utils/schema-utils.d.ts +44 -21
- package/dist/utils/schema-utils.d.ts.map +1 -1
- package/dist/utils/schema-utils.js +17 -11
- package/package.json +1 -1
- package/src/api/client.ts +12 -0
- package/src/api/debug.test.ts +76 -0
- package/src/api/debug.ts +46 -2
- package/src/api/schemas.ts +51 -29
- package/src/index.ts +5 -6
- package/src/plugins/apps/index.ts +9 -2
- package/src/plugins/findFirstAuthentication/index.test.ts +8 -5
- package/src/plugins/findFirstAuthentication/index.ts +14 -2
- package/src/plugins/findUniqueAuthentication/index.ts +4 -0
- package/src/plugins/getAction/index.ts +14 -0
- package/src/plugins/getApp/index.test.ts +3 -3
- package/src/plugins/getApp/index.ts +20 -14
- package/src/plugins/getApp/schemas.ts +7 -12
- package/src/plugins/getAuthentication/index.test.ts +1 -1
- package/src/plugins/getAuthentication/index.ts +8 -0
- package/src/plugins/getProfile/index.ts +4 -0
- package/src/plugins/getProfile/schemas.ts +4 -6
- package/src/plugins/listActions/index.test.ts +8 -7
- package/src/plugins/listActions/index.ts +12 -3
- package/src/plugins/listActions/schemas.ts +20 -25
- package/src/plugins/listApps/index.test.ts +24 -3
- package/src/plugins/listApps/index.ts +50 -25
- package/src/plugins/listApps/schemas.ts +17 -26
- package/src/plugins/listAuthentications/index.test.ts +52 -15
- package/src/plugins/listAuthentications/index.ts +15 -2
- package/src/plugins/listAuthentications/schemas.ts +4 -0
- package/src/plugins/listInputFieldChoices/index.ts +21 -1
- package/src/plugins/listInputFieldChoices/schemas.ts +61 -76
- package/src/plugins/listInputFields/index.test.ts +5 -9
- package/src/plugins/listInputFields/index.ts +20 -2
- package/src/plugins/manifest/index.test.ts +503 -197
- package/src/plugins/manifest/index.ts +338 -82
- package/src/plugins/manifest/schemas.ts +9 -2
- package/src/plugins/registry/index.ts +8 -2
- package/src/plugins/request/index.ts +1 -0
- package/src/plugins/runAction/index.ts +18 -0
- package/src/resolvers/actionKey.ts +15 -13
- package/src/resolvers/actionType.ts +10 -12
- package/src/resolvers/appKey.ts +2 -6
- package/src/resolvers/authenticationId.ts +25 -19
- package/src/resolvers/index.ts +7 -113
- package/src/resolvers/inputFieldKey.ts +38 -0
- package/src/resolvers/inputs.ts +28 -10
- package/src/schemas/Action.ts +8 -3
- package/src/schemas/App.ts +7 -9
- package/src/schemas/Auth.ts +1 -1
- package/src/schemas/Field.ts +24 -57
- package/src/schemas/Run.ts +40 -0
- package/src/schemas/UserProfile.ts +24 -7
- package/src/sdk.ts +18 -12
- package/src/types/plugin.ts +8 -0
- package/src/types/sdk.ts +31 -21
- package/src/utils/domain-utils.test.ts +196 -2
- package/src/utils/domain-utils.ts +77 -102
- package/src/utils/file-utils.test.ts +73 -0
- package/src/utils/file-utils.ts +94 -0
- package/src/utils/schema-utils.ts +96 -44
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/plugins/lockVersion/index.d.ts +0 -24
- package/dist/plugins/lockVersion/index.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.js +0 -72
- package/dist/plugins/lockVersion/index.test.d.ts +0 -2
- package/dist/plugins/lockVersion/index.test.d.ts.map +0 -1
- package/dist/plugins/lockVersion/index.test.js +0 -129
- package/dist/plugins/lockVersion/schemas.d.ts +0 -10
- package/dist/plugins/lockVersion/schemas.d.ts.map +0 -1
- package/dist/plugins/lockVersion/schemas.js +0 -6
- package/src/plugins/lockVersion/index.test.ts +0 -176
- package/src/plugins/lockVersion/index.ts +0 -112
- package/src/plugins/lockVersion/schemas.ts +0 -9
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
manifestPlugin,
|
|
4
|
+
readManifestFromFile,
|
|
5
|
+
findManifestEntry,
|
|
6
|
+
getPreferredManifestEntryKey,
|
|
7
|
+
} from "./index";
|
|
3
8
|
import { createSdk } from "../../sdk";
|
|
4
9
|
import type { Manifest } from "./schemas";
|
|
5
10
|
|
|
6
|
-
// Mock
|
|
7
|
-
vi.mock("
|
|
8
|
-
|
|
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}`)),
|
|
9
16
|
}));
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
resolve: vi.fn((path) => `/resolved/${path}`),
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
import { readFileSync } from "fs";
|
|
16
|
-
import { resolve } from "path";
|
|
18
|
+
import { readFile, resolve } from "../../utils/file-utils";
|
|
17
19
|
import type { ApiClient } from "../../api";
|
|
18
20
|
|
|
19
|
-
const
|
|
21
|
+
const mockReadFile = vi.mocked(readFile);
|
|
20
22
|
const mockResolve = vi.mocked(resolve);
|
|
23
|
+
const mockResolveAppKeys = vi.fn();
|
|
21
24
|
|
|
22
25
|
describe("manifestPlugin", () => {
|
|
23
26
|
const mockManifest = {
|
|
@@ -43,63 +46,100 @@ describe("manifestPlugin", () => {
|
|
|
43
46
|
vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
44
47
|
|
|
45
48
|
mockApiClient = {
|
|
46
|
-
get: vi.fn().
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
service_id: null,
|
|
55
|
-
auth_type: "oauth",
|
|
56
|
-
auth_fields: [
|
|
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
57
|
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
],
|
|
62
107
|
},
|
|
63
108
|
],
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
is_adoptable: true,
|
|
89
|
-
is_usable: true,
|
|
90
|
-
update_cta_level: "info",
|
|
91
|
-
update_cta_message:
|
|
92
|
-
"This version is legacy. Consider updating to the latest version for better support.",
|
|
93
|
-
badges: [
|
|
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: [
|
|
94
133
|
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
134
|
+
id: "SlackCLIAPI@1.30.0",
|
|
135
|
+
name: "Slack",
|
|
136
|
+
slug: "slack",
|
|
137
|
+
images: {},
|
|
99
138
|
},
|
|
100
139
|
],
|
|
101
|
-
}
|
|
102
|
-
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
return Promise.resolve({ count: 0, results: [] });
|
|
103
143
|
}),
|
|
104
144
|
} as Partial<ApiClient> as ApiClient;
|
|
105
145
|
|
|
@@ -141,10 +181,7 @@ describe("manifestPlugin", () => {
|
|
|
141
181
|
});
|
|
142
182
|
|
|
143
183
|
function createTestSdk(options: any = {}) {
|
|
144
|
-
return createSdk(options)
|
|
145
|
-
.addPlugin(apiPlugin)
|
|
146
|
-
.addPlugin(listAppsMockPlugin)
|
|
147
|
-
.addPlugin(manifestPlugin);
|
|
184
|
+
return createSdk(options).addPlugin(apiPlugin).addPlugin(manifestPlugin);
|
|
148
185
|
}
|
|
149
186
|
|
|
150
187
|
describe("plugin initialization", () => {
|
|
@@ -153,28 +190,20 @@ describe("manifestPlugin", () => {
|
|
|
153
190
|
const context = sdk.getContext();
|
|
154
191
|
|
|
155
192
|
expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
|
|
156
|
-
expect(context.getImplementation).toBeInstanceOf(Function);
|
|
157
|
-
expect(context.getManifestEntry).toBeInstanceOf(Function);
|
|
158
193
|
});
|
|
159
194
|
|
|
160
|
-
it("should provide helper functions with manifestPath", () => {
|
|
161
|
-
|
|
195
|
+
it("should provide helper functions with manifestPath", async () => {
|
|
196
|
+
mockReadFile.mockResolvedValue(mockManifestContent);
|
|
162
197
|
|
|
163
198
|
const sdk = createTestSdk({ manifestPath: "manifest.json" });
|
|
164
199
|
const context = sdk.getContext();
|
|
165
200
|
|
|
166
201
|
expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
|
|
167
|
-
expect(context.getImplementation).toBeInstanceOf(Function);
|
|
168
|
-
expect(context.getManifestEntry).toBeInstanceOf(Function);
|
|
169
202
|
|
|
170
203
|
// Verify file operations happen when functions are called
|
|
171
|
-
|
|
204
|
+
await context.getVersionedImplementationId("slack");
|
|
172
205
|
expect(mockResolve).toHaveBeenCalledWith("manifest.json");
|
|
173
|
-
expect(
|
|
174
|
-
"/resolved/manifest.json",
|
|
175
|
-
"utf8",
|
|
176
|
-
);
|
|
177
|
-
expect(manifestEntry).toEqual(mockManifest.apps.slack);
|
|
206
|
+
expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
|
|
178
207
|
});
|
|
179
208
|
|
|
180
209
|
it("should provide helper functions when no manifest or path provided", () => {
|
|
@@ -182,29 +211,22 @@ describe("manifestPlugin", () => {
|
|
|
182
211
|
const context = sdk.getContext();
|
|
183
212
|
|
|
184
213
|
expect(context.getVersionedImplementationId).toBeInstanceOf(Function);
|
|
185
|
-
expect(context.getImplementation).toBeInstanceOf(Function);
|
|
186
|
-
expect(context.getManifestEntry).toBeInstanceOf(Function);
|
|
187
214
|
|
|
188
|
-
//
|
|
189
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
190
|
-
expect(manifestEntry).toBeNull();
|
|
215
|
+
// Manifest functions are available even when no manifest is provided
|
|
191
216
|
});
|
|
192
217
|
|
|
193
218
|
it("should prioritize direct manifest over manifestPath", () => {
|
|
194
|
-
|
|
219
|
+
mockReadFile.mockResolvedValue(
|
|
195
220
|
'{"apps": {"other": {"implementationName": "Other", "version": "1.0.0"}}}',
|
|
196
221
|
);
|
|
197
222
|
|
|
198
|
-
|
|
223
|
+
createTestSdk({
|
|
199
224
|
manifest: mockManifest,
|
|
200
225
|
manifestPath: "manifest.json",
|
|
201
226
|
});
|
|
202
|
-
const context = sdk.getContext();
|
|
203
227
|
|
|
204
|
-
// Verify that direct manifest is used
|
|
205
|
-
|
|
206
|
-
expect(manifestEntry).toEqual(mockManifest.apps.slack);
|
|
207
|
-
expect(mockReadFileSync).not.toHaveBeenCalled();
|
|
228
|
+
// Verify that direct manifest is used and file loading is not needed
|
|
229
|
+
expect(mockReadFile).not.toHaveBeenCalled();
|
|
208
230
|
});
|
|
209
231
|
});
|
|
210
232
|
|
|
@@ -238,54 +260,11 @@ describe("manifestPlugin", () => {
|
|
|
238
260
|
});
|
|
239
261
|
});
|
|
240
262
|
|
|
241
|
-
describe("getImplementation function", () => {
|
|
242
|
-
it("should return app from manifest when available", async () => {
|
|
243
|
-
const sdk = createTestSdk({ manifest: mockManifest });
|
|
244
|
-
const context = sdk.getContext();
|
|
245
|
-
|
|
246
|
-
const result = await context.getImplementation("slack");
|
|
247
|
-
expect(result).toBeDefined();
|
|
248
|
-
expect(mockApiClient.get).toHaveBeenCalledWith(
|
|
249
|
-
"/api/v4/implementations/",
|
|
250
|
-
{
|
|
251
|
-
searchParams: {
|
|
252
|
-
selected_apis: "SlackCLIAPI@1.21.1",
|
|
253
|
-
},
|
|
254
|
-
},
|
|
255
|
-
);
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("should fetch app from API when not in manifest", async () => {
|
|
259
|
-
const sdk = createTestSdk();
|
|
260
|
-
const context = sdk.getContext();
|
|
261
|
-
|
|
262
|
-
const result = await context.getImplementation("slack");
|
|
263
|
-
expect(result).toBeDefined();
|
|
264
|
-
expect(result?.title).toBe("Slack");
|
|
265
|
-
expect(result?.current_implementation_id).toBe("SlackCLIAPI@1.30.0");
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it("should return null when app cannot be fetched", async () => {
|
|
269
|
-
mockSdk.listApps = vi.fn().mockReturnValue({
|
|
270
|
-
items: vi.fn().mockReturnValue([][Symbol.iterator]()),
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
const sdk = createTestSdk();
|
|
274
|
-
const context = sdk.getContext();
|
|
275
|
-
|
|
276
|
-
const result = await context.getImplementation("nonexistent");
|
|
277
|
-
expect(result).toBeNull();
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
|
|
281
263
|
describe("manifest parsing", () => {
|
|
282
264
|
it("should parse valid manifest content", () => {
|
|
283
|
-
|
|
284
|
-
const context = sdk.getContext();
|
|
265
|
+
createTestSdk({ manifest: mockManifest });
|
|
285
266
|
|
|
286
|
-
// Verify manifest
|
|
287
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
288
|
-
expect(manifestEntry).toEqual(mockManifest.apps.slack);
|
|
267
|
+
// Verify manifest is parsed correctly for other functions to use
|
|
289
268
|
});
|
|
290
269
|
|
|
291
270
|
it("should handle manifest with missing version", () => {
|
|
@@ -297,68 +276,58 @@ describe("manifestPlugin", () => {
|
|
|
297
276
|
},
|
|
298
277
|
};
|
|
299
278
|
|
|
300
|
-
|
|
301
|
-
const context = sdk.getContext();
|
|
279
|
+
createTestSdk({ manifest: manifestWithoutVersion });
|
|
302
280
|
|
|
303
|
-
// Verify manifest
|
|
304
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
305
|
-
expect(manifestEntry).toEqual(manifestWithoutVersion.apps.slack);
|
|
281
|
+
// Verify manifest with missing version is handled correctly
|
|
306
282
|
});
|
|
307
283
|
|
|
308
284
|
it("should handle empty manifest", () => {
|
|
309
|
-
|
|
310
|
-
const context = sdk.getContext();
|
|
285
|
+
createTestSdk({ manifest: {} });
|
|
311
286
|
|
|
312
|
-
// Verify
|
|
313
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
314
|
-
expect(manifestEntry).toBeNull();
|
|
287
|
+
// Verify empty manifest is handled correctly
|
|
315
288
|
});
|
|
316
289
|
});
|
|
317
290
|
|
|
318
291
|
describe("file loading", () => {
|
|
319
|
-
it("should load manifest from file successfully", () => {
|
|
320
|
-
|
|
292
|
+
it("should load manifest from file successfully", async () => {
|
|
293
|
+
mockReadFile.mockResolvedValue(mockManifestContent);
|
|
321
294
|
|
|
322
295
|
const sdk = createTestSdk({ manifestPath: "manifest.json" });
|
|
323
296
|
const context = sdk.getContext();
|
|
324
297
|
|
|
325
298
|
// Verify that file operations happen when functions are called
|
|
326
|
-
|
|
299
|
+
await context.getVersionedImplementationId("slack");
|
|
327
300
|
expect(mockResolve).toHaveBeenCalledWith("manifest.json");
|
|
328
|
-
expect(
|
|
329
|
-
"/resolved/manifest.json",
|
|
330
|
-
"utf8",
|
|
331
|
-
);
|
|
332
|
-
expect(manifestEntry).toEqual(mockManifest.apps.slack);
|
|
301
|
+
expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
|
|
333
302
|
});
|
|
334
303
|
|
|
335
|
-
it("should handle file read errors gracefully", () => {
|
|
336
|
-
|
|
337
|
-
throw new Error("File not found");
|
|
338
|
-
});
|
|
304
|
+
it("should handle file read errors gracefully", async () => {
|
|
305
|
+
mockReadFile.mockRejectedValue(new Error("File not found"));
|
|
339
306
|
|
|
340
307
|
const sdk = createTestSdk({ manifestPath: "nonexistent.json" });
|
|
341
308
|
const context = sdk.getContext();
|
|
342
309
|
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
|
|
310
|
+
// Trigger file loading by calling a function
|
|
311
|
+
await context.getVersionedImplementationId("slack");
|
|
312
|
+
|
|
313
|
+
// Verify file loading errors are handled gracefully
|
|
346
314
|
expect(console.warn).toHaveBeenCalledWith(
|
|
347
315
|
expect.stringContaining(
|
|
348
|
-
"Failed to
|
|
316
|
+
"Failed to read manifest from nonexistent.json",
|
|
349
317
|
),
|
|
350
318
|
);
|
|
351
319
|
});
|
|
352
320
|
|
|
353
|
-
it("should handle invalid JSON gracefully", () => {
|
|
354
|
-
|
|
321
|
+
it("should handle invalid JSON gracefully", async () => {
|
|
322
|
+
mockReadFile.mockResolvedValue("invalid json content");
|
|
355
323
|
|
|
356
324
|
const sdk = createTestSdk({ manifestPath: "invalid.json" });
|
|
357
325
|
const context = sdk.getContext();
|
|
358
326
|
|
|
359
|
-
//
|
|
360
|
-
|
|
361
|
-
|
|
327
|
+
// Trigger file loading by calling a function
|
|
328
|
+
await context.getVersionedImplementationId("slack");
|
|
329
|
+
|
|
330
|
+
// Verify JSON parsing errors are handled gracefully
|
|
362
331
|
expect(console.warn).toHaveBeenCalledWith(
|
|
363
332
|
expect.stringContaining(
|
|
364
333
|
"Failed to parse manifest from /resolved/invalid.json",
|
|
@@ -368,25 +337,19 @@ describe("manifestPlugin", () => {
|
|
|
368
337
|
});
|
|
369
338
|
|
|
370
339
|
it("should handle JSON without apps property", () => {
|
|
371
|
-
|
|
340
|
+
mockReadFile.mockResolvedValue('{"other": "data"}');
|
|
372
341
|
|
|
373
|
-
|
|
374
|
-
const context = sdk.getContext();
|
|
342
|
+
createTestSdk({ manifestPath: "no-apps.json" });
|
|
375
343
|
|
|
376
|
-
// Verify
|
|
377
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
378
|
-
expect(manifestEntry).toBeNull();
|
|
344
|
+
// Verify missing apps property is handled correctly
|
|
379
345
|
});
|
|
380
346
|
|
|
381
347
|
it("should handle JSON with invalid apps format", () => {
|
|
382
|
-
|
|
348
|
+
mockReadFile.mockResolvedValue('{"apps": "not an object"}');
|
|
383
349
|
|
|
384
|
-
|
|
385
|
-
const context = sdk.getContext();
|
|
350
|
+
createTestSdk({ manifestPath: "invalid-apps.json" });
|
|
386
351
|
|
|
387
|
-
// Verify
|
|
388
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
389
|
-
expect(manifestEntry).toBeNull();
|
|
352
|
+
// Verify invalid apps format is handled correctly
|
|
390
353
|
});
|
|
391
354
|
});
|
|
392
355
|
|
|
@@ -398,17 +361,294 @@ describe("manifestPlugin", () => {
|
|
|
398
361
|
.addPlugin(manifestPlugin);
|
|
399
362
|
const context = sdk.getContext();
|
|
400
363
|
|
|
401
|
-
// Verify that the manifest data is used correctly
|
|
402
|
-
const manifestEntry = context.getManifestEntry("slack");
|
|
403
|
-
expect(manifestEntry).toEqual(mockManifest.apps.slack);
|
|
364
|
+
// Verify that the manifest data is used correctly by other functions
|
|
404
365
|
|
|
405
366
|
const versionedId = await context.getVersionedImplementationId("slack");
|
|
406
367
|
expect(versionedId).toBe("SlackCLIAPI@1.21.1");
|
|
407
368
|
});
|
|
408
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
|
+
"slack", // Input is slug
|
|
496
|
+
{ 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
|
+
});
|
|
409
649
|
});
|
|
410
650
|
|
|
411
|
-
describe("
|
|
651
|
+
describe("readManifestFromFile", () => {
|
|
412
652
|
const mockManifestContent = JSON.stringify({
|
|
413
653
|
apps: {
|
|
414
654
|
slack: {
|
|
@@ -426,16 +666,13 @@ describe("loadManifestFromFile", () => {
|
|
|
426
666
|
vi.clearAllMocks();
|
|
427
667
|
});
|
|
428
668
|
|
|
429
|
-
it("should
|
|
430
|
-
|
|
669
|
+
it("should read and parse manifest from file", async () => {
|
|
670
|
+
mockReadFile.mockResolvedValue(mockManifestContent);
|
|
431
671
|
|
|
432
|
-
const result =
|
|
672
|
+
const result = await readManifestFromFile("manifest.json");
|
|
433
673
|
|
|
434
674
|
expect(mockResolve).toHaveBeenCalledWith("manifest.json");
|
|
435
|
-
expect(
|
|
436
|
-
"/resolved/manifest.json",
|
|
437
|
-
"utf8",
|
|
438
|
-
);
|
|
675
|
+
expect(mockReadFile).toHaveBeenCalledWith("/resolved/manifest.json");
|
|
439
676
|
expect(result).toEqual({
|
|
440
677
|
apps: {
|
|
441
678
|
slack: {
|
|
@@ -450,21 +687,90 @@ describe("loadManifestFromFile", () => {
|
|
|
450
687
|
});
|
|
451
688
|
});
|
|
452
689
|
|
|
453
|
-
it("should handle file read errors", () => {
|
|
454
|
-
|
|
455
|
-
throw new Error("File not found");
|
|
456
|
-
});
|
|
690
|
+
it("should handle file read errors", async () => {
|
|
691
|
+
mockReadFile.mockRejectedValue(new Error("File not found"));
|
|
457
692
|
|
|
458
|
-
const result =
|
|
693
|
+
const result = await readManifestFromFile("nonexistent.json");
|
|
459
694
|
|
|
460
695
|
expect(result).toBeNull();
|
|
461
696
|
});
|
|
462
697
|
|
|
463
|
-
it("should handle invalid JSON content", () => {
|
|
464
|
-
|
|
698
|
+
it("should handle invalid JSON content", async () => {
|
|
699
|
+
mockReadFile.mockResolvedValue("invalid json");
|
|
465
700
|
|
|
466
|
-
const result =
|
|
701
|
+
const result = await readManifestFromFile("invalid.json");
|
|
467
702
|
|
|
468
703
|
expect(result).toBeNull();
|
|
469
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
|
+
});
|
|
470
776
|
});
|