zinkee 0.1.22 → 0.1.23
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/.claude/settings.local.json +2 -1
- package/dist/index.js +94 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/automations.test.ts +130 -0
- package/src/commands/automations.ts +50 -2
- package/src/utils/examples.test.ts +46 -0
- package/src/utils/examples.ts +67 -0
package/package.json
CHANGED
|
@@ -449,6 +449,136 @@ describe("automations commands", () => {
|
|
|
449
449
|
});
|
|
450
450
|
});
|
|
451
451
|
|
|
452
|
+
it("builds send_message actions with wrapped fieldMapping (recipients/subject/body)", async () => {
|
|
453
|
+
const configPath = createApiConfig();
|
|
454
|
+
const output = createStdoutBuffer();
|
|
455
|
+
const fetchMock = createFetchMock();
|
|
456
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
457
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
458
|
+
|
|
459
|
+
await program.parseAsync([
|
|
460
|
+
"node",
|
|
461
|
+
"zinkee",
|
|
462
|
+
"--json",
|
|
463
|
+
"automations",
|
|
464
|
+
"actions",
|
|
465
|
+
"add",
|
|
466
|
+
"automation-1",
|
|
467
|
+
"--type",
|
|
468
|
+
"send_message",
|
|
469
|
+
"--name",
|
|
470
|
+
"Notify customer",
|
|
471
|
+
"--to",
|
|
472
|
+
"customer@example.com",
|
|
473
|
+
"--subject",
|
|
474
|
+
"Your order status has changed",
|
|
475
|
+
"--message",
|
|
476
|
+
"Hello, your order is now processed.",
|
|
477
|
+
]);
|
|
478
|
+
|
|
479
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
480
|
+
type: "send_message",
|
|
481
|
+
name: "Notify customer",
|
|
482
|
+
config: {
|
|
483
|
+
fieldMapping: {
|
|
484
|
+
recipients: {
|
|
485
|
+
rawValue: "customer@example.com",
|
|
486
|
+
source: null,
|
|
487
|
+
},
|
|
488
|
+
subject: {
|
|
489
|
+
rawValue: "Your order status has changed",
|
|
490
|
+
source: null,
|
|
491
|
+
},
|
|
492
|
+
body: {
|
|
493
|
+
rawValue: "Hello, your order is now processed.",
|
|
494
|
+
source: null,
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("wraps send_message --map values in {rawValue, source} like other field mappings", async () => {
|
|
502
|
+
const configPath = createApiConfig();
|
|
503
|
+
const output = createStdoutBuffer();
|
|
504
|
+
const fetchMock = createFetchMock();
|
|
505
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
506
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
507
|
+
|
|
508
|
+
await program.parseAsync([
|
|
509
|
+
"node",
|
|
510
|
+
"zinkee",
|
|
511
|
+
"--json",
|
|
512
|
+
"automations",
|
|
513
|
+
"actions",
|
|
514
|
+
"add",
|
|
515
|
+
"automation-1",
|
|
516
|
+
"--type",
|
|
517
|
+
"send_message",
|
|
518
|
+
"--name",
|
|
519
|
+
"Notify customer",
|
|
520
|
+
"--to",
|
|
521
|
+
"customer@example.com",
|
|
522
|
+
"--map-json",
|
|
523
|
+
'metadata={"orderId":"ORD-1"}',
|
|
524
|
+
]);
|
|
525
|
+
|
|
526
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
527
|
+
type: "send_message",
|
|
528
|
+
name: "Notify customer",
|
|
529
|
+
config: {
|
|
530
|
+
fieldMapping: {
|
|
531
|
+
recipients: {
|
|
532
|
+
rawValue: "customer@example.com",
|
|
533
|
+
source: null,
|
|
534
|
+
},
|
|
535
|
+
metadata: {
|
|
536
|
+
rawValue: { orderId: "ORD-1" },
|
|
537
|
+
source: null,
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it("builds generate_document actions with template, schema, and file-field ids", async () => {
|
|
545
|
+
const configPath = createApiConfig();
|
|
546
|
+
const output = createStdoutBuffer();
|
|
547
|
+
const fetchMock = createFetchMock();
|
|
548
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ id: "action-1" }));
|
|
549
|
+
const program = buildAutomationsProgram({ configPath, stdout: output.stdout });
|
|
550
|
+
|
|
551
|
+
await program.parseAsync([
|
|
552
|
+
"node",
|
|
553
|
+
"zinkee",
|
|
554
|
+
"--json",
|
|
555
|
+
"automations",
|
|
556
|
+
"actions",
|
|
557
|
+
"add",
|
|
558
|
+
"automation-1",
|
|
559
|
+
"--type",
|
|
560
|
+
"generate_document",
|
|
561
|
+
"--name",
|
|
562
|
+
"Generate invoice PDF",
|
|
563
|
+
"--document-template",
|
|
564
|
+
"6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
565
|
+
"--target-schema",
|
|
566
|
+
"68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
567
|
+
"--target-file-field",
|
|
568
|
+
"c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
569
|
+
]);
|
|
570
|
+
|
|
571
|
+
expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
|
|
572
|
+
type: "generate_document",
|
|
573
|
+
name: "Generate invoice PDF",
|
|
574
|
+
config: {
|
|
575
|
+
documentTemplateId: "6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
576
|
+
targetSchemaId: "68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
577
|
+
targetFileFieldId: "c8edbde8-a361-4e9f-a39a-2e88971f21a7",
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
|
|
452
582
|
it("rejects source-based trigger conditions", async () => {
|
|
453
583
|
const configPath = createApiConfig();
|
|
454
584
|
const output = createStdoutBuffer();
|
|
@@ -107,6 +107,11 @@ interface ActionMutationCommandOptions {
|
|
|
107
107
|
plugin?: string;
|
|
108
108
|
arg?: string[];
|
|
109
109
|
argJson?: string[];
|
|
110
|
+
to?: string;
|
|
111
|
+
subject?: string;
|
|
112
|
+
message?: string;
|
|
113
|
+
documentTemplate?: string;
|
|
114
|
+
targetFileField?: string;
|
|
110
115
|
raw?: string;
|
|
111
116
|
rawFile?: string;
|
|
112
117
|
}
|
|
@@ -1016,6 +1021,18 @@ function buildActionInput(
|
|
|
1016
1021
|
config.targetSchemaId = options.targetSchema;
|
|
1017
1022
|
}
|
|
1018
1023
|
|
|
1024
|
+
if (options.to !== undefined) {
|
|
1025
|
+
mappings.recipients = options.to;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (options.subject !== undefined) {
|
|
1029
|
+
mappings.subject = options.subject;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
if (options.message !== undefined) {
|
|
1033
|
+
mappings.body = options.message;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1019
1036
|
if (!isEmptyObject(mappings)) {
|
|
1020
1037
|
config.fieldMapping = mappings;
|
|
1021
1038
|
}
|
|
@@ -1073,6 +1090,14 @@ function buildActionInput(
|
|
|
1073
1090
|
config.pluginArgs = args;
|
|
1074
1091
|
}
|
|
1075
1092
|
|
|
1093
|
+
if (options.documentTemplate !== undefined) {
|
|
1094
|
+
config.documentTemplateId = options.documentTemplate;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (options.targetFileField !== undefined) {
|
|
1098
|
+
config.targetFileFieldId = options.targetFileField;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1076
1101
|
if (!isEmptyObject(config)) {
|
|
1077
1102
|
if (isJsonObject(config.fieldMapping)) {
|
|
1078
1103
|
config.fieldMapping = normalizeAutomationFieldMapping(
|
|
@@ -1319,9 +1344,26 @@ function addTriggerPayloadOptions(command: Command): Command {
|
|
|
1319
1344
|
.option("--raw-file <path>", "path to raw trigger JSON payload");
|
|
1320
1345
|
}
|
|
1321
1346
|
|
|
1347
|
+
const ACTION_TYPES_HELP = `
|
|
1348
|
+
Action types and their flags:
|
|
1349
|
+
create_record --target-schema, --map, --map-json
|
|
1350
|
+
update_record --target-schema, --use-trigger-record, --map,
|
|
1351
|
+
--condition, --condition-json
|
|
1352
|
+
search_records --target-schema, --condition, --condition-json
|
|
1353
|
+
send_message --to, --subject, --message
|
|
1354
|
+
(--map / --map-json also accepted for extra keys)
|
|
1355
|
+
http_request --method, --url, --header, --body, --content-type,
|
|
1356
|
+
--timeout-ms, --var, --auth-mode, --connection
|
|
1357
|
+
execute_plugin --plugin, --arg, --arg-json
|
|
1358
|
+
generate_document --document-template, --target-schema, --target-file-field
|
|
1359
|
+
`;
|
|
1360
|
+
|
|
1322
1361
|
function addActionPayloadOptions(command: Command): Command {
|
|
1323
1362
|
return command
|
|
1324
|
-
.option(
|
|
1363
|
+
.option(
|
|
1364
|
+
"--type <type>",
|
|
1365
|
+
"action type (create_record|update_record|search_records|send_message|http_request|execute_plugin|generate_document)",
|
|
1366
|
+
)
|
|
1325
1367
|
.option("--name <name>", "action name")
|
|
1326
1368
|
.option("--target-schema <uuid>", "target schema id")
|
|
1327
1369
|
.option("--map <field=value>", "field mapping", collectValues, [])
|
|
@@ -1346,8 +1388,14 @@ function addActionPayloadOptions(command: Command): Command {
|
|
|
1346
1388
|
.option("--plugin <id>", "plugin id for execute_plugin")
|
|
1347
1389
|
.option("--arg <key=value>", "plugin argument", collectValues, [])
|
|
1348
1390
|
.option("--arg-json <key=json>", "plugin argument as JSON", collectValues, [])
|
|
1391
|
+
.option("--to <recipient>", "send_message recipient (maps to fieldMapping.recipients)")
|
|
1392
|
+
.option("--subject <text>", "send_message subject")
|
|
1393
|
+
.option("--message <text>", "send_message body text (maps to fieldMapping.body)")
|
|
1394
|
+
.option("--document-template <uuid>", "generate_document template id")
|
|
1395
|
+
.option("--target-file-field <uuid>", "generate_document target FileField id")
|
|
1349
1396
|
.option("--raw <json>", "raw action JSON payload")
|
|
1350
|
-
.option("--raw-file <path>", "path to raw action JSON payload")
|
|
1397
|
+
.option("--raw-file <path>", "path to raw action JSON payload")
|
|
1398
|
+
.addHelpText("after", ACTION_TYPES_HELP);
|
|
1351
1399
|
}
|
|
1352
1400
|
|
|
1353
1401
|
export function registerAutomationsCommands(
|
|
@@ -102,6 +102,52 @@ describe("getCommandExamples", () => {
|
|
|
102
102
|
expect(webhookExamples[0]?.raw).toContain("idempotencyKeyJsonPath");
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
+
it("covers every action type documented by the backend", () => {
|
|
106
|
+
const actionExamples = getCommandExamples("automations actions add");
|
|
107
|
+
const backendActionTypes = [
|
|
108
|
+
"create_record",
|
|
109
|
+
"update_record",
|
|
110
|
+
"search_records",
|
|
111
|
+
"send_message",
|
|
112
|
+
"http_request",
|
|
113
|
+
"execute_plugin",
|
|
114
|
+
"generate_document",
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
for (const type of backendActionTypes) {
|
|
118
|
+
expect(
|
|
119
|
+
actionExamples.some((example) => example.command.includes(`--type ${type}`)),
|
|
120
|
+
).toBe(true);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("documents send_message with wrapped {rawValue, source} values keyed by recipients/subject/body", () => {
|
|
125
|
+
const actionExamples = getCommandExamples("automations actions add");
|
|
126
|
+
const sendMessage = actionExamples.find((example) =>
|
|
127
|
+
example.command.includes("--type send_message"),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(sendMessage?.command).toContain("--to customer@example.com");
|
|
131
|
+
expect(sendMessage?.command).toContain("--subject");
|
|
132
|
+
expect(sendMessage?.command).toContain("--message");
|
|
133
|
+
expect(sendMessage?.raw).toContain(`"recipients"`);
|
|
134
|
+
expect(sendMessage?.raw).toContain(`"rawValue"`);
|
|
135
|
+
expect(sendMessage?.raw).not.toContain(`"channel"`);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("documents generate_document with template, schema, and file-field ids", () => {
|
|
139
|
+
const actionExamples = getCommandExamples("automations actions add");
|
|
140
|
+
const generateDocument = actionExamples.find((example) =>
|
|
141
|
+
example.command.includes("--type generate_document"),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(generateDocument?.command).toContain("--document-template");
|
|
145
|
+
expect(generateDocument?.command).toContain("--target-schema");
|
|
146
|
+
expect(generateDocument?.command).toContain("--target-file-field");
|
|
147
|
+
expect(generateDocument?.raw).toContain("documentTemplateId");
|
|
148
|
+
expect(generateDocument?.raw).toContain("targetFileFieldId");
|
|
149
|
+
});
|
|
150
|
+
|
|
105
151
|
it("covers every records query operator documented by the backend", () => {
|
|
106
152
|
const examples = getCommandExamples("records query");
|
|
107
153
|
const backendOperators = [
|
package/src/utils/examples.ts
CHANGED
|
@@ -3515,6 +3515,47 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
3515
3515
|
"steps": ["one", "two"]
|
|
3516
3516
|
}
|
|
3517
3517
|
}
|
|
3518
|
+
}`,
|
|
3519
|
+
},
|
|
3520
|
+
{
|
|
3521
|
+
description:
|
|
3522
|
+
"Add a send-message action. --to maps to fieldMapping.recipients and --message maps to fieldMapping.body. Values are wrapped in {rawValue, source} like every other automation field mapping; use --map / --map-json for extra keys.",
|
|
3523
|
+
command:
|
|
3524
|
+
`zinkee --json automations actions add automation-1 --type send_message --name "Notify customer" --to customer@example.com --subject "Your order status has changed" --message "Hello, your order is now processed."`,
|
|
3525
|
+
raw: `{
|
|
3526
|
+
"type": "send_message",
|
|
3527
|
+
"name": "Notify customer",
|
|
3528
|
+
"config": {
|
|
3529
|
+
"fieldMapping": {
|
|
3530
|
+
"recipients": {
|
|
3531
|
+
"rawValue": "customer@example.com",
|
|
3532
|
+
"source": null
|
|
3533
|
+
},
|
|
3534
|
+
"subject": {
|
|
3535
|
+
"rawValue": "Your order status has changed",
|
|
3536
|
+
"source": null
|
|
3537
|
+
},
|
|
3538
|
+
"body": {
|
|
3539
|
+
"rawValue": "Hello, your order is now processed.",
|
|
3540
|
+
"source": null
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
}
|
|
3544
|
+
}`,
|
|
3545
|
+
},
|
|
3546
|
+
{
|
|
3547
|
+
description:
|
|
3548
|
+
"Add a generate-document action. The renderer reads the template, writes the file, and stores its id in the target FileField of the target schema.",
|
|
3549
|
+
command:
|
|
3550
|
+
'zinkee --json automations actions add automation-1 --type generate_document --name "Generate invoice PDF" --document-template 6c08673e-f8a6-44d2-8c91-d09eca911f8f --target-schema 68d2c7be-4392-42de-b5ae-625ad79cfc3c --target-file-field c8edbde8-a361-4e9f-a39a-2e88971f21a7',
|
|
3551
|
+
raw: `{
|
|
3552
|
+
"type": "generate_document",
|
|
3553
|
+
"name": "Generate invoice PDF",
|
|
3554
|
+
"config": {
|
|
3555
|
+
"documentTemplateId": "6c08673e-f8a6-44d2-8c91-d09eca911f8f",
|
|
3556
|
+
"targetSchemaId": "68d2c7be-4392-42de-b5ae-625ad79cfc3c",
|
|
3557
|
+
"targetFileFieldId": "c8edbde8-a361-4e9f-a39a-2e88971f21a7"
|
|
3558
|
+
}
|
|
3518
3559
|
}`,
|
|
3519
3560
|
},
|
|
3520
3561
|
]),
|
|
@@ -3569,6 +3610,32 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
3569
3610
|
"authMode": "STORED",
|
|
3570
3611
|
"connectionId": "connection-1"
|
|
3571
3612
|
}
|
|
3613
|
+
}`,
|
|
3614
|
+
},
|
|
3615
|
+
{
|
|
3616
|
+
description:
|
|
3617
|
+
"Replace a send-message action. --to maps to fieldMapping.recipients and --message maps to fieldMapping.body; values are wrapped in {rawValue, source}.",
|
|
3618
|
+
command:
|
|
3619
|
+
`zinkee --json automations actions update automation-1 action-1 --type send_message --name "Notify customer" --to customer@example.com --subject "Your order status has changed" --message "Hello, your order is now processed."`,
|
|
3620
|
+
raw: `{
|
|
3621
|
+
"type": "send_message",
|
|
3622
|
+
"name": "Notify customer",
|
|
3623
|
+
"config": {
|
|
3624
|
+
"fieldMapping": {
|
|
3625
|
+
"recipients": {
|
|
3626
|
+
"rawValue": "customer@example.com",
|
|
3627
|
+
"source": null
|
|
3628
|
+
},
|
|
3629
|
+
"subject": {
|
|
3630
|
+
"rawValue": "Your order status has changed",
|
|
3631
|
+
"source": null
|
|
3632
|
+
},
|
|
3633
|
+
"body": {
|
|
3634
|
+
"rawValue": "Hello, your order is now processed.",
|
|
3635
|
+
"source": null
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3572
3639
|
}`,
|
|
3573
3640
|
},
|
|
3574
3641
|
]),
|