@scalekit-sdk/node 2.2.0-beta.2 → 2.2.0-beta.3
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/lib/actions.d.ts +126 -0
- package/lib/actions.js +199 -0
- package/lib/actions.js.map +1 -0
- package/lib/core.js +1 -1
- package/lib/scalekit.d.ts +2 -0
- package/lib/scalekit.js +2 -0
- package/lib/scalekit.js.map +1 -1
- package/package.json +1 -1
- package/reference.md +307 -0
- package/src/actions.ts +349 -0
- package/src/core.ts +1 -1
- package/src/scalekit.ts +7 -0
- package/tests/actions.test.ts +356 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import ScalekitClient from "../src/scalekit";
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
|
|
3
|
+
import { TestDataGenerator, TestOrganizationManager } from "./utils/test-data";
|
|
4
|
+
import {
|
|
5
|
+
AuthorizationDetails,
|
|
6
|
+
CreateConnectedAccount,
|
|
7
|
+
OauthToken,
|
|
8
|
+
} from "../src/pkg/grpc/scalekit/v1/connected_accounts/connected_accounts_pb";
|
|
9
|
+
|
|
10
|
+
declare const global: {
|
|
11
|
+
client: ScalekitClient;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
describe("Actions", () => {
|
|
15
|
+
let client: ScalekitClient;
|
|
16
|
+
let testOrg: string;
|
|
17
|
+
|
|
18
|
+
// Happy-path assumptions for the test environment:
|
|
19
|
+
// - A GMAIL connector named "GMAIL-test" exists.
|
|
20
|
+
// - A connected account with identifier "default" is ACTIVE for that connector.
|
|
21
|
+
// - A tool named "gmail_fetch_mails" is available for that connection.
|
|
22
|
+
const GMAIL_CONNECTION_NAME = "GMAIL-test";
|
|
23
|
+
const GMAIL_IDENTIFIER = "default";
|
|
24
|
+
const GMAIL_TOOL_NAME = "gmail_fetch_mails";
|
|
25
|
+
const USER_PROFILE_PATH = "/v1/users/me/profile";
|
|
26
|
+
const FRESHDESK_CONNECTION_NAME = "freshdesk";
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
client = global.client;
|
|
30
|
+
testOrg = await TestOrganizationManager.createTestOrganization(client);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(async () => {
|
|
34
|
+
await TestOrganizationManager.cleanupTestOrganization(client, testOrg);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should expose actions on ScalekitClient", () => {
|
|
38
|
+
expect(client.actions).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should expose key connected-account helper methods", () => {
|
|
42
|
+
|
|
43
|
+
expect(typeof client.actions.createConnectedAccount).toBe("function");
|
|
44
|
+
expect(typeof client.actions.getOrCreateConnectedAccount).toBe("function");
|
|
45
|
+
expect(typeof client.actions.updateConnectedAccount).toBe("function");
|
|
46
|
+
expect(typeof client.actions.deleteConnectedAccount).toBe("function");
|
|
47
|
+
expect(typeof client.actions.getConnectedAccount).toBe("function");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("executeTool", () => {
|
|
51
|
+
it("should delegate to tools.executeTool with basic params", async () => {
|
|
52
|
+
try {
|
|
53
|
+
await client.actions.executeTool({
|
|
54
|
+
toolName: "test_tool",
|
|
55
|
+
toolInput: { key: "value" },
|
|
56
|
+
});
|
|
57
|
+
} catch (error: any) {
|
|
58
|
+
expect(error).toBeDefined();
|
|
59
|
+
expect(error.message || error.toString()).toBeDefined();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should execute a Gmail tool successfully via actions", async () => {
|
|
64
|
+
const toolInput = { max_results: 1 };
|
|
65
|
+
|
|
66
|
+
const response = await client.actions.executeTool({
|
|
67
|
+
toolName: GMAIL_TOOL_NAME,
|
|
68
|
+
toolInput,
|
|
69
|
+
identifier: GMAIL_IDENTIFIER,
|
|
70
|
+
connector: GMAIL_CONNECTION_NAME,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(response).toBeDefined();
|
|
74
|
+
// We expect a non-empty executionId and some data from the tool.
|
|
75
|
+
expect(typeof response.executionId).toBe("string");
|
|
76
|
+
expect(response.executionId.length).toBeGreaterThan(0);
|
|
77
|
+
expect(response.data).toBeDefined();
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("getAuthorizationLink", () => {
|
|
82
|
+
it("should call underlying magic link API", async () => {
|
|
83
|
+
try {
|
|
84
|
+
const response = await client.actions.getAuthorizationLink({
|
|
85
|
+
connectionName: "test_connector",
|
|
86
|
+
identifier: "test_identifier",
|
|
87
|
+
organizationId: testOrg,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
expect(response).toBeDefined();
|
|
91
|
+
} catch (error: any) {
|
|
92
|
+
expect(error).toBeDefined();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("createConnectedAccount", () => {
|
|
98
|
+
it("should construct CreateConnectedAccount payload", async () => {
|
|
99
|
+
const oauthToken = new OauthToken({
|
|
100
|
+
accessToken: "test_access_token",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const authorizationDetails = new AuthorizationDetails({
|
|
104
|
+
details: {
|
|
105
|
+
case: "oauthToken",
|
|
106
|
+
value: oauthToken,
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const response = await client.actions.createConnectedAccount({
|
|
112
|
+
connectionName: "test_connector",
|
|
113
|
+
identifier: `test_${Date.now()}`,
|
|
114
|
+
authorizationDetails,
|
|
115
|
+
organizationId: testOrg,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(response).toBeDefined();
|
|
119
|
+
} catch (error: any) {
|
|
120
|
+
expect(error).toBeDefined();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should validate required parameters", async () => {
|
|
125
|
+
const oauthToken = new OauthToken({
|
|
126
|
+
accessToken: "test_access_token",
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const authorizationDetails = new AuthorizationDetails({
|
|
130
|
+
details: {
|
|
131
|
+
case: "oauthToken",
|
|
132
|
+
value: oauthToken,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Missing connectionName
|
|
137
|
+
await expect(
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
+
client.actions.createConnectedAccount({
|
|
140
|
+
connectionName: "" as any,
|
|
141
|
+
identifier: "test_id",
|
|
142
|
+
authorizationDetails,
|
|
143
|
+
} as any)
|
|
144
|
+
).rejects.toThrow("connectionName is required");
|
|
145
|
+
|
|
146
|
+
// Missing identifier
|
|
147
|
+
await expect(
|
|
148
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
149
|
+
client.actions.createConnectedAccount({
|
|
150
|
+
connectionName: "GMAIL",
|
|
151
|
+
identifier: "" as any,
|
|
152
|
+
authorizationDetails,
|
|
153
|
+
} as any)
|
|
154
|
+
).rejects.toThrow("identifier is required");
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("getOrCreateConnectedAccount", () => {
|
|
159
|
+
it("should delegate validation to underlying client", async () => {
|
|
160
|
+
// connector / connectionName required
|
|
161
|
+
await expect(
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
+
client.actions.getOrCreateConnectedAccount({
|
|
164
|
+
connectionName: "" as any,
|
|
165
|
+
identifier: "user_123",
|
|
166
|
+
} as any)
|
|
167
|
+
).rejects.toThrow("connector is required");
|
|
168
|
+
|
|
169
|
+
// identifier required
|
|
170
|
+
await expect(
|
|
171
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
|
+
client.actions.getOrCreateConnectedAccount({
|
|
173
|
+
connectionName: "gmail",
|
|
174
|
+
identifier: "" as any,
|
|
175
|
+
} as any)
|
|
176
|
+
).rejects.toThrow("identifier is required");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("request", () => {
|
|
181
|
+
it("should validate required parameters", async () => {
|
|
182
|
+
await expect(
|
|
183
|
+
client.actions.request({
|
|
184
|
+
connectionName: "",
|
|
185
|
+
identifier: "",
|
|
186
|
+
path: "",
|
|
187
|
+
})
|
|
188
|
+
).rejects.toThrow();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should fetch user profile successfully via proxy request", async () => {
|
|
192
|
+
const response = await client.actions.request({
|
|
193
|
+
connectionName: GMAIL_CONNECTION_NAME,
|
|
194
|
+
identifier: GMAIL_IDENTIFIER,
|
|
195
|
+
path: USER_PROFILE_PATH,
|
|
196
|
+
method: "GET",
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
expect(response).toBeDefined();
|
|
200
|
+
expect(response.status).toBeGreaterThanOrEqual(200);
|
|
201
|
+
expect(response.status).toBeLessThan(300);
|
|
202
|
+
// Response data should be JSON-serializable user profile shape.
|
|
203
|
+
expect(response.data).toBeDefined();
|
|
204
|
+
expect(typeof response.data).toBe("object");
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("connected account lifecycle (Gmail OAuth)", () => {
|
|
209
|
+
it("should create, get, update, and delete a Gmail connected account with apiConfig", async () => {
|
|
210
|
+
const uniqueId = TestDataGenerator.generateUniqueId();
|
|
211
|
+
const identifier = `test_actions_gmail_${uniqueId}`;
|
|
212
|
+
|
|
213
|
+
const oauthToken = new OauthToken({
|
|
214
|
+
accessToken: "test_access_token_api",
|
|
215
|
+
refreshToken: "test_refresh_token_api",
|
|
216
|
+
scopes: ["read", "write"],
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const authorizationDetails = new AuthorizationDetails({
|
|
220
|
+
details: {
|
|
221
|
+
case: "oauthToken",
|
|
222
|
+
value: oauthToken,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const initialApiConfig = {
|
|
227
|
+
version: "v1.0",
|
|
228
|
+
domain: "gmail.com",
|
|
229
|
+
api_endpoint: "https://gmail.googleapis.com",
|
|
230
|
+
custom_auth_header: "Bearer",
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Create
|
|
234
|
+
const createResponse = await client.actions.createConnectedAccount({
|
|
235
|
+
connectionName: GMAIL_CONNECTION_NAME,
|
|
236
|
+
identifier,
|
|
237
|
+
authorizationDetails,
|
|
238
|
+
apiConfig: initialApiConfig,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
expect(createResponse.connectedAccount).toBeDefined();
|
|
242
|
+
const created = createResponse.connectedAccount!;
|
|
243
|
+
expect(created.identifier).toBe(identifier);
|
|
244
|
+
expect(created.apiConfig).toBeDefined();
|
|
245
|
+
|
|
246
|
+
// Get via actions wrapper
|
|
247
|
+
const getResponse = await client.actions.getConnectedAccount({
|
|
248
|
+
connectionName: GMAIL_CONNECTION_NAME,
|
|
249
|
+
identifier,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(getResponse.connectedAccount).toBeDefined();
|
|
253
|
+
expect(getResponse.connectedAccount!.identifier).toBe(identifier);
|
|
254
|
+
|
|
255
|
+
// Update apiConfig
|
|
256
|
+
const updatedApiConfig = {
|
|
257
|
+
version: "v2.0",
|
|
258
|
+
domain: "updated.gmail.com",
|
|
259
|
+
api_endpoint: "https://updated.gmail.googleapis.com",
|
|
260
|
+
custom_auth_header: "Updated Bearer",
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const updateResponse = await client.actions.updateConnectedAccount({
|
|
264
|
+
connectionName: GMAIL_CONNECTION_NAME,
|
|
265
|
+
identifier,
|
|
266
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
267
|
+
apiConfig: updatedApiConfig as any,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
expect(updateResponse.connectedAccount).toBeDefined();
|
|
271
|
+
const updated = updateResponse.connectedAccount!;
|
|
272
|
+
expect(updated.identifier).toBe(identifier);
|
|
273
|
+
expect(updated.apiConfig).toBeDefined();
|
|
274
|
+
|
|
275
|
+
// Delete via actions wrapper
|
|
276
|
+
const deleteResponse = await client.actions.deleteConnectedAccount({
|
|
277
|
+
connectionName: GMAIL_CONNECTION_NAME,
|
|
278
|
+
identifier,
|
|
279
|
+
});
|
|
280
|
+
expect(deleteResponse).toBeDefined();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe("connected account lifecycle (Freshdesk static auth)", () => {
|
|
285
|
+
it("should create, update, and delete a Freshdesk connected account with static auth", async () => {
|
|
286
|
+
const uniqueId = TestDataGenerator.generateUniqueId();
|
|
287
|
+
const identifier = `test_actions_freshdesk_${uniqueId}`;
|
|
288
|
+
|
|
289
|
+
// Initial static auth
|
|
290
|
+
const initialStaticAuth = {
|
|
291
|
+
static_auth: {
|
|
292
|
+
domain: "initial.freshdesk.com",
|
|
293
|
+
username: "initial_user",
|
|
294
|
+
password: "initial_password",
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
try {
|
|
299
|
+
// Create Freshdesk account
|
|
300
|
+
const createResponse = await client.actions.createConnectedAccount({
|
|
301
|
+
connectionName: FRESHDESK_CONNECTION_NAME,
|
|
302
|
+
identifier,
|
|
303
|
+
// For Node, authorizationDetails is carried through as a generic value; we rely
|
|
304
|
+
// on the backend to interpret the structure.
|
|
305
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
306
|
+
authorizationDetails: initialStaticAuth as any,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
expect(createResponse.connectedAccount).toBeDefined();
|
|
310
|
+
const created = createResponse.connectedAccount!;
|
|
311
|
+
expect(created.identifier).toBe(identifier);
|
|
312
|
+
|
|
313
|
+
// Update static auth
|
|
314
|
+
const updatedStaticAuth = {
|
|
315
|
+
static_auth: {
|
|
316
|
+
domain: "updated.freshdesk.com",
|
|
317
|
+
username: "updated_user",
|
|
318
|
+
password: "updated_password",
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const updateResponse = await client.actions.updateConnectedAccount({
|
|
323
|
+
connectionName: FRESHDESK_CONNECTION_NAME,
|
|
324
|
+
identifier,
|
|
325
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
326
|
+
authorizationDetails: updatedStaticAuth as any,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
expect(updateResponse.connectedAccount).toBeDefined();
|
|
330
|
+
const updated = updateResponse.connectedAccount!;
|
|
331
|
+
expect(updated.identifier).toBe(identifier);
|
|
332
|
+
|
|
333
|
+
// Best-effort check: authorizationDetails should be present
|
|
334
|
+
expect(updated.authorizationDetails).toBeDefined();
|
|
335
|
+
|
|
336
|
+
// Delete Freshdesk account
|
|
337
|
+
const deleteResponse = await client.actions.deleteConnectedAccount({
|
|
338
|
+
connectionName: FRESHDESK_CONNECTION_NAME,
|
|
339
|
+
identifier,
|
|
340
|
+
});
|
|
341
|
+
expect(deleteResponse).toBeDefined();
|
|
342
|
+
} catch (error: any) {
|
|
343
|
+
// If the Freshdesk connector is not configured in this environment,
|
|
344
|
+
// treat this as a skipped integration scenario rather than a hard failure.
|
|
345
|
+
if (
|
|
346
|
+
error?.name === "ScalekitNotFoundException" ||
|
|
347
|
+
/connection not found/i.test(String(error.message ?? error))
|
|
348
|
+
) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
|