zinkee 0.1.33 → 0.1.35
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 +9 -1
- package/dist/index.js +162 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/records.ts +17 -7
- package/src/commands/records.test.ts +101 -9
- package/src/commands/records.ts +97 -14
- package/src/utils/examples.test.ts +4 -2
- package/src/utils/examples.ts +84 -21
package/package.json
CHANGED
package/src/api/records.ts
CHANGED
|
@@ -33,8 +33,18 @@ export interface RecordQueryInput {
|
|
|
33
33
|
offset?: number;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export
|
|
37
|
-
|
|
36
|
+
export type CreateRecordValues = Record<string, unknown>;
|
|
37
|
+
|
|
38
|
+
export interface CreateRecordError {
|
|
39
|
+
index: number;
|
|
40
|
+
reason: string;
|
|
41
|
+
message: string;
|
|
42
|
+
action: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CreateRecordsResult {
|
|
46
|
+
created: string[];
|
|
47
|
+
errors: CreateRecordError[];
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
export interface UpdateRecordInput {
|
|
@@ -102,13 +112,13 @@ export class RecordsApi {
|
|
|
102
112
|
);
|
|
103
113
|
}
|
|
104
114
|
|
|
105
|
-
|
|
115
|
+
createRecords(
|
|
106
116
|
schema: NormalizedUuidOrSlug,
|
|
107
|
-
|
|
108
|
-
): Promise<
|
|
109
|
-
return this.client.requestJson<
|
|
117
|
+
records: CreateRecordValues[],
|
|
118
|
+
): Promise<CreateRecordsResult> {
|
|
119
|
+
return this.client.requestJson<CreateRecordsResult>(
|
|
110
120
|
`/api/v2/schemas/${selectorValue(schema)}/records`,
|
|
111
|
-
jsonOptions(
|
|
121
|
+
jsonOptions({ records }, "POST"),
|
|
112
122
|
);
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -367,15 +367,15 @@ describe("records commands", () => {
|
|
|
367
367
|
);
|
|
368
368
|
});
|
|
369
369
|
|
|
370
|
-
it("creates
|
|
370
|
+
it("creates a single record by merging raw JSON with set and set-json assignments", async () => {
|
|
371
371
|
const configPath = createApiConfig();
|
|
372
372
|
const output = createStdoutBuffer();
|
|
373
373
|
const fetchMock = createFetchMock();
|
|
374
374
|
mockSchemaLookup(fetchMock);
|
|
375
375
|
fetchMock.mockResolvedValueOnce(
|
|
376
376
|
jsonResponse({
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
created: ["record-1"],
|
|
378
|
+
errors: [],
|
|
379
379
|
}),
|
|
380
380
|
);
|
|
381
381
|
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
@@ -402,16 +402,108 @@ describe("records commands", () => {
|
|
|
402
402
|
);
|
|
403
403
|
expect(init?.method).toBe("POST");
|
|
404
404
|
expect(JSON.parse(String(init?.body))).toEqual({
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
405
|
+
records: [
|
|
406
|
+
{
|
|
407
|
+
name: "Ana",
|
|
408
|
+
status: "active",
|
|
409
|
+
profile: {
|
|
410
|
+
tier: "gold",
|
|
411
|
+
},
|
|
410
412
|
},
|
|
411
|
-
|
|
413
|
+
],
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("creates multiple records from a --records-file", async () => {
|
|
418
|
+
const configPath = createApiConfig();
|
|
419
|
+
const recordsFile = createTempConfig(
|
|
420
|
+
JSON.stringify([
|
|
421
|
+
{ "contact-name": "Ada", status: 2 },
|
|
422
|
+
{ "contact-name": "Grace", tags: ["a", "b"] },
|
|
423
|
+
]),
|
|
424
|
+
);
|
|
425
|
+
const output = createStdoutBuffer();
|
|
426
|
+
const fetchMock = createFetchMock();
|
|
427
|
+
mockSchemaLookup(fetchMock);
|
|
428
|
+
fetchMock.mockResolvedValueOnce(
|
|
429
|
+
jsonResponse({
|
|
430
|
+
created: ["record-1", "record-2"],
|
|
431
|
+
errors: [],
|
|
432
|
+
}),
|
|
433
|
+
);
|
|
434
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
435
|
+
|
|
436
|
+
await program.parseAsync([
|
|
437
|
+
"node",
|
|
438
|
+
"zinkee",
|
|
439
|
+
"--json",
|
|
440
|
+
"records",
|
|
441
|
+
"create",
|
|
442
|
+
"contacts",
|
|
443
|
+
"--records-file",
|
|
444
|
+
recordsFile,
|
|
445
|
+
]);
|
|
446
|
+
|
|
447
|
+
const init = fetchMock.mock.calls[1]?.[1];
|
|
448
|
+
|
|
449
|
+
expect(fetchMock.mock.calls[1]?.[0]).toBe(
|
|
450
|
+
"https://api.zinkee.test/api/v2/schemas/schema-1/records",
|
|
451
|
+
);
|
|
452
|
+
expect(init?.method).toBe("POST");
|
|
453
|
+
expect(JSON.parse(String(init?.body))).toEqual({
|
|
454
|
+
records: [
|
|
455
|
+
{ "contact-name": "Ada", status: 2 },
|
|
456
|
+
{ "contact-name": "Grace", tags: ["a", "b"] },
|
|
457
|
+
],
|
|
412
458
|
});
|
|
413
459
|
});
|
|
414
460
|
|
|
461
|
+
it("rejects combining --records-file with single-record flags", async () => {
|
|
462
|
+
const configPath = createApiConfig();
|
|
463
|
+
const recordsFile = createTempConfig(JSON.stringify([{ "contact-name": "Ada" }]));
|
|
464
|
+
const output = createStdoutBuffer();
|
|
465
|
+
const fetchMock = createFetchMock();
|
|
466
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
467
|
+
|
|
468
|
+
await expect(
|
|
469
|
+
program.parseAsync([
|
|
470
|
+
"node",
|
|
471
|
+
"zinkee",
|
|
472
|
+
"records",
|
|
473
|
+
"create",
|
|
474
|
+
"contacts",
|
|
475
|
+
"--records-file",
|
|
476
|
+
recordsFile,
|
|
477
|
+
"--set",
|
|
478
|
+
"contact-name=Grace",
|
|
479
|
+
]),
|
|
480
|
+
).rejects.toThrowError(/not both/);
|
|
481
|
+
|
|
482
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it("rejects a --records-file that is not a JSON array", async () => {
|
|
486
|
+
const configPath = createApiConfig();
|
|
487
|
+
const recordsFile = createTempConfig(JSON.stringify({ "contact-name": "Ada" }));
|
|
488
|
+
const output = createStdoutBuffer();
|
|
489
|
+
const fetchMock = createFetchMock();
|
|
490
|
+
const program = buildProgram({ configPath, stdout: output.stdout });
|
|
491
|
+
|
|
492
|
+
await expect(
|
|
493
|
+
program.parseAsync([
|
|
494
|
+
"node",
|
|
495
|
+
"zinkee",
|
|
496
|
+
"records",
|
|
497
|
+
"create",
|
|
498
|
+
"contacts",
|
|
499
|
+
"--records-file",
|
|
500
|
+
recordsFile,
|
|
501
|
+
]),
|
|
502
|
+
).rejects.toThrowError(/JSON array/);
|
|
503
|
+
|
|
504
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
505
|
+
});
|
|
506
|
+
|
|
415
507
|
it("wraps raw-file fields into the set payload", async () => {
|
|
416
508
|
const configPath = createApiConfig();
|
|
417
509
|
const output = createStdoutBuffer();
|
package/src/commands/records.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
type ApiRecord,
|
|
8
|
-
type
|
|
8
|
+
type CreateRecordValues,
|
|
9
9
|
type RecordQueryExpression,
|
|
10
10
|
type RecordQueryInput,
|
|
11
11
|
type RecordQueryResult,
|
|
@@ -66,6 +66,7 @@ interface RecordsCreateCommandOptions {
|
|
|
66
66
|
setJson?: JsonKeyValueAssignment[];
|
|
67
67
|
raw?: string;
|
|
68
68
|
rawFile?: string;
|
|
69
|
+
recordsFile?: string;
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
interface RecordsUpdateCommandOptions extends RecordsCreateCommandOptions {
|
|
@@ -473,9 +474,72 @@ function buildRecordQueryInput(
|
|
|
473
474
|
return input;
|
|
474
475
|
}
|
|
475
476
|
|
|
476
|
-
function
|
|
477
|
-
|
|
478
|
-
|
|
477
|
+
function readRecordsFromFile(path: string): CreateRecordValues[] {
|
|
478
|
+
let contents: string;
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
contents = readFileSync(path, "utf8");
|
|
482
|
+
} catch (error) {
|
|
483
|
+
const message = error instanceof Error ? error.message : "Unable to read file.";
|
|
484
|
+
throw new CliError(
|
|
485
|
+
"invalid_cli_usage",
|
|
486
|
+
`Failed to read records from ${path}: ${message}`,
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
let parsed: unknown;
|
|
491
|
+
try {
|
|
492
|
+
parsed = JSON.parse(contents);
|
|
493
|
+
} catch (error) {
|
|
494
|
+
const message = error instanceof Error ? error.message : "Invalid JSON.";
|
|
495
|
+
throw new CliError(
|
|
496
|
+
"invalid_cli_usage",
|
|
497
|
+
`Failed to parse JSON from --records-file ${path}: ${message}`,
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (!Array.isArray(parsed)) {
|
|
502
|
+
throw new CliError(
|
|
503
|
+
"invalid_cli_usage",
|
|
504
|
+
`Expected --records-file ${path} to contain a JSON array of record objects.`,
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (parsed.length === 0) {
|
|
509
|
+
throw new CliError(
|
|
510
|
+
"invalid_cli_usage",
|
|
511
|
+
`--records-file ${path} must contain at least one record.`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return parsed.map((record, index) => {
|
|
516
|
+
if (!isJsonObject(record)) {
|
|
517
|
+
throw new CliError(
|
|
518
|
+
"invalid_cli_usage",
|
|
519
|
+
`Record at index ${index} in --records-file ${path} must be a JSON object.`,
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
return record;
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function buildCreateRecords(options: RecordsCreateCommandOptions): CreateRecordValues[] {
|
|
527
|
+
const hasSingleRecordFlags =
|
|
528
|
+
(options.set?.length ?? 0) > 0 ||
|
|
529
|
+
(options.setJson?.length ?? 0) > 0 ||
|
|
530
|
+
options.raw !== undefined ||
|
|
531
|
+
options.rawFile !== undefined;
|
|
532
|
+
|
|
533
|
+
if (options.recordsFile !== undefined) {
|
|
534
|
+
if (hasSingleRecordFlags) {
|
|
535
|
+
throw new CliError(
|
|
536
|
+
"invalid_cli_usage",
|
|
537
|
+
'Provide either "--records-file" or the single-record flags (--set/--set-json/--raw/--raw-file), not both.',
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
return readRecordsFromFile(options.recordsFile);
|
|
541
|
+
}
|
|
542
|
+
|
|
479
543
|
const values = {
|
|
480
544
|
...readRawObject(options.raw, options.rawFile),
|
|
481
545
|
...buildAssignmentObject(options.set, options.setJson),
|
|
@@ -484,11 +548,11 @@ function buildRecordCreateInput(
|
|
|
484
548
|
if (isEmptyObject(values)) {
|
|
485
549
|
throw new CliError(
|
|
486
550
|
"invalid_cli_usage",
|
|
487
|
-
'Provide record fields via "--set", "--set-json", "--raw", or "--
|
|
551
|
+
'Provide record fields via "--set", "--set-json", "--raw", "--raw-file", or load multiple records with "--records-file".',
|
|
488
552
|
);
|
|
489
553
|
}
|
|
490
554
|
|
|
491
|
-
return
|
|
555
|
+
return [values];
|
|
492
556
|
}
|
|
493
557
|
|
|
494
558
|
function buildRecordUpdateInput(
|
|
@@ -684,12 +748,21 @@ export function registerRecordsCommands(
|
|
|
684
748
|
|
|
685
749
|
records
|
|
686
750
|
.command("create")
|
|
687
|
-
.description("Create
|
|
751
|
+
.description("Create one or more records")
|
|
688
752
|
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
689
|
-
.option(
|
|
753
|
+
.option(
|
|
754
|
+
"--set <field=value>",
|
|
755
|
+
"set a string field (reference fields: a record id, 'recordId::label', or an exact display value)",
|
|
756
|
+
collectKeyValueAssignments,
|
|
757
|
+
[],
|
|
758
|
+
)
|
|
690
759
|
.option("--set-json <field=json>", "set a structured field", collectJsonAssignments, [])
|
|
691
760
|
.option("--raw <json>", "raw JSON object payload")
|
|
692
761
|
.option("--raw-file <path>", "path to a raw JSON object payload")
|
|
762
|
+
.option(
|
|
763
|
+
"--records-file <path>",
|
|
764
|
+
"path to a JSON array of record objects (mutually exclusive with --set/--set-json/--raw/--raw-file)",
|
|
765
|
+
)
|
|
693
766
|
.action(async function action(
|
|
694
767
|
this: Command,
|
|
695
768
|
schema: NormalizedUuidOrSlug,
|
|
@@ -697,19 +770,24 @@ export function registerRecordsCommands(
|
|
|
697
770
|
) {
|
|
698
771
|
const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
|
|
699
772
|
ensureWritable("records create", readOnly, "write");
|
|
773
|
+
|
|
774
|
+
const records = buildCreateRecords(commandOptions);
|
|
700
775
|
const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
|
|
701
776
|
|
|
702
|
-
const data = await api.
|
|
703
|
-
resolvedSchema,
|
|
704
|
-
buildRecordCreateInput(commandOptions),
|
|
705
|
-
);
|
|
777
|
+
const data = await api.createRecords(resolvedSchema, records);
|
|
706
778
|
|
|
707
779
|
if (json) {
|
|
708
780
|
emitJson(stdout, "records create", data, filePath);
|
|
709
781
|
return;
|
|
710
782
|
}
|
|
711
783
|
|
|
712
|
-
writeLine(stdout, `Created record "${
|
|
784
|
+
writeLine(stdout, `Created ${data.created.length} record(s) in schema "${schema.value}".`);
|
|
785
|
+
if (data.errors.length > 0) {
|
|
786
|
+
writeLine(stdout, `${data.errors.length} record(s) rejected:`);
|
|
787
|
+
for (const error of data.errors) {
|
|
788
|
+
writeLine(stdout, ` - index ${error.index}: ${error.message}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
713
791
|
});
|
|
714
792
|
|
|
715
793
|
records
|
|
@@ -717,7 +795,12 @@ export function registerRecordsCommands(
|
|
|
717
795
|
.description("Update a record")
|
|
718
796
|
.argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
|
|
719
797
|
.argument("<record>", "record uuid or slug", parseUuidOrSlugSelector)
|
|
720
|
-
.option(
|
|
798
|
+
.option(
|
|
799
|
+
"--set <field=value>",
|
|
800
|
+
"set a string field (reference fields: a record id, 'recordId::label', or an exact display value)",
|
|
801
|
+
collectKeyValueAssignments,
|
|
802
|
+
[],
|
|
803
|
+
)
|
|
721
804
|
.option("--set-json <field=json>", "set a structured field", collectJsonAssignments, [])
|
|
722
805
|
.option("--unset <field>", "unset a field", collectStringValues, [])
|
|
723
806
|
.option("--raw <json>", "raw JSON object of fields to set")
|
|
@@ -177,13 +177,15 @@ describe("getCommandExamples", () => {
|
|
|
177
177
|
}
|
|
178
178
|
});
|
|
179
179
|
|
|
180
|
-
it("offers
|
|
180
|
+
it("offers minimal, multi-type, and reference records create examples", () => {
|
|
181
181
|
const examples = getCommandExamples("records create");
|
|
182
182
|
|
|
183
|
-
expect(examples).toHaveLength(
|
|
183
|
+
expect(examples).toHaveLength(3);
|
|
184
184
|
expect(examples[0]?.raw).toContain('"contact-name"');
|
|
185
185
|
expect(examples[1]?.raw).toContain('"customer-ref"');
|
|
186
186
|
expect(examples[1]?.raw).toContain('"owner"');
|
|
187
|
+
expect(examples[2]?.raw).toContain('"customer-ref"');
|
|
188
|
+
expect(examples[2]?.response).toContain("data_field_reference_not_found");
|
|
187
189
|
});
|
|
188
190
|
|
|
189
191
|
it("provides full/minimal/folder variants for schemas create", () => {
|
package/src/utils/examples.ts
CHANGED
|
@@ -677,38 +677,83 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
677
677
|
]),
|
|
678
678
|
"records create": buildCommandExamples([
|
|
679
679
|
{
|
|
680
|
-
description:
|
|
680
|
+
description:
|
|
681
|
+
"Create a single record with --set/--set-json (the single-record flags build one record, sent as a one-element 'records' array).",
|
|
681
682
|
command:
|
|
682
683
|
`zinkee --json records create contacts --set contact-name="Ada Lovelace" --set contact-email=ada@example.com`,
|
|
683
684
|
raw: `{
|
|
684
|
-
"
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
685
|
+
"records": [
|
|
686
|
+
{
|
|
687
|
+
"contact-name": "Ada Lovelace",
|
|
688
|
+
"contact-email": "ada@example.com"
|
|
689
|
+
}
|
|
690
|
+
]
|
|
688
691
|
}`,
|
|
689
692
|
response: `{
|
|
690
|
-
"
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
693
|
+
"created": [
|
|
694
|
+
"550e8400-e29b-41d4-a716-446655440000"
|
|
695
|
+
],
|
|
696
|
+
"errors": []
|
|
697
|
+
}`,
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
description:
|
|
701
|
+
"Create many records from a JSON array file (one object per record, slug -> value). Best-effort: valid records are created and invalid ones are reported per index in 'errors'. ReferenceField values accept the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or an exact display value; the server stores the canonical 'recordId::displayValue'.",
|
|
702
|
+
command: `zinkee --json records create contacts --records-file ./records.json`,
|
|
703
|
+
raw: `{
|
|
704
|
+
"records": [
|
|
705
|
+
{
|
|
706
|
+
"contact-name": "Ada Lovelace",
|
|
707
|
+
"contact-email": "ada@example.com"
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"contact-name": "Grace Hopper",
|
|
711
|
+
"contact-email": "grace@example.com",
|
|
712
|
+
"status": 2,
|
|
713
|
+
"amount": 1999.99,
|
|
714
|
+
"due-date": "2026-06-01T09:00:00Z",
|
|
715
|
+
"customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621",
|
|
716
|
+
"owner": "6c8a3e0d-4e5f-4a1b-9c2d-3e4f5a6b7c8d"
|
|
717
|
+
}
|
|
718
|
+
]
|
|
719
|
+
}`,
|
|
720
|
+
response: `{
|
|
721
|
+
"created": [
|
|
722
|
+
"550e8400-e29b-41d4-a716-446655440000"
|
|
723
|
+
],
|
|
724
|
+
"errors": [
|
|
725
|
+
{
|
|
726
|
+
"index": 1,
|
|
727
|
+
"reason": "record_field_not_found",
|
|
728
|
+
"message": "Field 'contact-emial' was not found in schema '2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e'.",
|
|
729
|
+
"action": "Use a valid field slug from the schema and retry."
|
|
730
|
+
}
|
|
731
|
+
]
|
|
695
732
|
}`,
|
|
696
733
|
},
|
|
697
734
|
{
|
|
698
735
|
description:
|
|
699
|
-
"
|
|
736
|
+
"Set a ReferenceField by the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or by an exact display value. The server resolves and stores the canonical 'recordId::displayValue'. Unknown references are rejected per index with reason 'data_field_reference_not_found'.",
|
|
700
737
|
command:
|
|
701
|
-
`zinkee --json records create
|
|
738
|
+
`zinkee --json records create deals --set deal-name="Acme renewal" --set customer-ref=00000000-0000-0000-0000-000000000000`,
|
|
702
739
|
raw: `{
|
|
703
|
-
"
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
740
|
+
"records": [
|
|
741
|
+
{
|
|
742
|
+
"deal-name": "Acme renewal",
|
|
743
|
+
"customer-ref": "00000000-0000-0000-0000-000000000000"
|
|
744
|
+
}
|
|
745
|
+
]
|
|
746
|
+
}`,
|
|
747
|
+
response: `{
|
|
748
|
+
"created": [],
|
|
749
|
+
"errors": [
|
|
750
|
+
{
|
|
751
|
+
"index": 0,
|
|
752
|
+
"reason": "data_field_reference_not_found",
|
|
753
|
+
"message": "Referenced record was not found in the target schema.",
|
|
754
|
+
"action": "Use an existing record id or an existing display value for this reference field."
|
|
755
|
+
}
|
|
756
|
+
]
|
|
712
757
|
}`,
|
|
713
758
|
},
|
|
714
759
|
]),
|
|
@@ -753,6 +798,24 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
|
|
|
753
798
|
'zinkee --json records update contacts 550e8400-e29b-41d4-a716-446655440000 --unset contact-email --unset due-date',
|
|
754
799
|
raw: `{
|
|
755
800
|
"unset": ["contact-email", "due-date"]
|
|
801
|
+
}`,
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
description:
|
|
805
|
+
"Set a ReferenceField on an existing record. Pass the target record id (a bare UUID, or the compound 'recordId::anything' whose label is ignored) or an exact display value; the response shows the canonical 'recordId::displayValue' the server stored. If the reference doesn't exist the whole update is rejected with reason 'data_field_reference_not_found'.",
|
|
806
|
+
command:
|
|
807
|
+
'zinkee --json records update deals 550e8400-e29b-41d4-a716-446655440000 --set customer-ref=8ad602a1-f6e3-43f6-9ab2-f1c5afc99621',
|
|
808
|
+
raw: `{
|
|
809
|
+
"set": {
|
|
810
|
+
"customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621"
|
|
811
|
+
}
|
|
812
|
+
}`,
|
|
813
|
+
response: `{
|
|
814
|
+
"recordId": "550e8400-e29b-41d4-a716-446655440000",
|
|
815
|
+
"values": {
|
|
816
|
+
"deal-name": "Acme renewal",
|
|
817
|
+
"customer-ref": "8ad602a1-f6e3-43f6-9ab2-f1c5afc99621::Acme Corp"
|
|
818
|
+
}
|
|
756
819
|
}`,
|
|
757
820
|
},
|
|
758
821
|
]),
|