zinkee 0.1.22 → 0.1.24

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.24",
4
4
  "description": "CLI for Zinkee API v2",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,110 @@
1
+ import { ApiClient } from "../client.js";
2
+ import type { NormalizedUuidOrSlug } from "../parsers/selectors.js";
3
+
4
+ export interface ApiDocumentTemplateAsset {
5
+ [key: string]: unknown;
6
+ id?: string;
7
+ name?: string;
8
+ link?: string;
9
+ }
10
+
11
+ export interface ApiDocumentTemplate {
12
+ [key: string]: unknown;
13
+ id?: string;
14
+ name?: string;
15
+ assets?: ApiDocumentTemplateAsset[];
16
+ createdAt?: string;
17
+ updatedAt?: string;
18
+ createdBy?: string;
19
+ updatedBy?: string;
20
+ }
21
+
22
+ export interface CreateDocumentTemplateInput {
23
+ [key: string]: unknown;
24
+ name: string;
25
+ assets?: ApiDocumentTemplateAsset[];
26
+ }
27
+
28
+ export interface UpdateDocumentTemplateInput {
29
+ [key: string]: unknown;
30
+ name?: string;
31
+ }
32
+
33
+ function jsonOptions(body: unknown, method: string): RequestInit {
34
+ return {
35
+ method,
36
+ headers: {
37
+ "content-type": "application/json",
38
+ },
39
+ body: JSON.stringify(body),
40
+ };
41
+ }
42
+
43
+ function selectorValue(selector: NormalizedUuidOrSlug): string {
44
+ return encodeURIComponent(selector.value);
45
+ }
46
+
47
+ function segment(value: string): string {
48
+ return encodeURIComponent(value);
49
+ }
50
+
51
+ export class DocumentTemplatesApi {
52
+ constructor(private readonly client: ApiClient) {}
53
+
54
+ listTemplates(): Promise<ApiDocumentTemplate[]> {
55
+ return this.client.getJson<ApiDocumentTemplate[]>("/api/v2/document-templates");
56
+ }
57
+
58
+ createTemplate(input: CreateDocumentTemplateInput): Promise<ApiDocumentTemplate> {
59
+ return this.client.requestJson<ApiDocumentTemplate>(
60
+ "/api/v2/document-templates",
61
+ jsonOptions(input, "POST"),
62
+ );
63
+ }
64
+
65
+ updateTemplate(
66
+ template: NormalizedUuidOrSlug,
67
+ input: UpdateDocumentTemplateInput,
68
+ ): Promise<ApiDocumentTemplate> {
69
+ return this.client.requestJson<ApiDocumentTemplate>(
70
+ `/api/v2/document-templates/${selectorValue(template)}`,
71
+ jsonOptions(input, "PATCH"),
72
+ );
73
+ }
74
+
75
+ deleteTemplate(template: NormalizedUuidOrSlug): Promise<Record<string, unknown>> {
76
+ return this.client.requestJson<Record<string, unknown>>(
77
+ `/api/v2/document-templates/${selectorValue(template)}`,
78
+ { method: "DELETE" },
79
+ );
80
+ }
81
+
82
+ addAsset(
83
+ template: NormalizedUuidOrSlug,
84
+ input: FormData,
85
+ ): Promise<ApiDocumentTemplate> {
86
+ return this.client.requestJson<ApiDocumentTemplate>(
87
+ `/api/v2/document-templates/${selectorValue(template)}/assets`,
88
+ { method: "POST", body: input },
89
+ );
90
+ }
91
+
92
+ deleteAsset(
93
+ template: NormalizedUuidOrSlug,
94
+ assetId: string,
95
+ ): Promise<Record<string, unknown>> {
96
+ return this.client.requestJson<Record<string, unknown>>(
97
+ `/api/v2/document-templates/${selectorValue(template)}/assets/${segment(assetId)}`,
98
+ { method: "DELETE" },
99
+ );
100
+ }
101
+
102
+ downloadAsset(
103
+ template: NormalizedUuidOrSlug,
104
+ assetId: string,
105
+ ): Promise<ArrayBuffer> {
106
+ return this.client.requestBinary(
107
+ `/api/v2/document-templates/${selectorValue(template)}/assets/${segment(assetId)}/download`,
108
+ );
109
+ }
110
+ }
@@ -441,6 +441,62 @@ export const commandRegistry = {
441
441
  supportsRaw: false,
442
442
  output: writeOutput,
443
443
  },
444
+ "document-templates list": {
445
+ command: "document-templates list",
446
+ accessLevel: "read",
447
+ supportsExample: true,
448
+ supportsRaw: false,
449
+ output: readOutput,
450
+ },
451
+ "document-templates get": {
452
+ command: "document-templates get",
453
+ accessLevel: "read",
454
+ supportsExample: true,
455
+ supportsRaw: false,
456
+ output: readOutput,
457
+ },
458
+ "document-templates create": {
459
+ command: "document-templates create",
460
+ accessLevel: "write",
461
+ supportsExample: true,
462
+ supportsRaw: true,
463
+ output: writeOutput,
464
+ },
465
+ "document-templates update": {
466
+ command: "document-templates update",
467
+ accessLevel: "write",
468
+ supportsExample: true,
469
+ supportsRaw: false,
470
+ output: writeOutput,
471
+ },
472
+ "document-templates delete": {
473
+ command: "document-templates delete",
474
+ accessLevel: "destructive",
475
+ supportsExample: true,
476
+ supportsRaw: false,
477
+ output: writeOutput,
478
+ },
479
+ "document-templates assets add": {
480
+ command: "document-templates assets add",
481
+ accessLevel: "write",
482
+ supportsExample: true,
483
+ supportsRaw: false,
484
+ output: writeOutput,
485
+ },
486
+ "document-templates assets delete": {
487
+ command: "document-templates assets delete",
488
+ accessLevel: "destructive",
489
+ supportsExample: true,
490
+ supportsRaw: false,
491
+ output: writeOutput,
492
+ },
493
+ "document-templates assets download": {
494
+ command: "document-templates assets download",
495
+ accessLevel: "read",
496
+ supportsExample: true,
497
+ supportsRaw: false,
498
+ output: "binary",
499
+ },
444
500
  "files upload": {
445
501
  command: "files upload",
446
502
  accessLevel: "write",
@@ -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(