zinkee 0.1.21 → 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 +3 -1
- package/dist/index.js +100 -7
- 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/commands/displays.ts +5 -2
- package/src/utils/examples.test.ts +46 -0
- package/src/utils/examples.ts +70 -3
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(
|
package/src/commands/displays.ts
CHANGED
|
@@ -17,6 +17,9 @@ import type { RuntimeOverrides } from "../types.js";
|
|
|
17
17
|
import { CliError } from "../utils/errors.js";
|
|
18
18
|
import { buildJsonSuccess, printTable } from "../utils/output.js";
|
|
19
19
|
|
|
20
|
+
const DATE_DYNAMIC_OPTIONS =
|
|
21
|
+
"today, thisWeek, thisMonth, thisQuarter, thisSemester, thisYear, next7Days, next30Days, next6Months, next12Months, last12Months";
|
|
22
|
+
|
|
20
23
|
interface CommandIo {
|
|
21
24
|
write(chunk: string): unknown;
|
|
22
25
|
}
|
|
@@ -674,7 +677,7 @@ export function registerDisplaysCommands(
|
|
|
674
677
|
.option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static")
|
|
675
678
|
.option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p: string[] = []) => [...p, v], [])
|
|
676
679
|
.option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, [])
|
|
677
|
-
.option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>")
|
|
680
|
+
.option("--date-mode <mode>").option("--date-dynamic-option <option>", `dynamic date period (${DATE_DYNAMIC_OPTIONS})`).option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>")
|
|
678
681
|
.option("--raw <json>").option("--raw-file <path>")
|
|
679
682
|
.action(async function action(this: Command, display: NormalizedUuidOrSlug, commandOptions: VariableMutationOptions) {
|
|
680
683
|
const { api, json, readOnly } = createDisplaysApi(this, filePath); ensureWritable("displays variables create", readOnly, "write");
|
|
@@ -686,7 +689,7 @@ export function registerDisplaysCommands(
|
|
|
686
689
|
.option("--type <type>").option("--name <name>").option("--allow-multiple").option("--static")
|
|
687
690
|
.option("--default <value>").option("--default-value <value>", "repeatable default value", (v, p: string[] = []) => [...p, v], [])
|
|
688
691
|
.option("--source-schema <id>").option("--source-field <id>").option("--filter <expr>", "repeatable filter", collectFilters, [])
|
|
689
|
-
.option("--date-mode <mode>").option("--date-dynamic-option <option>").option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>")
|
|
692
|
+
.option("--date-mode <mode>").option("--date-dynamic-option <option>", `dynamic date period (${DATE_DYNAMIC_OPTIONS})`).option("--date-specific <iso>").option("--date-range-start <iso>").option("--date-range-end <iso>")
|
|
690
693
|
.option("--raw <json>").option("--raw-file <path>")
|
|
691
694
|
.action(async function action(this: Command, display: NormalizedUuidOrSlug, variableId: string, commandOptions: VariableMutationOptions) {
|
|
692
695
|
const { api, json, readOnly } = createDisplaysApi(this, filePath); ensureWritable("displays variables update", readOnly, "write");
|
|
@@ -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
|
@@ -1298,13 +1298,13 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
1298
1298
|
}`,
|
|
1299
1299
|
},
|
|
1300
1300
|
{
|
|
1301
|
-
description: "Change a DATE variable to use
|
|
1301
|
+
description: "Change a DATE variable to use last12Months.",
|
|
1302
1302
|
command:
|
|
1303
|
-
'zinkee --json displays variables update 0f6f062f-1478-4df2-96a1-c495d037d260 bbbbbbbb-2222-4222-a222-222222222222 --date-mode dynamic --date-dynamic-option
|
|
1303
|
+
'zinkee --json displays variables update 0f6f062f-1478-4df2-96a1-c495d037d260 bbbbbbbb-2222-4222-a222-222222222222 --date-mode dynamic --date-dynamic-option last12Months',
|
|
1304
1304
|
raw: `{
|
|
1305
1305
|
"date": {
|
|
1306
1306
|
"mode": "dynamic",
|
|
1307
|
-
"dynamicOption": "
|
|
1307
|
+
"dynamicOption": "last12Months"
|
|
1308
1308
|
}
|
|
1309
1309
|
}`,
|
|
1310
1310
|
},
|
|
@@ -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
|
]),
|