zinkee 0.1.12 → 0.1.15

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.12",
3
+ "version": "0.1.15",
4
4
  "description": "CLI for Zinkee API v2",
5
5
  "type": "module",
6
6
  "bin": {
@@ -133,6 +133,13 @@ export class RecordsApi {
133
133
  );
134
134
  }
135
135
 
136
+ clearRecords(schema: NormalizedUuidOrSlug): Promise<Record<string, unknown>> {
137
+ return this.client.requestJson<Record<string, unknown>>(
138
+ `/api/v2/schemas/${selectorValue(schema)}/records:clear`,
139
+ { method: "DELETE" },
140
+ );
141
+ }
142
+
136
143
  private normalizeQueryInput(query: RecordQueryInput): Record<string, unknown> {
137
144
  const where = query.where?.map((expression) => ({
138
145
  field: expression.field,
@@ -665,6 +665,13 @@ export const commandRegistry = {
665
665
  supportsRaw: false,
666
666
  output: writeOutput,
667
667
  },
668
+ "records clear": {
669
+ command: "records clear",
670
+ accessLevel: "destructive",
671
+ supportsExample: true,
672
+ supportsRaw: false,
673
+ output: writeOutput,
674
+ },
668
675
  "comments list": {
669
676
  command: "comments list",
670
677
  accessLevel: "read",
@@ -335,7 +335,7 @@ describe("automations commands", () => {
335
335
  "--target-schema",
336
336
  "schema-1",
337
337
  "--map-json",
338
- 'owner={"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}',
338
+ 'owner={"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}',
339
339
  ]);
340
340
 
341
341
  expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
@@ -348,7 +348,7 @@ describe("automations commands", () => {
348
348
  rawValue: null,
349
349
  source: {
350
350
  type: "TRIGGER",
351
- id: "payload_value",
351
+ id: "f1e1d000-0000-0000-0000-000000000005",
352
352
  },
353
353
  },
354
354
  },
@@ -378,7 +378,7 @@ describe("automations commands", () => {
378
378
  "--target-schema",
379
379
  "schema-1",
380
380
  "--condition-json",
381
- '{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}',
381
+ '{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
382
382
  ]);
383
383
 
384
384
  expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({
@@ -395,7 +395,7 @@ describe("automations commands", () => {
395
395
  rawValue: null,
396
396
  source: {
397
397
  type: "TRIGGER",
398
- id: "payload_value",
398
+ id: "f1e1d000-0000-0000-0000-000000000005",
399
399
  },
400
400
  },
401
401
  ],
@@ -471,7 +471,7 @@ describe("automations commands", () => {
471
471
  "--trigger-field",
472
472
  "field-1",
473
473
  "--condition-json",
474
- '{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}',
474
+ '{"field":"status","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}',
475
475
  ]),
476
476
  ).rejects.toThrowError('Expected trigger condition 1 value 1 to use only literal "rawValue" values.');
477
477
 
@@ -151,7 +151,7 @@ describe("displays commands", () => {
151
151
  "--name",
152
152
  "Revenue",
153
153
  "--chart-type",
154
- "bar",
154
+ "bars",
155
155
  ]);
156
156
 
157
157
  const init = fetchMock.mock.calls[0]?.[1];
@@ -159,7 +159,7 @@ describe("displays commands", () => {
159
159
  expect(JSON.parse(String(init?.body))).toMatchObject({
160
160
  type: "CHART",
161
161
  presentation: { name: "Revenue" },
162
- chart: { chartType: "bar" },
162
+ chart: { chartType: "bars" },
163
163
  });
164
164
  expect(JSON.parse(output.read())).toEqual({
165
165
  data: { displayId: "display-1", widgetId: "widget-1" },
@@ -364,4 +364,62 @@ describe("records commands", () => {
364
364
 
365
365
  expect(fetchMock).not.toHaveBeenCalled();
366
366
  });
367
+
368
+ it("clears all records in a schema", async () => {
369
+ const configPath = createApiConfig();
370
+ const output = createStdoutBuffer();
371
+ const fetchMock = createFetchMock();
372
+ mockSchemaLookup(fetchMock);
373
+ fetchMock.mockResolvedValueOnce(
374
+ jsonResponse({
375
+ schemaId: "schema-1",
376
+ cleared: true,
377
+ }),
378
+ );
379
+ const program = buildProgram({ configPath, stdout: output.stdout });
380
+
381
+ await program.parseAsync([
382
+ "node",
383
+ "zinkee",
384
+ "--json",
385
+ "records",
386
+ "clear",
387
+ "contacts",
388
+ ]);
389
+
390
+ const init = fetchMock.mock.calls[1]?.[1];
391
+
392
+ expect(fetchMock.mock.calls[1]?.[0]).toBe(
393
+ "https://api.zinkee.test/api/v2/schemas/schema-1/records:clear",
394
+ );
395
+ expect(init?.method).toBe("DELETE");
396
+ expect(init?.body).toBeUndefined();
397
+
398
+ const payload = JSON.parse(output.read());
399
+
400
+ expect(payload.data).toEqual({
401
+ schemaId: "schema-1",
402
+ cleared: true,
403
+ });
404
+ });
405
+
406
+ it("blocks records clear in read-only mode", async () => {
407
+ const configPath = createApiConfig();
408
+ const output = createStdoutBuffer();
409
+ const fetchMock = createFetchMock();
410
+ const program = buildProgram({ configPath, stdout: output.stdout });
411
+
412
+ await expect(
413
+ program.parseAsync([
414
+ "node",
415
+ "zinkee",
416
+ "--read-only",
417
+ "records",
418
+ "clear",
419
+ "contacts",
420
+ ]),
421
+ ).rejects.toThrowError('Command "records clear" is not allowed in read-only mode.');
422
+
423
+ expect(fetchMock).not.toHaveBeenCalled();
424
+ });
367
425
  });
@@ -723,4 +723,23 @@ export function registerRecordsCommands(
723
723
 
724
724
  writeLine(stdout, `Deleted record "${record.value}" from schema "${schema.value}".`);
725
725
  });
726
+
727
+ records
728
+ .command("clear")
729
+ .description("Delete all records from a schema's table")
730
+ .argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector)
731
+ .action(async function action(this: Command, schema: NormalizedUuidOrSlug) {
732
+ const { api, schemasApi, json, readOnly } = createRecordsApi(this, filePath);
733
+ ensureWritable("records clear", readOnly, "destructive");
734
+ const resolvedSchema = await resolveSchemaSelector(schemasApi, schema);
735
+
736
+ const data = await api.clearRecords(resolvedSchema);
737
+
738
+ if (json) {
739
+ emitJson(stdout, "records clear", data, filePath);
740
+ return;
741
+ }
742
+
743
+ writeLine(stdout, `Cleared all records from schema "${schema.value}".`);
744
+ });
726
745
  }
@@ -411,11 +411,11 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
411
411
  {
412
412
  description: "Patch formula field (formula text).",
413
413
  command:
414
- `zinkee --json schemas fields update contacts formula-total --raw '{"type":"FormulaField","config":{"formulaText":"amount * 1.10","uiFormatKind":"number","status":"ACTIVE","numberFormatOptions":{"format":"number","decimals":2,"thousandsSeparator":true},"conditionalFormatting":[]}}'`,
414
+ `zinkee --json schemas fields update contacts formula-total --raw '{"type":"FormulaField","config":{"formulaText":"c52efe3f-2825-4c60-a7e3-34dd4c1ce043 * 1.10","uiFormatKind":"number","status":"ACTIVE","numberFormatOptions":{"format":"number","decimals":2,"thousandsSeparator":true},"conditionalFormatting":[]}}'`,
415
415
  raw: `{
416
416
  "type": "FormulaField",
417
417
  "config": {
418
- "formulaText": "amount * 1.10",
418
+ "formulaText": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043 * 1.10",
419
419
  "uiFormatKind": "number",
420
420
  "status": "ACTIVE",
421
421
  "numberFormatOptions": {
@@ -1398,9 +1398,9 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1398
1398
  }`,
1399
1399
  },
1400
1400
  {
1401
- description: "Create a bar CHART widget with a time-serie x-axis and two y-axis series.",
1401
+ description: "Create a bars CHART widget with a time-serie x-axis and two y-axis series.",
1402
1402
  command:
1403
- `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by month" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type bar --x-axis-field due-date --x-axis-date-format MONTH --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"BAR"}' --y-axis-serie '{"typeCalc":"COUNT","fieldCalc":"id","color":"#E57373","axisType":"LINE"}' --legend '{"position":"bottom"}'`,
1403
+ `zinkee --json displays widgets create 0f6f062f-1478-4df2-96a1-c495d037d260 --type CHART --name "Revenue by month" --origin-resource 2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e --layout '{"x":0,"y":0,"w":8,"h":6}' --chart-type bars --x-axis-field due-date --x-axis-date-format MONTH --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#4B9CD3","axisType":"PRIMARY"}' --y-axis-serie '{"typeCalc":"COUNT","fieldCalc":"id","color":"#E57373","axisType":"SECONDARY"}' --legend '{"position":"bottom"}'`,
1404
1404
  raw: `{
1405
1405
  "type": "CHART",
1406
1406
  "source": {
@@ -1412,7 +1412,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1412
1412
  },
1413
1413
  "presentation": { "name": "Revenue by month" },
1414
1414
  "chart": {
1415
- "chartType": "bar",
1415
+ "chartType": "bars",
1416
1416
  "xAxisFieldId": "due-date",
1417
1417
  "xAxisDateFormat": "MONTH",
1418
1418
  "xAxisAsTimeSerie": true,
@@ -1421,13 +1421,13 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1421
1421
  "typeCalc": "SUM",
1422
1422
  "fieldCalc": "amount",
1423
1423
  "color": "#4B9CD3",
1424
- "axisType": "BAR"
1424
+ "axisType": "PRIMARY"
1425
1425
  },
1426
1426
  {
1427
1427
  "typeCalc": "COUNT",
1428
1428
  "fieldCalc": "id",
1429
1429
  "color": "#E57373",
1430
- "axisType": "LINE"
1430
+ "axisType": "SECONDARY"
1431
1431
  }
1432
1432
  ],
1433
1433
  "legend": { "position": "bottom" }
@@ -1582,12 +1582,12 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1582
1582
  }`,
1583
1583
  },
1584
1584
  {
1585
- description: "Replace the chart series with a weekly line.",
1585
+ description: "Replace the chart series with weekly lines.",
1586
1586
  command:
1587
- `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 22222222-2222-4222-a222-222222222222 --chart-type line --x-axis-field due-date --x-axis-date-format WEEK --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#2E7D32","axisType":"LINE"}'`,
1587
+ `zinkee --json displays widgets update 0f6f062f-1478-4df2-96a1-c495d037d260 22222222-2222-4222-a222-222222222222 --chart-type lines --x-axis-field due-date --x-axis-date-format WEEK --x-axis-time-series --y-axis-serie '{"typeCalc":"SUM","fieldCalc":"amount","color":"#2E7D32","axisType":"PRIMARY"}'`,
1588
1588
  raw: `{
1589
1589
  "chart": {
1590
- "chartType": "line",
1590
+ "chartType": "lines",
1591
1591
  "xAxisFieldId": "due-date",
1592
1592
  "xAxisDateFormat": "WEEK",
1593
1593
  "xAxisAsTimeSerie": true,
@@ -1596,7 +1596,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1596
1596
  "typeCalc": "SUM",
1597
1597
  "fieldCalc": "amount",
1598
1598
  "color": "#2E7D32",
1599
- "axisType": "LINE"
1599
+ "axisType": "PRIMARY"
1600
1600
  }
1601
1601
  ]
1602
1602
  }
@@ -1932,7 +1932,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1932
1932
  "required": false,
1933
1933
  "system": false,
1934
1934
  "config": {
1935
- "formulaText": "amount * 1.21",
1935
+ "formulaText": "c52efe3f-2825-4c60-a7e3-34dd4c1ce043 * 1.21",
1936
1936
  "uiFormatKind": "number",
1937
1937
  "status": "ACTIVE",
1938
1938
  "defaultValue": null,
@@ -1947,12 +1947,12 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
1947
1947
  {
1948
1948
  "id": "6d7e8f90-1234-5678-9abc-def012345678",
1949
1949
  "type": "FormulaField",
1950
- "slug": "formula-deadline",
1951
- "label": "Computed Deadline",
1950
+ "slug": "formula-period",
1951
+ "label": "Formula Period",
1952
1952
  "required": false,
1953
1953
  "system": false,
1954
1954
  "config": {
1955
- "formulaText": "addDays(startDate, 7)",
1955
+ "formulaText": "date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90, \"AAAA-MM\")",
1956
1956
  "uiFormatKind": "date",
1957
1957
  "status": "ACTIVE",
1958
1958
  "defaultValue": null,
@@ -2234,6 +2234,17 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
2234
2234
  response: `{
2235
2235
  "recordId": "550e8400-e29b-41d4-a716-446655440000",
2236
2236
  "deleted": true
2237
+ }`,
2238
+ },
2239
+ ]),
2240
+ "records clear": buildCommandExamples([
2241
+ {
2242
+ description:
2243
+ "Delete every record from a schema's table. Heritage rules (CASCADE / SET NULL) propagate to child schemas asynchronously via the CDC pipeline.",
2244
+ command: "zinkee --json records clear contacts",
2245
+ response: `{
2246
+ "schemaId": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
2247
+ "cleared": true
2237
2248
  }`,
2238
2249
  },
2239
2250
  ]),
@@ -3353,7 +3364,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
3353
3364
  description:
3354
3365
  "Add a search-records action whose condition pulls its value from the trigger payload using --condition-json.",
3355
3366
  command:
3356
- `zinkee --json automations actions add automation-1 --type search_records --name "Find duplicates" --target-schema followups --condition-json '{"field":"email","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}'`,
3367
+ `zinkee --json automations actions add automation-1 --type search_records --name "Find duplicates" --target-schema followups --condition-json '{"field":"email","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"f1e1d000-0000-0000-0000-000000000005"}}]}'`,
3357
3368
  raw: `{
3358
3369
  "type": "search_records",
3359
3370
  "name": "Find duplicates",
@@ -3368,7 +3379,7 @@ const specificExamples: Record<string, readonly CommandExample[]> = {
3368
3379
  "rawValue": null,
3369
3380
  "source": {
3370
3381
  "type": "TRIGGER",
3371
- "id": "payload_value"
3382
+ "id": "f1e1d000-0000-0000-0000-000000000005"
3372
3383
  }
3373
3384
  }
3374
3385
  ]
@@ -3965,7 +3976,7 @@ async function buildDynamicAutomationExamples(
3965
3976
  {
3966
3977
  description:
3967
3978
  `Add a create-record action with literal and trigger-based mappings targeting ${schemaField.schemaRef}.`,
3968
- command: `zinkee --json automations actions add ${automationId} --type create_record --name "Create follow-up order" --target-schema ${schemaField.schemaRef} --map ${schemaField.fieldRef}=pending --map-json owner='{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}'`,
3979
+ command: `zinkee --json automations actions add ${automationId} --type create_record --name "Create follow-up order" --target-schema ${schemaField.schemaRef} --map ${schemaField.fieldRef}=pending --map-json owner='{"rawValue":null,"source":{"type":"TRIGGER","id":"${schemaField.fieldId}"}}'`,
3969
3980
  raw: `{
3970
3981
  "type": "create_record",
3971
3982
  "name": "Create follow-up order",
@@ -3980,7 +3991,7 @@ async function buildDynamicAutomationExamples(
3980
3991
  "rawValue": null,
3981
3992
  "source": {
3982
3993
  "type": "TRIGGER",
3983
- "id": "payload_value"
3994
+ "id": "${schemaField.fieldId}"
3984
3995
  }
3985
3996
  }
3986
3997
  }
@@ -4008,7 +4019,7 @@ async function buildDynamicAutomationExamples(
4008
4019
  },
4009
4020
  {
4010
4021
  description: `Add a search-records action with a trigger-based condition on ${schemaField.schemaRef}.`,
4011
- command: `zinkee --json automations actions add ${automationId} --type search_records --name "Find duplicates" --target-schema ${schemaField.schemaRef} --condition-json '{"field":"${schemaField.fieldRef}","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"payload_value"}}]}'`,
4022
+ command: `zinkee --json automations actions add ${automationId} --type search_records --name "Find duplicates" --target-schema ${schemaField.schemaRef} --condition-json '{"field":"${schemaField.fieldRef}","comparator":"eq","values":[{"rawValue":null,"source":{"type":"TRIGGER","id":"${schemaField.fieldId}"}}]}'`,
4012
4023
  raw: `{
4013
4024
  "type": "search_records",
4014
4025
  "name": "Find duplicates",
@@ -4023,7 +4034,7 @@ async function buildDynamicAutomationExamples(
4023
4034
  "rawValue": null,
4024
4035
  "source": {
4025
4036
  "type": "TRIGGER",
4026
- "id": "payload_value"
4037
+ "id": "${schemaField.fieldId}"
4027
4038
  }
4028
4039
  }
4029
4040
  ]
@@ -450,17 +450,18 @@ const fieldTypeDefinitions = [
450
450
  aliases: ["formula", "FormulaField"],
451
451
  description: "Calculated field from a formula.",
452
452
  rawShape:
453
- '{"type":"FormulaField","slug":"formula-summary","label":"Formula Summary","config":{"formulaText":"concat(firstName, \' \', lastName)","uiFormatKind":"none"}}',
453
+ '{"type":"FormulaField","slug":"formula-code","label":"Formula Code","config":{"formulaText":"concat(\\"CUST-\\", f1e1d000-0000-0000-0000-000000000005)","uiFormatKind":"none"}}',
454
454
  examplePayloads: [
455
455
  {
456
456
  name: "Formula field (none)",
457
457
  payload: {
458
458
  type: "FormulaField",
459
- slug: "formula-summary",
460
- label: "Formula Summary",
459
+ slug: "formula-code",
460
+ label: "Formula Code",
461
461
  required: false,
462
462
  config: {
463
- formulaText: "concat(firstName, ' ', lastName)",
463
+ formulaText:
464
+ 'concat("CUST-", f1e1d000-0000-0000-0000-000000000005)',
464
465
  uiFormatKind: "none",
465
466
  status: "ACTIVE",
466
467
  conditionalFormatting: [],
@@ -475,7 +476,7 @@ const fieldTypeDefinitions = [
475
476
  label: "Formula Total",
476
477
  required: false,
477
478
  config: {
478
- formulaText: "amount * 1.21",
479
+ formulaText: "c52efe3f-2825-4c60-a7e3-34dd4c1ce043 * 1.21",
479
480
  uiFormatKind: "number",
480
481
  status: "ACTIVE",
481
482
  numberFormatOptions: {
@@ -491,11 +492,12 @@ const fieldTypeDefinitions = [
491
492
  name: "Formula field (date)",
492
493
  payload: {
493
494
  type: "FormulaField",
494
- slug: "formula-deadline",
495
- label: "Computed Deadline",
495
+ slug: "formula-period",
496
+ label: "Formula Period",
496
497
  required: false,
497
498
  config: {
498
- formulaText: "addDays(startDate, 7)",
499
+ formulaText:
500
+ 'date_format(d4e5f6a7-b8c9-0d1e-2f3a-4b5c6d7e8f90, "AAAA-MM")',
499
501
  uiFormatKind: "date",
500
502
  status: "ACTIVE",
501
503
  dateFormatOptions: {
@@ -515,7 +517,7 @@ const fieldTypeDefinitions = [
515
517
  label: "Computed Assignee",
516
518
  required: false,
517
519
  config: {
518
- formulaText: "manager",
520
+ formulaText: "a7b8c9d0-e1f2-3a4b-5c6d-7e8f90123456",
519
521
  uiFormatKind: "member",
520
522
  status: "ACTIVE",
521
523
  conditionalFormatting: [],