zinkee 0.1.44 → 0.1.45
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/README.md +4 -0
- package/dist/chunk-MHEVRMDX.js +1230 -0
- package/dist/chunk-MHEVRMDX.js.map +1 -0
- package/{src/utils/examples.ts → dist/examples-E6FFEVBE.js} +856 -1141
- package/dist/examples-E6FFEVBE.js.map +1 -0
- package/dist/index.js +1145 -8456
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
- package/.github/workflows/npm-publish.yml +0 -77
- package/.github/workflows/pr-checks.yml +0 -36
- package/AGENTS.md +0 -110
- package/docs/cli-contract.md +0 -41
- package/docs/npm-release.md +0 -64
- package/docs/superpowers/plans/2026-03-24-zinkee-cli-implementation.md +0 -837
- package/docs/superpowers/plans/2026-03-25-cli-backend-error-contract.md +0 -503
- package/docs/superpowers/plans/2026-03-26-display-freeform-create.md +0 -389
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-backend-blockers.md +0 -172
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-design.md +0 -1576
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-checklist.md +0 -215
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-design.md +0 -492
- package/docs/superpowers/specs/2026-03-24-zinkee-cli-e2e-status.md +0 -307
- package/docs/superpowers/specs/2026-07-30-cli-pr-checks-design.md +0 -37
- package/src/api/automations.ts +0 -404
- package/src/api/comments.ts +0 -51
- package/src/api/displays.ts +0 -337
- package/src/api/document-templates.ts +0 -110
- package/src/api/files.ts +0 -50
- package/src/api/formulas.test.ts +0 -65
- package/src/api/formulas.ts +0 -67
- package/src/api/logs.ts +0 -34
- package/src/api/navigation.ts +0 -70
- package/src/api/records.ts +0 -184
- package/src/api/schemas.ts +0 -148
- package/src/api/teamspace.ts +0 -110
- package/src/cli-examples.ts +0 -130
- package/src/cli-runner.ts +0 -95
- package/src/client.test.ts +0 -189
- package/src/client.ts +0 -269
- package/src/command-registry.ts +0 -882
- package/src/commands/automations.test.ts +0 -1030
- package/src/commands/automations.ts +0 -2102
- package/src/commands/comments.test.ts +0 -214
- package/src/commands/comments.ts +0 -303
- package/src/commands/config.test.ts +0 -81
- package/src/commands/config.ts +0 -150
- package/src/commands/displays.test.ts +0 -1105
- package/src/commands/displays.ts +0 -1442
- package/src/commands/document-templates.test.ts +0 -569
- package/src/commands/document-templates.ts +0 -563
- package/src/commands/files.test.ts +0 -284
- package/src/commands/files.ts +0 -280
- package/src/commands/formulas.test.ts +0 -194
- package/src/commands/formulas.ts +0 -243
- package/src/commands/logs.test.ts +0 -123
- package/src/commands/logs.ts +0 -159
- package/src/commands/navigation.test.ts +0 -211
- package/src/commands/navigation.ts +0 -348
- package/src/commands/profiles.test.ts +0 -191
- package/src/commands/profiles.ts +0 -303
- package/src/commands/records.test.ts +0 -860
- package/src/commands/records.ts +0 -883
- package/src/commands/schemas.test.ts +0 -1252
- package/src/commands/schemas.ts +0 -890
- package/src/commands/teamspace.test.ts +0 -229
- package/src/commands/teamspace.ts +0 -546
- package/src/completion/engine.test.ts +0 -138
- package/src/completion/engine.ts +0 -168
- package/src/completion/install.test.ts +0 -179
- package/src/completion/install.ts +0 -260
- package/src/completion/runtime.ts +0 -150
- package/src/completion/scripts.ts +0 -91
- package/src/config.test.ts +0 -362
- package/src/config.ts +0 -294
- package/src/index.test.ts +0 -217
- package/src/index.ts +0 -8
- package/src/parsers/expressions.test.ts +0 -95
- package/src/parsers/expressions.ts +0 -128
- package/src/parsers/kv.test.ts +0 -35
- package/src/parsers/kv.ts +0 -49
- package/src/parsers/selectors.test.ts +0 -23
- package/src/parsers/selectors.ts +0 -29
- package/src/program.ts +0 -64
- package/src/runtime-context.ts +0 -103
- package/src/types.ts +0 -76
- package/src/utils/argv-rewrite.test.ts +0 -149
- package/src/utils/argv-rewrite.ts +0 -269
- package/src/utils/errors.test.ts +0 -85
- package/src/utils/errors.ts +0 -191
- package/src/utils/examples.test.ts +0 -374
- package/src/utils/output.test.ts +0 -65
- package/src/utils/output.ts +0 -154
- package/src/utils/schema-fields.ts +0 -1016
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -13
|
@@ -1,1030 +0,0 @@
|
|
|
1
|
-
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
import { Command } from "commander";
|
|
6
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { registerAutomationsCommands } from "./automations.js";
|
|
9
|
-
|
|
10
|
-
function createTempConfig(contents: string): string {
|
|
11
|
-
const dir = mkdtempSync(join(tmpdir(), "zinkee-automations-"));
|
|
12
|
-
const filePath = join(dir, "config.toml");
|
|
13
|
-
writeFileSync(filePath, contents, "utf8");
|
|
14
|
-
return filePath;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function createApiConfig(): string {
|
|
18
|
-
return createTempConfig(`
|
|
19
|
-
default_profile = "prod"
|
|
20
|
-
|
|
21
|
-
[profiles.prod]
|
|
22
|
-
api_key = "automations-token-1234"
|
|
23
|
-
base_url = "https://api.zinkee.test"
|
|
24
|
-
`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function createStdoutBuffer() {
|
|
28
|
-
let output = "";
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
stdout: {
|
|
32
|
-
write(chunk: string) {
|
|
33
|
-
output += chunk;
|
|
34
|
-
return true;
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
read() {
|
|
38
|
-
return output;
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function jsonResponse(body: unknown, status: number = 200): Response {
|
|
44
|
-
return new Response(JSON.stringify(body), {
|
|
45
|
-
status,
|
|
46
|
-
headers: {
|
|
47
|
-
"content-type": "application/json",
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function createFetchMock() {
|
|
53
|
-
const fetchMock = vi.fn<typeof fetch>();
|
|
54
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
55
|
-
return fetchMock;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function buildAutomationsProgram(options: { configPath?: string; stdout?: { write(chunk: string): unknown } } = {}): Command {
|
|
59
|
-
const program = new Command();
|
|
60
|
-
|
|
61
|
-
program
|
|
62
|
-
.name("zinkee")
|
|
63
|
-
.description("CLI for Zinkee API v2")
|
|
64
|
-
.version("0.1.0")
|
|
65
|
-
.enablePositionalOptions()
|
|
66
|
-
.showHelpAfterError()
|
|
67
|
-
.option("--profile <name>", "select a profile from config")
|
|
68
|
-
.option("--base-url <url>", "override the configured base URL")
|
|
69
|
-
.option("--api-key <token>", "override the configured API key")
|
|
70
|
-
.option("--read-only", "run in read-only mode")
|
|
71
|
-
.option("--json", "emit JSON output");
|
|
72
|
-
|
|
73
|
-
registerAutomationsCommands(program, options);
|
|
74
|
-
|
|
75
|
-
return program;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
afterEach(() => {
|
|
79
|
-
vi.unstubAllGlobals();
|
|
80
|
-
vi.restoreAllMocks();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe("automations commands", () => {
|
|
84
|
-
it("registers the automations command group", () => {
|
|
85
|
-
const program = buildAutomationsProgram();
|
|
86
|
-
|
|
87
|
-
expect(program.commands.some((command) => command.name() === "automations")).toBe(true);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it("moves an automation with the folder id in the body, not the path", async () => {
|
|
91
|
-
const configPath = createApiConfig();
|
|
92
|
-
const output = createStdoutBuffer();
|
|
93
|
-
const fetchMock = createFetchMock();
|
|
94
|
-
fetchMock.mockResolvedValueOnce(
|
|
95
|
-
jsonResponse({ id: "folder-1", name: "Ops", isRoot: false }),
|
|
96
|
-
);
|
|
97
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
98
|
-
|
|
99
|
-
await program.parseAsync([
|
|
100
|
-
"node",
|
|
101
|
-
"zinkee",
|
|
102
|
-
"--json",
|
|
103
|
-
"automations",
|
|
104
|
-
"folders",
|
|
105
|
-
"move",
|
|
106
|
-
"11111111-1111-4111-a111-111111111111",
|
|
107
|
-
"--to",
|
|
108
|
-
"22222222-2222-4222-a222-222222222222",
|
|
109
|
-
]);
|
|
110
|
-
|
|
111
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
112
|
-
"https://api.zinkee.test/api/v2/automation-folders/automations:move",
|
|
113
|
-
);
|
|
114
|
-
const init = fetchMock.mock.calls[0]?.[1];
|
|
115
|
-
expect(init?.method).toBe("POST");
|
|
116
|
-
expect(JSON.parse(String(init?.body))).toEqual({
|
|
117
|
-
automationId: "11111111-1111-4111-a111-111111111111",
|
|
118
|
-
targetFolderId: "22222222-2222-4222-a222-222222222222",
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("sets trigger payloads through the generic trigger flags", async () => {
|
|
123
|
-
const configPath = createApiConfig();
|
|
124
|
-
const output = createStdoutBuffer();
|
|
125
|
-
const fetchMock = createFetchMock();
|
|
126
|
-
fetchMock.mockResolvedValueOnce(
|
|
127
|
-
jsonResponse({
|
|
128
|
-
ok: true,
|
|
129
|
-
}),
|
|
130
|
-
);
|
|
131
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
132
|
-
|
|
133
|
-
await program.parseAsync([
|
|
134
|
-
"node",
|
|
135
|
-
"zinkee",
|
|
136
|
-
"--json",
|
|
137
|
-
"automations",
|
|
138
|
-
"trigger",
|
|
139
|
-
"set",
|
|
140
|
-
"automation-1",
|
|
141
|
-
"--trigger-type",
|
|
142
|
-
"record_changed",
|
|
143
|
-
"--trigger-schema",
|
|
144
|
-
"schema-1",
|
|
145
|
-
"--trigger-field",
|
|
146
|
-
"field-1",
|
|
147
|
-
"--trigger-field",
|
|
148
|
-
"field-2",
|
|
149
|
-
"--condition",
|
|
150
|
-
"status:eq:open",
|
|
151
|
-
]);
|
|
152
|
-
|
|
153
|
-
const init = fetchMock.mock.calls[0]?.[1];
|
|
154
|
-
|
|
155
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
156
|
-
"https://api.zinkee.test/api/v2/automations/automation-1/trigger",
|
|
157
|
-
);
|
|
158
|
-
expect(init?.method).toBe("PUT");
|
|
159
|
-
expect(JSON.parse(String(init?.body))).toEqual({
|
|
160
|
-
type: "record_changed",
|
|
161
|
-
config: {
|
|
162
|
-
schemaId: "schema-1",
|
|
163
|
-
fieldIds: ["field-1", "field-2"],
|
|
164
|
-
conditions: [
|
|
165
|
-
{
|
|
166
|
-
field: "status",
|
|
167
|
-
comparator: "eq",
|
|
168
|
-
values: [
|
|
169
|
-
{
|
|
170
|
-
rawValue: "open",
|
|
171
|
-
source: null,
|
|
172
|
-
},
|
|
173
|
-
],
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
expect(JSON.parse(output.read())).toEqual({
|
|
179
|
-
data: {
|
|
180
|
-
ok: true,
|
|
181
|
-
},
|
|
182
|
-
meta: {
|
|
183
|
-
command: "automations trigger set",
|
|
184
|
-
configPath,
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it("adds action payloads through the generic --type path", async () => {
|
|
190
|
-
const configPath = createApiConfig();
|
|
191
|
-
const output = createStdoutBuffer();
|
|
192
|
-
const fetchMock = createFetchMock();
|
|
193
|
-
fetchMock.mockResolvedValueOnce(
|
|
194
|
-
jsonResponse({
|
|
195
|
-
id: "action-1",
|
|
196
|
-
}),
|
|
197
|
-
);
|
|
198
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
199
|
-
|
|
200
|
-
await program.parseAsync([
|
|
201
|
-
"node",
|
|
202
|
-
"zinkee",
|
|
203
|
-
"--json",
|
|
204
|
-
"automations",
|
|
205
|
-
"actions",
|
|
206
|
-
"add",
|
|
207
|
-
"automation-1",
|
|
208
|
-
"--type",
|
|
209
|
-
"http_request",
|
|
210
|
-
"--name",
|
|
211
|
-
"Notify",
|
|
212
|
-
"--method",
|
|
213
|
-
"post",
|
|
214
|
-
"--url",
|
|
215
|
-
"https://example.com/hook",
|
|
216
|
-
"--header",
|
|
217
|
-
"authorization=Bearer token",
|
|
218
|
-
"--body",
|
|
219
|
-
'{"ok":true}',
|
|
220
|
-
"--content-type",
|
|
221
|
-
"application/json",
|
|
222
|
-
"--timeout-ms",
|
|
223
|
-
"5000",
|
|
224
|
-
"--var",
|
|
225
|
-
"env=prod",
|
|
226
|
-
"--connection",
|
|
227
|
-
"connection-1",
|
|
228
|
-
]);
|
|
229
|
-
|
|
230
|
-
const init = fetchMock.mock.calls[0]?.[1];
|
|
231
|
-
|
|
232
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
233
|
-
"https://api.zinkee.test/api/v2/automations/automation-1/actions",
|
|
234
|
-
);
|
|
235
|
-
expect(init?.method).toBe("POST");
|
|
236
|
-
expect(JSON.parse(String(init?.body))).toEqual({
|
|
237
|
-
type: "http_request",
|
|
238
|
-
name: "Notify",
|
|
239
|
-
config: {
|
|
240
|
-
method: "POST",
|
|
241
|
-
url: "https://example.com/hook",
|
|
242
|
-
headers: {
|
|
243
|
-
authorization: "Bearer token",
|
|
244
|
-
},
|
|
245
|
-
body: {
|
|
246
|
-
ok: true,
|
|
247
|
-
},
|
|
248
|
-
contentType: "application/json",
|
|
249
|
-
timeoutMs: 5000,
|
|
250
|
-
variables: {
|
|
251
|
-
env: "prod",
|
|
252
|
-
},
|
|
253
|
-
connectionId: "connection-1",
|
|
254
|
-
},
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
it("maps record action field mappings to the backend fieldMapping contract", async () => {
|
|
259
|
-
const configPath = createApiConfig();
|
|
260
|
-
const output = createStdoutBuffer();
|
|
261
|
-
const fetchMock = createFetchMock();
|
|
262
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
263
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
264
|
-
|
|
265
|
-
await program.parseAsync([
|
|
266
|
-
"node",
|
|
267
|
-
"zinkee",
|
|
268
|
-
"--json",
|
|
269
|
-
"automations",
|
|
270
|
-
"actions",
|
|
271
|
-
"add",
|
|
272
|
-
"automation-1",
|
|
273
|
-
"--type",
|
|
274
|
-
"create_record",
|
|
275
|
-
"--name",
|
|
276
|
-
"Create follow-up",
|
|
277
|
-
"--target-schema",
|
|
278
|
-
"schema-1",
|
|
279
|
-
"--map",
|
|
280
|
-
"status=pending",
|
|
281
|
-
"--map-json",
|
|
282
|
-
'meta={"source":"automation"}',
|
|
283
|
-
]);
|
|
284
|
-
|
|
285
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
286
|
-
type: "create_record",
|
|
287
|
-
name: "Create follow-up",
|
|
288
|
-
config: {
|
|
289
|
-
targetSchemaId: "schema-1",
|
|
290
|
-
fieldMapping: {
|
|
291
|
-
status: {
|
|
292
|
-
rawValue: "pending",
|
|
293
|
-
source: null,
|
|
294
|
-
},
|
|
295
|
-
meta: {
|
|
296
|
-
rawValue: {
|
|
297
|
-
source: "automation",
|
|
298
|
-
},
|
|
299
|
-
source: null,
|
|
300
|
-
},
|
|
301
|
-
},
|
|
302
|
-
},
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it("maps plugin arguments to the backend pluginArgs contract", async () => {
|
|
307
|
-
const configPath = createApiConfig();
|
|
308
|
-
const output = createStdoutBuffer();
|
|
309
|
-
const fetchMock = createFetchMock();
|
|
310
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
311
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
312
|
-
|
|
313
|
-
await program.parseAsync([
|
|
314
|
-
"node",
|
|
315
|
-
"zinkee",
|
|
316
|
-
"--json",
|
|
317
|
-
"automations",
|
|
318
|
-
"actions",
|
|
319
|
-
"add",
|
|
320
|
-
"automation-1",
|
|
321
|
-
"--type",
|
|
322
|
-
"execute_plugin",
|
|
323
|
-
"--name",
|
|
324
|
-
"Run plugin",
|
|
325
|
-
"--plugin",
|
|
326
|
-
"demo-plugin",
|
|
327
|
-
"--arg",
|
|
328
|
-
"mode=safe",
|
|
329
|
-
"--arg-json",
|
|
330
|
-
'payload={"dryRun":true}',
|
|
331
|
-
]);
|
|
332
|
-
|
|
333
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
334
|
-
type: "execute_plugin",
|
|
335
|
-
name: "Run plugin",
|
|
336
|
-
config: {
|
|
337
|
-
pluginId: "demo-plugin",
|
|
338
|
-
pluginArgs: {
|
|
339
|
-
mode: "safe",
|
|
340
|
-
payload: {
|
|
341
|
-
dryRun: true,
|
|
342
|
-
},
|
|
343
|
-
},
|
|
344
|
-
},
|
|
345
|
-
});
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
it("preserves source-based field mappings for record actions", async () => {
|
|
349
|
-
const configPath = createApiConfig();
|
|
350
|
-
const output = createStdoutBuffer();
|
|
351
|
-
const fetchMock = createFetchMock();
|
|
352
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
353
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
354
|
-
|
|
355
|
-
await program.parseAsync([
|
|
356
|
-
"node",
|
|
357
|
-
"zinkee",
|
|
358
|
-
"--json",
|
|
359
|
-
"automations",
|
|
360
|
-
"actions",
|
|
361
|
-
"add",
|
|
362
|
-
"automation-1",
|
|
363
|
-
"--type",
|
|
364
|
-
"create_record",
|
|
365
|
-
"--name",
|
|
366
|
-
"Create from trigger",
|
|
367
|
-
"--target-schema",
|
|
368
|
-
"schema-1",
|
|
369
|
-
"--map-json",
|
|
370
|
-
'owner={"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}',
|
|
371
|
-
]);
|
|
372
|
-
|
|
373
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
374
|
-
type: "create_record",
|
|
375
|
-
name: "Create from trigger",
|
|
376
|
-
config: {
|
|
377
|
-
targetSchemaId: "schema-1",
|
|
378
|
-
fieldMapping: {
|
|
379
|
-
owner: {
|
|
380
|
-
rawValue: null,
|
|
381
|
-
source: {
|
|
382
|
-
type: "TRIGGER",
|
|
383
|
-
id: "f1e1d000-0000-0000-0000-000000000005",
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
},
|
|
387
|
-
},
|
|
388
|
-
});
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
it("maps search-record conditions to structured values", async () => {
|
|
392
|
-
const configPath = createApiConfig();
|
|
393
|
-
const output = createStdoutBuffer();
|
|
394
|
-
const fetchMock = createFetchMock();
|
|
395
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
396
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
397
|
-
|
|
398
|
-
await program.parseAsync([
|
|
399
|
-
"node",
|
|
400
|
-
"zinkee",
|
|
401
|
-
"--json",
|
|
402
|
-
"automations",
|
|
403
|
-
"actions",
|
|
404
|
-
"add",
|
|
405
|
-
"automation-1",
|
|
406
|
-
"--type",
|
|
407
|
-
"search_records",
|
|
408
|
-
"--name",
|
|
409
|
-
"Search related",
|
|
410
|
-
"--target-schema",
|
|
411
|
-
"schema-1",
|
|
412
|
-
"--condition-json",
|
|
413
|
-
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
|
|
414
|
-
]);
|
|
415
|
-
|
|
416
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
417
|
-
type: "search_records",
|
|
418
|
-
name: "Search related",
|
|
419
|
-
config: {
|
|
420
|
-
targetSchemaId: "schema-1",
|
|
421
|
-
conditions: [
|
|
422
|
-
{
|
|
423
|
-
field: "status",
|
|
424
|
-
comparator: "eq",
|
|
425
|
-
values: [
|
|
426
|
-
{
|
|
427
|
-
rawValue: null,
|
|
428
|
-
source: {
|
|
429
|
-
type: "TRIGGER",
|
|
430
|
-
id: "f1e1d000-0000-0000-0000-000000000005",
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
],
|
|
434
|
-
},
|
|
435
|
-
],
|
|
436
|
-
},
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it("omits conditions when update_record uses the trigger record", async () => {
|
|
441
|
-
const configPath = createApiConfig();
|
|
442
|
-
const output = createStdoutBuffer();
|
|
443
|
-
const fetchMock = createFetchMock();
|
|
444
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
445
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
446
|
-
|
|
447
|
-
await program.parseAsync([
|
|
448
|
-
"node",
|
|
449
|
-
"zinkee",
|
|
450
|
-
"--json",
|
|
451
|
-
"automations",
|
|
452
|
-
"actions",
|
|
453
|
-
"add",
|
|
454
|
-
"automation-1",
|
|
455
|
-
"--type",
|
|
456
|
-
"update_record",
|
|
457
|
-
"--name",
|
|
458
|
-
"Update trigger record",
|
|
459
|
-
"--target-schema",
|
|
460
|
-
"schema-1",
|
|
461
|
-
"--map",
|
|
462
|
-
"status=done",
|
|
463
|
-
"--condition",
|
|
464
|
-
"status:eq:open",
|
|
465
|
-
"--use-trigger-record",
|
|
466
|
-
]);
|
|
467
|
-
|
|
468
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
469
|
-
type: "update_record",
|
|
470
|
-
name: "Update trigger record",
|
|
471
|
-
config: {
|
|
472
|
-
targetSchemaId: "schema-1",
|
|
473
|
-
fieldMapping: {
|
|
474
|
-
status: {
|
|
475
|
-
rawValue: "done",
|
|
476
|
-
source: null,
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
useTriggerRecord: true,
|
|
480
|
-
},
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it("builds send_message actions with wrapped fieldMapping (recipients/subject/body)", async () => {
|
|
485
|
-
const configPath = createApiConfig();
|
|
486
|
-
const output = createStdoutBuffer();
|
|
487
|
-
const fetchMock = createFetchMock();
|
|
488
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
489
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
490
|
-
|
|
491
|
-
await program.parseAsync([
|
|
492
|
-
"node",
|
|
493
|
-
"zinkee",
|
|
494
|
-
"--json",
|
|
495
|
-
"automations",
|
|
496
|
-
"actions",
|
|
497
|
-
"add",
|
|
498
|
-
"automation-1",
|
|
499
|
-
"--type",
|
|
500
|
-
"send_message",
|
|
501
|
-
"--name",
|
|
502
|
-
"Notify customer",
|
|
503
|
-
"--to",
|
|
504
|
-
"customer@example.com",
|
|
505
|
-
"--subject",
|
|
506
|
-
"Your order status has changed",
|
|
507
|
-
"--message",
|
|
508
|
-
"Hello, your order is now processed.",
|
|
509
|
-
]);
|
|
510
|
-
|
|
511
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
512
|
-
type: "send_message",
|
|
513
|
-
name: "Notify customer",
|
|
514
|
-
config: {
|
|
515
|
-
fieldMapping: {
|
|
516
|
-
recipients: {
|
|
517
|
-
rawValue: "customer@example.com",
|
|
518
|
-
source: null,
|
|
519
|
-
},
|
|
520
|
-
subject: {
|
|
521
|
-
rawValue: "Your order status has changed",
|
|
522
|
-
source: null,
|
|
523
|
-
},
|
|
524
|
-
body: {
|
|
525
|
-
rawValue: "Hello, your order is now processed.",
|
|
526
|
-
source: null,
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
it("wraps send_message --map values in {rawValue, source} like other field mappings", async () => {
|
|
534
|
-
const configPath = createApiConfig();
|
|
535
|
-
const output = createStdoutBuffer();
|
|
536
|
-
const fetchMock = createFetchMock();
|
|
537
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
538
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
539
|
-
|
|
540
|
-
await program.parseAsync([
|
|
541
|
-
"node",
|
|
542
|
-
"zinkee",
|
|
543
|
-
"--json",
|
|
544
|
-
"automations",
|
|
545
|
-
"actions",
|
|
546
|
-
"add",
|
|
547
|
-
"automation-1",
|
|
548
|
-
"--type",
|
|
549
|
-
"send_message",
|
|
550
|
-
"--name",
|
|
551
|
-
"Notify customer",
|
|
552
|
-
"--to",
|
|
553
|
-
"customer@example.com",
|
|
554
|
-
"--map-json",
|
|
555
|
-
'metadata={"orderId":"ORD-1"}',
|
|
556
|
-
]);
|
|
557
|
-
|
|
558
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
559
|
-
type: "send_message",
|
|
560
|
-
name: "Notify customer",
|
|
561
|
-
config: {
|
|
562
|
-
fieldMapping: {
|
|
563
|
-
recipients: {
|
|
564
|
-
rawValue: "customer@example.com",
|
|
565
|
-
source: null,
|
|
566
|
-
},
|
|
567
|
-
metadata: {
|
|
568
|
-
rawValue: { orderId: "ORD-1" },
|
|
569
|
-
source: null,
|
|
570
|
-
},
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
});
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
it("builds generate_document actions with template, schema, and file-field ids", async () => {
|
|
577
|
-
const configPath = createApiConfig();
|
|
578
|
-
const output = createStdoutBuffer();
|
|
579
|
-
const fetchMock = createFetchMock();
|
|
580
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
581
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
582
|
-
|
|
583
|
-
await program.parseAsync([
|
|
584
|
-
"node",
|
|
585
|
-
"zinkee",
|
|
586
|
-
"--json",
|
|
587
|
-
"automations",
|
|
588
|
-
"actions",
|
|
589
|
-
"add",
|
|
590
|
-
"automation-1",
|
|
591
|
-
"--type",
|
|
592
|
-
"generate_document",
|
|
593
|
-
"--name",
|
|
594
|
-
"Generate invoice PDF",
|
|
595
|
-
"--document-template",
|
|
596
|
-
"6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
597
|
-
"--target-schema",
|
|
598
|
-
"68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
599
|
-
"--target-file-field",
|
|
600
|
-
"c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
601
|
-
]);
|
|
602
|
-
|
|
603
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
604
|
-
type: "generate_document",
|
|
605
|
-
name: "Generate invoice PDF",
|
|
606
|
-
config: {
|
|
607
|
-
documentTemplateId: "6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
608
|
-
targetSchemaId: "68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
609
|
-
targetFileFieldId: "c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
610
|
-
},
|
|
611
|
-
});
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it("accepts the field:comparator literal form for no-value comparators", async () => {
|
|
615
|
-
const configPath = createApiConfig();
|
|
616
|
-
const output = createStdoutBuffer();
|
|
617
|
-
const fetchMock = createFetchMock();
|
|
618
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ ok: true }));
|
|
619
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
620
|
-
|
|
621
|
-
await program.parseAsync([
|
|
622
|
-
"node",
|
|
623
|
-
"zinkee",
|
|
624
|
-
"--json",
|
|
625
|
-
"automations",
|
|
626
|
-
"trigger",
|
|
627
|
-
"set",
|
|
628
|
-
"automation-1",
|
|
629
|
-
"--trigger-type",
|
|
630
|
-
"record_changed",
|
|
631
|
-
"--trigger-schema",
|
|
632
|
-
"schema-1",
|
|
633
|
-
"--trigger-field",
|
|
634
|
-
"field-1",
|
|
635
|
-
"--condition",
|
|
636
|
-
"field-1:chg",
|
|
637
|
-
]);
|
|
638
|
-
|
|
639
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
640
|
-
type: "record_changed",
|
|
641
|
-
config: {
|
|
642
|
-
schemaId: "schema-1",
|
|
643
|
-
fieldIds: ["field-1"],
|
|
644
|
-
conditions: [
|
|
645
|
-
{
|
|
646
|
-
field: "field-1",
|
|
647
|
-
comparator: "chg",
|
|
648
|
-
values: [],
|
|
649
|
-
},
|
|
650
|
-
],
|
|
651
|
-
},
|
|
652
|
-
});
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
it("accepts empty condition-json values for no-value comparators", async () => {
|
|
656
|
-
const configPath = createApiConfig();
|
|
657
|
-
const output = createStdoutBuffer();
|
|
658
|
-
const fetchMock = createFetchMock();
|
|
659
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ ok: true }));
|
|
660
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
661
|
-
|
|
662
|
-
await program.parseAsync([
|
|
663
|
-
"node",
|
|
664
|
-
"zinkee",
|
|
665
|
-
"--json",
|
|
666
|
-
"automations",
|
|
667
|
-
"trigger",
|
|
668
|
-
"set",
|
|
669
|
-
"automation-1",
|
|
670
|
-
"--trigger-type",
|
|
671
|
-
"record_changed",
|
|
672
|
-
"--trigger-schema",
|
|
673
|
-
"schema-1",
|
|
674
|
-
"--trigger-field",
|
|
675
|
-
"field-1",
|
|
676
|
-
"--condition-json",
|
|
677
|
-
'{"field":"field-1","comparator":"ie","values":[]}',
|
|
678
|
-
]);
|
|
679
|
-
|
|
680
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
681
|
-
type: "record_changed",
|
|
682
|
-
config: {
|
|
683
|
-
schemaId: "schema-1",
|
|
684
|
-
fieldIds: ["field-1"],
|
|
685
|
-
conditions: [
|
|
686
|
-
{
|
|
687
|
-
field: "field-1",
|
|
688
|
-
comparator: "ie",
|
|
689
|
-
values: [],
|
|
690
|
-
},
|
|
691
|
-
],
|
|
692
|
-
},
|
|
693
|
-
});
|
|
694
|
-
});
|
|
695
|
-
|
|
696
|
-
it("rejects the field:comparator literal form for comparators that need a value", async () => {
|
|
697
|
-
const configPath = createApiConfig();
|
|
698
|
-
const output = createStdoutBuffer();
|
|
699
|
-
const fetchMock = createFetchMock();
|
|
700
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
701
|
-
|
|
702
|
-
await expect(
|
|
703
|
-
program.parseAsync([
|
|
704
|
-
"node",
|
|
705
|
-
"zinkee",
|
|
706
|
-
"--json",
|
|
707
|
-
"automations",
|
|
708
|
-
"trigger",
|
|
709
|
-
"set",
|
|
710
|
-
"automation-1",
|
|
711
|
-
"--trigger-type",
|
|
712
|
-
"record_changed",
|
|
713
|
-
"--trigger-schema",
|
|
714
|
-
"schema-1",
|
|
715
|
-
"--trigger-field",
|
|
716
|
-
"field-1",
|
|
717
|
-
"--condition",
|
|
718
|
-
"field-1:eq",
|
|
719
|
-
]),
|
|
720
|
-
).rejects.toThrowError(/field:comparator for comparators without value/);
|
|
721
|
-
|
|
722
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
it("rejects empty condition-json values for comparators that need a value", async () => {
|
|
726
|
-
const configPath = createApiConfig();
|
|
727
|
-
const output = createStdoutBuffer();
|
|
728
|
-
const fetchMock = createFetchMock();
|
|
729
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
730
|
-
|
|
731
|
-
await expect(
|
|
732
|
-
program.parseAsync([
|
|
733
|
-
"node",
|
|
734
|
-
"zinkee",
|
|
735
|
-
"--json",
|
|
736
|
-
"automations",
|
|
737
|
-
"trigger",
|
|
738
|
-
"set",
|
|
739
|
-
"automation-1",
|
|
740
|
-
"--trigger-type",
|
|
741
|
-
"record_changed",
|
|
742
|
-
"--trigger-schema",
|
|
743
|
-
"schema-1",
|
|
744
|
-
"--trigger-field",
|
|
745
|
-
"field-1",
|
|
746
|
-
"--condition-json",
|
|
747
|
-
'{"field":"field-1","comparator":"eq","values":[]}',
|
|
748
|
-
]),
|
|
749
|
-
).rejects.toThrowError(/non-empty "values" array/);
|
|
750
|
-
|
|
751
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
it("rejects source-based trigger conditions", async () => {
|
|
755
|
-
const configPath = createApiConfig();
|
|
756
|
-
const output = createStdoutBuffer();
|
|
757
|
-
const fetchMock = createFetchMock();
|
|
758
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
759
|
-
|
|
760
|
-
await expect(
|
|
761
|
-
program.parseAsync([
|
|
762
|
-
"node",
|
|
763
|
-
"zinkee",
|
|
764
|
-
"--json",
|
|
765
|
-
"automations",
|
|
766
|
-
"trigger",
|
|
767
|
-
"set",
|
|
768
|
-
"automation-1",
|
|
769
|
-
"--trigger-type",
|
|
770
|
-
"record_changed",
|
|
771
|
-
"--trigger-schema",
|
|
772
|
-
"schema-1",
|
|
773
|
-
"--trigger-field",
|
|
774
|
-
"field-1",
|
|
775
|
-
"--condition-json",
|
|
776
|
-
'{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
|
|
777
|
-
]),
|
|
778
|
-
).rejects.toThrowError('Expected trigger condition 1 value 1 to use only literal "rawValue" values.');
|
|
779
|
-
|
|
780
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
781
|
-
});
|
|
782
|
-
|
|
783
|
-
it("sets flow payloads from repeated entry and transition flags", async () => {
|
|
784
|
-
const configPath = createApiConfig();
|
|
785
|
-
const output = createStdoutBuffer();
|
|
786
|
-
const fetchMock = createFetchMock();
|
|
787
|
-
fetchMock.mockResolvedValueOnce(
|
|
788
|
-
jsonResponse({
|
|
789
|
-
id: "automation-1",
|
|
790
|
-
}),
|
|
791
|
-
);
|
|
792
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
793
|
-
|
|
794
|
-
await program.parseAsync([
|
|
795
|
-
"node",
|
|
796
|
-
"zinkee",
|
|
797
|
-
"--json",
|
|
798
|
-
"automations",
|
|
799
|
-
"flow",
|
|
800
|
-
"set",
|
|
801
|
-
"automation-1",
|
|
802
|
-
"--entry",
|
|
803
|
-
"action-1",
|
|
804
|
-
"--entry",
|
|
805
|
-
"action-2",
|
|
806
|
-
"--transition",
|
|
807
|
-
"action-1:action-2",
|
|
808
|
-
"--transition",
|
|
809
|
-
"action-2:action-3",
|
|
810
|
-
]);
|
|
811
|
-
|
|
812
|
-
const init = fetchMock.mock.calls[0]?.[1];
|
|
813
|
-
|
|
814
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
815
|
-
"https://api.zinkee.test/api/v2/automations/automation-1/flow",
|
|
816
|
-
);
|
|
817
|
-
expect(init?.method).toBe("PUT");
|
|
818
|
-
expect(JSON.parse(String(init?.body))).toEqual({
|
|
819
|
-
entryActionIds: ["action-1", "action-2"],
|
|
820
|
-
transitions: [
|
|
821
|
-
{ fromActionId: "action-1", toActionId: "action-2" },
|
|
822
|
-
{ fromActionId: "action-2", toActionId: "action-3" },
|
|
823
|
-
],
|
|
824
|
-
});
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
it("blocks write operations in read-only mode", async () => {
|
|
828
|
-
const configPath = createApiConfig();
|
|
829
|
-
const fetchMock = createFetchMock();
|
|
830
|
-
const program = buildAutomationsProgram({ configPath });
|
|
831
|
-
|
|
832
|
-
await expect(
|
|
833
|
-
program.parseAsync([
|
|
834
|
-
"node",
|
|
835
|
-
"zinkee",
|
|
836
|
-
"--read-only",
|
|
837
|
-
"automations",
|
|
838
|
-
"trigger",
|
|
839
|
-
"set",
|
|
840
|
-
"automation-1",
|
|
841
|
-
"--trigger-type",
|
|
842
|
-
"scheduled",
|
|
843
|
-
"--cron",
|
|
844
|
-
"0 * * * *",
|
|
845
|
-
]),
|
|
846
|
-
).rejects.toThrowError(
|
|
847
|
-
'Command "automations trigger set" is not allowed in read-only mode.',
|
|
848
|
-
);
|
|
849
|
-
|
|
850
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
it("updates automations using the backend patch contract with set and unset", async () => {
|
|
854
|
-
const configPath = createApiConfig();
|
|
855
|
-
const output = createStdoutBuffer();
|
|
856
|
-
const fetchMock = createFetchMock();
|
|
857
|
-
fetchMock.mockResolvedValueOnce(
|
|
858
|
-
jsonResponse({
|
|
859
|
-
id: "automation-1",
|
|
860
|
-
name: "Lead welcome",
|
|
861
|
-
}),
|
|
862
|
-
);
|
|
863
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
864
|
-
|
|
865
|
-
await program.parseAsync([
|
|
866
|
-
"node",
|
|
867
|
-
"zinkee",
|
|
868
|
-
"--json",
|
|
869
|
-
"automations",
|
|
870
|
-
"update",
|
|
871
|
-
"automation-1",
|
|
872
|
-
"--name",
|
|
873
|
-
"Lead welcome",
|
|
874
|
-
"--raw",
|
|
875
|
-
'{"description":"Updated automation","unset":["archivedAt"]}',
|
|
876
|
-
]);
|
|
877
|
-
|
|
878
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
879
|
-
"https://api.zinkee.test/api/v2/automations/automation-1",
|
|
880
|
-
);
|
|
881
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
882
|
-
set: {
|
|
883
|
-
description: "Updated automation",
|
|
884
|
-
name: "Lead welcome",
|
|
885
|
-
},
|
|
886
|
-
unset: ["archivedAt"],
|
|
887
|
-
});
|
|
888
|
-
expect(JSON.parse(output.read())).toEqual({
|
|
889
|
-
data: {
|
|
890
|
-
id: "automation-1",
|
|
891
|
-
name: "Lead welcome",
|
|
892
|
-
},
|
|
893
|
-
meta: {
|
|
894
|
-
command: "automations update",
|
|
895
|
-
configPath,
|
|
896
|
-
},
|
|
897
|
-
});
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
it("updates connections deterministically for nested config paths", async () => {
|
|
901
|
-
const configPath = createApiConfig();
|
|
902
|
-
const output = createStdoutBuffer();
|
|
903
|
-
const fetchMock = createFetchMock();
|
|
904
|
-
fetchMock.mockResolvedValueOnce(
|
|
905
|
-
jsonResponse({
|
|
906
|
-
id: "connection-1",
|
|
907
|
-
name: "Primary",
|
|
908
|
-
}),
|
|
909
|
-
);
|
|
910
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
911
|
-
|
|
912
|
-
await program.parseAsync([
|
|
913
|
-
"node",
|
|
914
|
-
"zinkee",
|
|
915
|
-
"--json",
|
|
916
|
-
"automations",
|
|
917
|
-
"connections",
|
|
918
|
-
"update",
|
|
919
|
-
"connection-1",
|
|
920
|
-
"--set",
|
|
921
|
-
"config.baseUrl=https://api.example.com",
|
|
922
|
-
"--set",
|
|
923
|
-
"config.auth.token=secret",
|
|
924
|
-
"--set",
|
|
925
|
-
"name=Primary",
|
|
926
|
-
"--unset",
|
|
927
|
-
"config.legacy",
|
|
928
|
-
"--raw",
|
|
929
|
-
'{"set":{"config":{"region":"eu"}}, "unset":["config.baseUrl","oldField"]}',
|
|
930
|
-
]);
|
|
931
|
-
|
|
932
|
-
expect(fetchMock.mock.calls[0]?.[0]).toBe(
|
|
933
|
-
"https://api.zinkee.test/api/v2/automation-connections/connection-1",
|
|
934
|
-
);
|
|
935
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
936
|
-
set: {
|
|
937
|
-
config: {
|
|
938
|
-
region: "eu",
|
|
939
|
-
baseUrl: "https://api.example.com",
|
|
940
|
-
auth: {
|
|
941
|
-
token: "secret",
|
|
942
|
-
},
|
|
943
|
-
},
|
|
944
|
-
name: "Primary",
|
|
945
|
-
},
|
|
946
|
-
unset: ["oldField", "config.legacy"],
|
|
947
|
-
});
|
|
948
|
-
});
|
|
949
|
-
|
|
950
|
-
it("uses backend webhook endpoint field names", async () => {
|
|
951
|
-
const configPath = createApiConfig();
|
|
952
|
-
const output = createStdoutBuffer();
|
|
953
|
-
const fetchMock = createFetchMock();
|
|
954
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "webhook-1" }));
|
|
955
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
956
|
-
|
|
957
|
-
await program.parseAsync([
|
|
958
|
-
"node",
|
|
959
|
-
"zinkee",
|
|
960
|
-
"--json",
|
|
961
|
-
"automations",
|
|
962
|
-
"webhook",
|
|
963
|
-
"set",
|
|
964
|
-
"automation-1",
|
|
965
|
-
"--active",
|
|
966
|
-
"--idempotency-key-path",
|
|
967
|
-
"$.id",
|
|
968
|
-
"--event-type-path",
|
|
969
|
-
"$.type",
|
|
970
|
-
"--allowed-event-type",
|
|
971
|
-
"frontend.demo.created",
|
|
972
|
-
"--field-mapping",
|
|
973
|
-
'{"variable":"external_id","jsonPath":"$.data.id"}',
|
|
974
|
-
]);
|
|
975
|
-
|
|
976
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
977
|
-
active: true,
|
|
978
|
-
idempotencyKeyJsonPath: "$.id",
|
|
979
|
-
eventTypeJsonPath: "$.type",
|
|
980
|
-
allowedEventTypes: ["frontend.demo.created"],
|
|
981
|
-
fieldMappings: [
|
|
982
|
-
{
|
|
983
|
-
variable: "external_id",
|
|
984
|
-
jsonPath: "$.data.id",
|
|
985
|
-
},
|
|
986
|
-
],
|
|
987
|
-
});
|
|
988
|
-
});
|
|
989
|
-
|
|
990
|
-
it("uses secret instead of secrets when creating connections", async () => {
|
|
991
|
-
const configPath = createApiConfig();
|
|
992
|
-
const output = createStdoutBuffer();
|
|
993
|
-
const fetchMock = createFetchMock();
|
|
994
|
-
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "connection-1" }));
|
|
995
|
-
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
996
|
-
|
|
997
|
-
await program.parseAsync([
|
|
998
|
-
"node",
|
|
999
|
-
"zinkee",
|
|
1000
|
-
"--json",
|
|
1001
|
-
"automations",
|
|
1002
|
-
"connections",
|
|
1003
|
-
"create",
|
|
1004
|
-
"--name",
|
|
1005
|
-
"ERP API Key",
|
|
1006
|
-
"--type",
|
|
1007
|
-
"api_key",
|
|
1008
|
-
"--scope",
|
|
1009
|
-
"workspace",
|
|
1010
|
-
"--config",
|
|
1011
|
-
'{"headerName":"Authorization"}',
|
|
1012
|
-
"--secret",
|
|
1013
|
-
"apiKey=super-secret",
|
|
1014
|
-
"--active",
|
|
1015
|
-
]);
|
|
1016
|
-
|
|
1017
|
-
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
1018
|
-
name: "ERP API Key",
|
|
1019
|
-
type: "api_key",
|
|
1020
|
-
scope: "workspace",
|
|
1021
|
-
config: {
|
|
1022
|
-
headerName: "Authorization",
|
|
1023
|
-
},
|
|
1024
|
-
secret: {
|
|
1025
|
-
apiKey: "super-secret",
|
|
1026
|
-
},
|
|
1027
|
-
active: true,
|
|
1028
|
-
});
|
|
1029
|
-
});
|
|
1030
|
-
});
|