zinkee 0.1.29 → 0.1.31
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 +2 -1
- package/AGENTS.md +13 -0
- package/README.md +24 -0
- package/dist/index.js +161 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/api/schemas.ts +14 -0
- package/src/command-registry.ts +7 -0
- package/src/commands/schemas.test.ts +192 -0
- package/src/commands/schemas.ts +92 -3
- package/src/utils/examples.ts +91 -1
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"Bash(./gradlew :api:spotbugsMain)",
|
|
64
64
|
"Bash(./gradlew spotlessCheck)",
|
|
65
65
|
"Bash(git --no-pager diff --stat)",
|
|
66
|
-
"Bash(git --no-pager diff -- src/commands/records.ts src/api/records.ts)"
|
|
66
|
+
"Bash(git --no-pager diff -- src/commands/records.ts src/api/records.ts)",
|
|
67
|
+
"Bash(echo \"===EXIT: $?===\")"
|
|
67
68
|
]
|
|
68
69
|
}
|
|
69
70
|
}
|
package/AGENTS.md
CHANGED
|
@@ -48,6 +48,19 @@ Use this order when reasoning about behavior:
|
|
|
48
48
|
- parsers live in `src/parsers`
|
|
49
49
|
- shell completion (engine, scripts, install, runtime) lives in `src/completion`
|
|
50
50
|
|
|
51
|
+
## API Examples (`--example`)
|
|
52
|
+
|
|
53
|
+
- `--example` output comes 100% from hand-written `src/utils/examples.ts`
|
|
54
|
+
(`specificExamples`, keyed by command path e.g. `"schemas update"`). No
|
|
55
|
+
codegen, no OpenAPI import.
|
|
56
|
+
- These MIRROR the swagger `@ExampleObject`s in the `z2-backend` repo
|
|
57
|
+
(`api/src/main/java/com/zinkee/api/infra/web/v2/ApiV2<Resource>Documentation.java`).
|
|
58
|
+
When you add/change an example on either side, update the other with the same
|
|
59
|
+
payload (request `raw` and `response`). Nothing keeps them in sync
|
|
60
|
+
automatically.
|
|
61
|
+
- Keep `response` shapes aligned with the backend DTO field order (e.g.
|
|
62
|
+
`SchemaV2ResourceResponse`: `… commentable, highlightedField, fields`).
|
|
63
|
+
|
|
51
64
|
## Testing
|
|
52
65
|
|
|
53
66
|
Prefer focused command tests and parser/client unit tests.
|
package/README.md
CHANGED
|
@@ -248,6 +248,30 @@ date_format(44b72f55-3fc3-40f4-88bb-343bb4a6270c, "AAAA-MM")
|
|
|
248
248
|
- Functions: `concat`, `if`, `date_format`, `datetime_diff`, arithmetic (`+`,`-`,`*`,`/`)
|
|
249
249
|
- Invalid formulas are created with status `DRAFT` and evaluate to `null`
|
|
250
250
|
|
|
251
|
+
### Set unique fields
|
|
252
|
+
|
|
253
|
+
Define a composite unique constraint: the listed fields must be unique **together**
|
|
254
|
+
across the schema's records. `--field` accepts a field UUID or slug and is repeatable;
|
|
255
|
+
the order you pass them is the order of the constraint.
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Single field must be unique
|
|
259
|
+
zinkee schemas set-unique-fields contacts --field email
|
|
260
|
+
|
|
261
|
+
# Composite: the (contact-name, notes) combination must be unique
|
|
262
|
+
zinkee schemas set-unique-fields contacts --field contact-name --field notes
|
|
263
|
+
|
|
264
|
+
# Remove the unique constraint
|
|
265
|
+
zinkee schemas set-unique-fields contacts --clear
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Notes:
|
|
269
|
+
|
|
270
|
+
- Pass either one or more `--field`, or `--clear` — not both, and not neither.
|
|
271
|
+
- Every `--field` must already exist in the schema, otherwise the command fails.
|
|
272
|
+
- If existing records already violate the requested constraint, the backend rejects
|
|
273
|
+
it with `409 Conflict`.
|
|
274
|
+
|
|
251
275
|
---
|
|
252
276
|
|
|
253
277
|
## Records
|
package/dist/index.js
CHANGED
|
@@ -667,6 +667,13 @@ var commandRegistry = {
|
|
|
667
667
|
supportsRaw: false,
|
|
668
668
|
output: writeOutput
|
|
669
669
|
},
|
|
670
|
+
"schemas set-unique-fields": {
|
|
671
|
+
command: "schemas set-unique-fields",
|
|
672
|
+
accessLevel: "write",
|
|
673
|
+
supportsExample: true,
|
|
674
|
+
supportsRaw: false,
|
|
675
|
+
output: writeOutput
|
|
676
|
+
},
|
|
670
677
|
"schemas delete": {
|
|
671
678
|
command: "schemas delete",
|
|
672
679
|
accessLevel: "destructive",
|
|
@@ -2402,6 +2409,7 @@ var specificExamples = {
|
|
|
2402
2409
|
},
|
|
2403
2410
|
"auditable": true,
|
|
2404
2411
|
"commentable": true,
|
|
2412
|
+
"highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
2405
2413
|
"fields": [
|
|
2406
2414
|
{
|
|
2407
2415
|
"id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
@@ -2416,7 +2424,8 @@ var specificExamples = {
|
|
|
2416
2424
|
"conditionalFormatting": []
|
|
2417
2425
|
}
|
|
2418
2426
|
}
|
|
2419
|
-
]
|
|
2427
|
+
],
|
|
2428
|
+
"uniqueFields": []
|
|
2420
2429
|
}`
|
|
2421
2430
|
},
|
|
2422
2431
|
{
|
|
@@ -2432,6 +2441,86 @@ var specificExamples = {
|
|
|
2432
2441
|
command: "zinkee --json schemas update contacts --auditable true",
|
|
2433
2442
|
raw: `{
|
|
2434
2443
|
"auditable": true
|
|
2444
|
+
}`
|
|
2445
|
+
},
|
|
2446
|
+
{
|
|
2447
|
+
description: "Set the highlighted (main) field of a schema.",
|
|
2448
|
+
command: "zinkee --json schemas update contacts --highlighted-field contact-name",
|
|
2449
|
+
raw: `{
|
|
2450
|
+
"highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2451
|
+
}`
|
|
2452
|
+
}
|
|
2453
|
+
]),
|
|
2454
|
+
"schemas set-unique-fields": buildCommandExamples([
|
|
2455
|
+
{
|
|
2456
|
+
description: "Set a composite unique constraint: the listed fields must be unique together across records.",
|
|
2457
|
+
command: "zinkee --json schemas set-unique-fields contacts --field contact-name --field notes",
|
|
2458
|
+
raw: `{
|
|
2459
|
+
"fieldIds": [
|
|
2460
|
+
"c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
2461
|
+
"1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
|
|
2462
|
+
]
|
|
2463
|
+
}`,
|
|
2464
|
+
response: `{
|
|
2465
|
+
"id": "2eb8bdcf-6d9c-4fbb-9018-7fa8180f4c5e",
|
|
2466
|
+
"name": "Contacts",
|
|
2467
|
+
"slug": "contacts",
|
|
2468
|
+
"folder": {
|
|
2469
|
+
"id": "7f4e7c25-d8b2-4c1a-9f3e-6a7b5c8d9e1f",
|
|
2470
|
+
"name": "Customers",
|
|
2471
|
+
"isRoot": false
|
|
2472
|
+
},
|
|
2473
|
+
"auditable": true,
|
|
2474
|
+
"commentable": true,
|
|
2475
|
+
"highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
2476
|
+
"fields": [
|
|
2477
|
+
{
|
|
2478
|
+
"id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
2479
|
+
"type": "TextField",
|
|
2480
|
+
"slug": "contact-name",
|
|
2481
|
+
"label": "Customer Name",
|
|
2482
|
+
"required": true,
|
|
2483
|
+
"system": false,
|
|
2484
|
+
"config": {
|
|
2485
|
+
"textType": "NORMAL",
|
|
2486
|
+
"defaultValue": "Unknown",
|
|
2487
|
+
"conditionalFormatting": []
|
|
2488
|
+
}
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
"id": "1d2e3f4a-5b6c-7d8e-9f01-23456789abcd",
|
|
2492
|
+
"type": "TextField",
|
|
2493
|
+
"slug": "notes",
|
|
2494
|
+
"label": "Notes",
|
|
2495
|
+
"required": false,
|
|
2496
|
+
"system": false,
|
|
2497
|
+
"config": {
|
|
2498
|
+
"textType": "LONG",
|
|
2499
|
+
"defaultValue": null,
|
|
2500
|
+
"conditionalFormatting": []
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
],
|
|
2504
|
+
"uniqueFields": [
|
|
2505
|
+
"c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
2506
|
+
"1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
|
|
2507
|
+
]
|
|
2508
|
+
}`
|
|
2509
|
+
},
|
|
2510
|
+
{
|
|
2511
|
+
description: "Set a single-field unique constraint.",
|
|
2512
|
+
command: "zinkee --json schemas set-unique-fields contacts --field contact-name",
|
|
2513
|
+
raw: `{
|
|
2514
|
+
"fieldIds": [
|
|
2515
|
+
"c60b8526-5835-4c9f-aecd-9a16c7704a34"
|
|
2516
|
+
]
|
|
2517
|
+
}`
|
|
2518
|
+
},
|
|
2519
|
+
{
|
|
2520
|
+
description: "Clear the unique constraint (no field must be unique anymore).",
|
|
2521
|
+
command: "zinkee --json schemas set-unique-fields contacts --clear",
|
|
2522
|
+
raw: `{
|
|
2523
|
+
"fieldIds": []
|
|
2435
2524
|
}`
|
|
2436
2525
|
}
|
|
2437
2526
|
]),
|
|
@@ -3973,6 +4062,7 @@ var specificExamples = {
|
|
|
3973
4062
|
},
|
|
3974
4063
|
"auditable": true,
|
|
3975
4064
|
"commentable": true,
|
|
4065
|
+
"highlightedField": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
3976
4066
|
"fields": [
|
|
3977
4067
|
{
|
|
3978
4068
|
"id": "c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
@@ -4281,6 +4371,10 @@ var specificExamples = {
|
|
|
4281
4371
|
"conditionalFormatting": []
|
|
4282
4372
|
}
|
|
4283
4373
|
}
|
|
4374
|
+
],
|
|
4375
|
+
"uniqueFields": [
|
|
4376
|
+
"c60b8526-5835-4c9f-aecd-9a16c7704a34",
|
|
4377
|
+
"1d2e3f4a-5b6c-7d8e-9f01-23456789abcd"
|
|
4284
4378
|
]
|
|
4285
4379
|
}`
|
|
4286
4380
|
}
|
|
@@ -10711,6 +10805,12 @@ var SchemasApi = class {
|
|
|
10711
10805
|
jsonOptions7(input, "PATCH")
|
|
10712
10806
|
);
|
|
10713
10807
|
}
|
|
10808
|
+
setUniqueFields(schema, input) {
|
|
10809
|
+
return this.client.requestJson(
|
|
10810
|
+
`/api/v2/schemas/${selectorValue9(schema)}/unique-fields`,
|
|
10811
|
+
jsonOptions7(input, "PUT")
|
|
10812
|
+
);
|
|
10813
|
+
}
|
|
10714
10814
|
deleteSchema(schema) {
|
|
10715
10815
|
return this.client.requestJson(
|
|
10716
10816
|
`/api/v2/schemas/${selectorValue9(schema)}`,
|
|
@@ -11239,6 +11339,9 @@ function parseUuidOrSlugSelector9(input) {
|
|
|
11239
11339
|
throw new InvalidArgumentError9(message);
|
|
11240
11340
|
}
|
|
11241
11341
|
}
|
|
11342
|
+
function collectFieldSelectors(value, previous = []) {
|
|
11343
|
+
return [...previous, parseUuidOrSlugSelector9(value)];
|
|
11344
|
+
}
|
|
11242
11345
|
function parseBooleanFlag(input) {
|
|
11243
11346
|
const normalized = input.trim().toLowerCase();
|
|
11244
11347
|
if (["true", "1", "yes", "y", "on"].includes(normalized)) {
|
|
@@ -11498,15 +11601,27 @@ function registerSchemasCommands(program, options = {}) {
|
|
|
11498
11601
|
}
|
|
11499
11602
|
writeLine11(stdout, `Created schema "${data.slug ?? commandOptions.slug}".`);
|
|
11500
11603
|
});
|
|
11501
|
-
schemas.command("update").description("Update a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option("--name <name>", "schema name").option("--slug <slug>", "schema slug").option("--auditable <bool>", "auditable flag", parseBooleanFlag).option("--commentable <bool>", "commentable flag", parseBooleanFlag).
|
|
11604
|
+
schemas.command("update").description("Update a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option("--name <name>", "schema name").option("--slug <slug>", "schema slug").option("--auditable <bool>", "auditable flag", parseBooleanFlag).option("--commentable <bool>", "commentable flag", parseBooleanFlag).option(
|
|
11605
|
+
"--highlighted-field <field>",
|
|
11606
|
+
"highlighted (main) field uuid or slug",
|
|
11607
|
+
parseUuidOrSlugSelector9
|
|
11608
|
+
).action(async function action(schema, commandOptions) {
|
|
11502
11609
|
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
11503
11610
|
ensureWritable8("schemas update", readOnly, "write");
|
|
11504
11611
|
const resolvedSchema = await resolveSchemaSelector2(api, schema);
|
|
11505
11612
|
const input = buildSchemaUpdateInput(commandOptions);
|
|
11613
|
+
if (commandOptions.highlightedField !== void 0) {
|
|
11614
|
+
const resolvedField = await resolveFieldSelector(
|
|
11615
|
+
api,
|
|
11616
|
+
resolvedSchema,
|
|
11617
|
+
commandOptions.highlightedField
|
|
11618
|
+
);
|
|
11619
|
+
input.highlightedField = resolvedField.value;
|
|
11620
|
+
}
|
|
11506
11621
|
if (isEmptyObject6(input)) {
|
|
11507
11622
|
throw new CliError(
|
|
11508
11623
|
"invalid_cli_usage",
|
|
11509
|
-
'Provide at least one schema field to update via "--name", "--slug", "--auditable", or "--
|
|
11624
|
+
'Provide at least one schema field to update via "--name", "--slug", "--auditable", "--commentable", or "--highlighted-field".'
|
|
11510
11625
|
);
|
|
11511
11626
|
}
|
|
11512
11627
|
const data = await api.updateSchema(resolvedSchema, input);
|
|
@@ -11516,6 +11631,49 @@ function registerSchemasCommands(program, options = {}) {
|
|
|
11516
11631
|
}
|
|
11517
11632
|
writeLine11(stdout, `Updated schema "${data.slug ?? schema.value}".`);
|
|
11518
11633
|
});
|
|
11634
|
+
schemas.command("set-unique-fields").description("Set or clear the composite unique-field constraint on a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).option(
|
|
11635
|
+
"--field <field>",
|
|
11636
|
+
"field uuid or slug to include in the unique constraint (repeatable)",
|
|
11637
|
+
collectFieldSelectors,
|
|
11638
|
+
[]
|
|
11639
|
+
).option("--clear", "clear the unique constraint (sends an empty field list)").action(async function action(schema, commandOptions) {
|
|
11640
|
+
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
11641
|
+
ensureWritable8("schemas set-unique-fields", readOnly, "write");
|
|
11642
|
+
const selectors = commandOptions.field ?? [];
|
|
11643
|
+
if (commandOptions.clear && selectors.length > 0) {
|
|
11644
|
+
throw new CliError(
|
|
11645
|
+
"invalid_cli_usage",
|
|
11646
|
+
'Use either "--field" or "--clear", not both.'
|
|
11647
|
+
);
|
|
11648
|
+
}
|
|
11649
|
+
if (!commandOptions.clear && selectors.length === 0) {
|
|
11650
|
+
throw new CliError(
|
|
11651
|
+
"invalid_cli_usage",
|
|
11652
|
+
'Provide at least one field via "--field", or pass "--clear" to remove the unique constraint.'
|
|
11653
|
+
);
|
|
11654
|
+
}
|
|
11655
|
+
const resolvedSchema = await resolveSchemaSelector2(api, schema);
|
|
11656
|
+
const resolvedFields = await Promise.all(
|
|
11657
|
+
selectors.map((selector) => resolveFieldSelector(api, resolvedSchema, selector))
|
|
11658
|
+
);
|
|
11659
|
+
const input = {
|
|
11660
|
+
fieldIds: resolvedFields.map((field) => field.value)
|
|
11661
|
+
};
|
|
11662
|
+
const data = await api.setUniqueFields(resolvedSchema, input);
|
|
11663
|
+
if (json) {
|
|
11664
|
+
emitJson11(stdout, "schemas set-unique-fields", data, filePath);
|
|
11665
|
+
return;
|
|
11666
|
+
}
|
|
11667
|
+
if (input.fieldIds.length === 0) {
|
|
11668
|
+
writeLine11(stdout, `Cleared unique fields on schema "${schema.value}".`);
|
|
11669
|
+
return;
|
|
11670
|
+
}
|
|
11671
|
+
const count = input.fieldIds.length;
|
|
11672
|
+
writeLine11(
|
|
11673
|
+
stdout,
|
|
11674
|
+
`Set unique fields on schema "${schema.value}" (${count} field${count === 1 ? "" : "s"}).`
|
|
11675
|
+
);
|
|
11676
|
+
});
|
|
11519
11677
|
schemas.command("delete").description("Delete a schema").argument("<schema>", "schema uuid or slug", parseUuidOrSlugSelector9).action(async function action(schema) {
|
|
11520
11678
|
const { api, json, readOnly } = createSchemasApi(this, filePath);
|
|
11521
11679
|
ensureWritable8("schemas delete", readOnly, "destructive");
|