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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zinkee",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "CLI for Zinkee API v2",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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("--type <type>", "action type")
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 = [
@@ -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
  ]),