@uipath/data-fabric-tool 1.201.0-preview.115 → 1.201.0-preview.121
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/dist/index.js +1 -1
- package/dist/{tool-cyfkvxhw.js → tool-4g47skgm.js} +1059 -373
- package/dist/tool.js +1 -1
- package/package.json +2 -2
- package/src/commands/directory.ts +21 -22
- package/src/commands/entities.spec.ts +513 -112
- package/src/commands/entities.ts +402 -160
- package/src/commands/roles.ts +8 -6
- package/src/utils/output.spec.ts +21 -1
- package/src/utils/output.ts +22 -1
- package/src/utils/validate.spec.ts +57 -62
- package/src/utils/validate.ts +12 -16
- package/tests/entities-lifecycle.e2e.test.ts +92 -17
- package/tests/files-management.e2e.test.ts +2 -2
- package/tests/records-management.e2e.test.ts +1 -1
package/src/commands/roles.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
processContext,
|
|
8
8
|
RESULTS,
|
|
9
9
|
} from "@uipath/common";
|
|
10
|
+
import type { DataFabricRoleGetAllOptions } from "@uipath/uipath-typescript";
|
|
10
11
|
import type { Command } from "commander";
|
|
11
12
|
import { fail } from "../utils/output";
|
|
12
13
|
import { connectOrFail } from "../utils/sdk-client";
|
|
@@ -63,13 +64,14 @@ export const registerRolesCommand = (program: Command) => {
|
|
|
63
64
|
const sdk = await connectOrFail(options.tenant);
|
|
64
65
|
if (!sdk) return;
|
|
65
66
|
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
folderKey: options.folderKey,
|
|
71
|
-
}),
|
|
67
|
+
const getAllOptions: DataFabricRoleGetAllOptions = {
|
|
68
|
+
stats: options.stats,
|
|
69
|
+
...(options.folderKey !== undefined && {
|
|
70
|
+
folderKey: options.folderKey,
|
|
72
71
|
}),
|
|
72
|
+
};
|
|
73
|
+
const [listError, result] = await catchError(
|
|
74
|
+
sdk.entities.roles.getAll(getAllOptions),
|
|
73
75
|
);
|
|
74
76
|
|
|
75
77
|
if (listError) {
|
package/src/utils/output.spec.ts
CHANGED
|
@@ -9,7 +9,11 @@ vi.mock("@uipath/common", async (importOriginal) => {
|
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
import { OutputFormatter } from "@uipath/common";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
fail,
|
|
14
|
+
failInvalidArgument,
|
|
15
|
+
requireDestructiveConfirmation,
|
|
16
|
+
} from "./output";
|
|
13
17
|
|
|
14
18
|
describe("fail", () => {
|
|
15
19
|
beforeEach(() => {
|
|
@@ -28,6 +32,20 @@ describe("fail", () => {
|
|
|
28
32
|
);
|
|
29
33
|
expect(process.exitCode).toBe(1);
|
|
30
34
|
});
|
|
35
|
+
|
|
36
|
+
it("can mark client-side validation failures as invalid arguments", () => {
|
|
37
|
+
failInvalidArgument("Bad body", "Fix the JSON shape.");
|
|
38
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
39
|
+
expect.objectContaining({
|
|
40
|
+
Result: "Failure",
|
|
41
|
+
Message: "Bad body",
|
|
42
|
+
Instructions: "Fix the JSON shape.",
|
|
43
|
+
ErrorCode: "invalid_argument",
|
|
44
|
+
Retry: "RetryWillNotFix",
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
expect(process.exitCode).toBe(1);
|
|
48
|
+
});
|
|
31
49
|
});
|
|
32
50
|
|
|
33
51
|
describe("requireDestructiveConfirmation", () => {
|
|
@@ -84,6 +102,8 @@ describe("requireDestructiveConfirmation", () => {
|
|
|
84
102
|
expect.objectContaining({
|
|
85
103
|
Message: "Reason required for destructive operation",
|
|
86
104
|
Instructions: reasonInstruction,
|
|
105
|
+
ErrorCode: "invalid_argument",
|
|
106
|
+
Retry: "RetryWillNotFix",
|
|
87
107
|
}),
|
|
88
108
|
);
|
|
89
109
|
expect(process.exitCode).toBe(1);
|
package/src/utils/output.ts
CHANGED
|
@@ -22,6 +22,24 @@ export function fail(message: string, instructions: string): void {
|
|
|
22
22
|
processContext.exit(1);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Keep Data Fabric's existing `Failure` + exit 1 envelope while setting the
|
|
27
|
+
* machine-readable validation classification explicitly.
|
|
28
|
+
*/
|
|
29
|
+
export function failInvalidArgument(
|
|
30
|
+
message: string,
|
|
31
|
+
instructions: string,
|
|
32
|
+
): void {
|
|
33
|
+
OutputFormatter.error({
|
|
34
|
+
Result: RESULTS.Failure,
|
|
35
|
+
Message: message,
|
|
36
|
+
Instructions: instructions,
|
|
37
|
+
ErrorCode: "invalid_argument",
|
|
38
|
+
Retry: "RetryWillNotFix",
|
|
39
|
+
});
|
|
40
|
+
processContext.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
25
43
|
export interface DestructiveOptions {
|
|
26
44
|
yes?: boolean;
|
|
27
45
|
// Legacy hidden alias for `yes`. The canonical flag is `--yes`; `--confirm`
|
|
@@ -61,7 +79,10 @@ export function requireDestructiveConfirmation(
|
|
|
61
79
|
|
|
62
80
|
const reason = options.reason?.trim();
|
|
63
81
|
if (reason === undefined || reason === "") {
|
|
64
|
-
|
|
82
|
+
failInvalidArgument(
|
|
83
|
+
"Reason required for destructive operation",
|
|
84
|
+
reasonInstruction,
|
|
85
|
+
);
|
|
65
86
|
return null;
|
|
66
87
|
}
|
|
67
88
|
|
|
@@ -115,29 +115,29 @@ describe("validateName", () => {
|
|
|
115
115
|
});
|
|
116
116
|
|
|
117
117
|
describe("findInvalidFieldName", () => {
|
|
118
|
-
it("returns null when every
|
|
118
|
+
it("returns null when every name is valid", () => {
|
|
119
119
|
expect(
|
|
120
120
|
findInvalidFieldName([
|
|
121
|
-
{
|
|
122
|
-
{
|
|
121
|
+
{ name: "title", type: "STRING" },
|
|
122
|
+
{ name: "amount", type: "DECIMAL" },
|
|
123
123
|
]),
|
|
124
124
|
).toBeNull();
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
it("flags the first invalid
|
|
127
|
+
it("flags the first invalid name", () => {
|
|
128
128
|
const err = findInvalidFieldName([
|
|
129
|
-
{
|
|
130
|
-
{
|
|
131
|
-
{
|
|
129
|
+
{ name: "title", type: "STRING" },
|
|
130
|
+
{ name: "Class", type: "STRING" },
|
|
131
|
+
{ name: "amount", type: "DECIMAL" },
|
|
132
132
|
]);
|
|
133
133
|
expect(err?.message).toContain("'Class'");
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
it("skips entries where
|
|
137
|
-
// A shape-level check catches non-string
|
|
136
|
+
it("skips entries where name is not a string", () => {
|
|
137
|
+
// A shape-level check catches non-string name earlier in the
|
|
138
138
|
// command handler. This helper simply ignores them.
|
|
139
139
|
expect(
|
|
140
|
-
findInvalidFieldName([{
|
|
140
|
+
findInvalidFieldName([{ name: 42 }, { name: "good" }]),
|
|
141
141
|
).toBeNull();
|
|
142
142
|
});
|
|
143
143
|
});
|
|
@@ -156,29 +156,26 @@ describe("schema metadata validation", () => {
|
|
|
156
156
|
|
|
157
157
|
it("rejects case-insensitive field name and display-name collisions", () => {
|
|
158
158
|
expect(
|
|
159
|
-
findInvalidFieldMetadata([
|
|
160
|
-
|
|
161
|
-
{ fieldName: "amount" },
|
|
162
|
-
])?.message,
|
|
159
|
+
findInvalidFieldMetadata([{ name: "Amount" }, { name: "amount" }])
|
|
160
|
+
?.message,
|
|
163
161
|
).toContain("Duplicate field name");
|
|
164
162
|
expect(
|
|
165
163
|
findInvalidFieldMetadata([
|
|
166
|
-
{
|
|
167
|
-
{
|
|
164
|
+
{ name: "Amount", displayName: "Total" },
|
|
165
|
+
{ name: "Price", displayName: "total" },
|
|
168
166
|
])?.message,
|
|
169
167
|
).toContain("Duplicate field display name");
|
|
170
168
|
});
|
|
171
169
|
|
|
172
170
|
it("rejects a field name matching its entity name", () => {
|
|
173
171
|
expect(
|
|
174
|
-
findInvalidFieldMetadata([{
|
|
175
|
-
?.message,
|
|
172
|
+
findInvalidFieldMetadata([{ name: "invoice" }], "Invoice")?.message,
|
|
176
173
|
).toContain("cannot match entity name");
|
|
177
174
|
});
|
|
178
175
|
|
|
179
176
|
it("rejects more than ten MULTILINE_MAX fields", () => {
|
|
180
177
|
const fields = Array.from({ length: 11 }, (_, i) => ({
|
|
181
|
-
|
|
178
|
+
name: `Body${i}`,
|
|
182
179
|
type: "MULTILINE_MAX",
|
|
183
180
|
}));
|
|
184
181
|
expect(findTooManyMultilineMaxFields(fields)?.message).toContain(
|
|
@@ -191,17 +188,15 @@ describe("findUiBrokenFieldType", () => {
|
|
|
191
188
|
it("returns null for UI-compatible types", () => {
|
|
192
189
|
expect(
|
|
193
190
|
findUiBrokenFieldType([
|
|
194
|
-
{
|
|
195
|
-
{
|
|
196
|
-
{
|
|
191
|
+
{ name: "title", type: "STRING" },
|
|
192
|
+
{ name: "amount", type: "DECIMAL" },
|
|
193
|
+
{ name: "receipt", type: "FILE" },
|
|
197
194
|
]),
|
|
198
195
|
).toBeNull();
|
|
199
196
|
});
|
|
200
197
|
|
|
201
198
|
it("flags INTEGER with a DECIMAL substitution", () => {
|
|
202
|
-
const err = findUiBrokenFieldType([
|
|
203
|
-
{ fieldName: "count", type: "INTEGER" },
|
|
204
|
-
]);
|
|
199
|
+
const err = findUiBrokenFieldType([{ name: "count", type: "INTEGER" }]);
|
|
205
200
|
expect(err?.message).toContain("'INTEGER'");
|
|
206
201
|
expect(err?.instructions).toContain("DECIMAL");
|
|
207
202
|
});
|
|
@@ -215,16 +210,16 @@ describe("findUiBrokenFieldType", () => {
|
|
|
215
210
|
"UUID",
|
|
216
211
|
"DATETIME",
|
|
217
212
|
]) {
|
|
218
|
-
const err = findUiBrokenFieldType([{
|
|
213
|
+
const err = findUiBrokenFieldType([{ name: "x", type }]);
|
|
219
214
|
expect(err, `${type} should be flagged`).not.toBeNull();
|
|
220
215
|
}
|
|
221
216
|
});
|
|
222
217
|
|
|
223
218
|
it("flags the first offender when multiple are present", () => {
|
|
224
219
|
const err = findUiBrokenFieldType([
|
|
225
|
-
{
|
|
226
|
-
{
|
|
227
|
-
{
|
|
220
|
+
{ name: "a", type: "STRING" },
|
|
221
|
+
{ name: "b", type: "FLOAT" },
|
|
222
|
+
{ name: "c", type: "DATETIME" },
|
|
228
223
|
]);
|
|
229
224
|
expect(err?.message).toContain("'b'");
|
|
230
225
|
});
|
|
@@ -471,12 +466,12 @@ describe("findReferenceFolderKeyOnChoiceSet", () => {
|
|
|
471
466
|
expect(
|
|
472
467
|
findReferenceFolderKeyOnChoiceSet([
|
|
473
468
|
{
|
|
474
|
-
|
|
469
|
+
name: "status",
|
|
475
470
|
type: "CHOICE_SET_SINGLE",
|
|
476
471
|
choiceSetId: "cs-1",
|
|
477
472
|
},
|
|
478
473
|
{
|
|
479
|
-
|
|
474
|
+
name: "tags",
|
|
480
475
|
type: "CHOICE_SET_MULTIPLE",
|
|
481
476
|
choiceSetId: "cs-2",
|
|
482
477
|
},
|
|
@@ -488,7 +483,7 @@ describe("findReferenceFolderKeyOnChoiceSet", () => {
|
|
|
488
483
|
expect(
|
|
489
484
|
findReferenceFolderKeyOnChoiceSet([
|
|
490
485
|
{
|
|
491
|
-
|
|
486
|
+
name: "customerId",
|
|
492
487
|
type: "RELATIONSHIP",
|
|
493
488
|
referenceEntityId: "e-1",
|
|
494
489
|
referenceFieldId: "f-1",
|
|
@@ -501,7 +496,7 @@ describe("findReferenceFolderKeyOnChoiceSet", () => {
|
|
|
501
496
|
it("flags referenceFolderKey on CHOICE_SET_SINGLE", () => {
|
|
502
497
|
const err = findReferenceFolderKeyOnChoiceSet([
|
|
503
498
|
{
|
|
504
|
-
|
|
499
|
+
name: "status",
|
|
505
500
|
type: "CHOICE_SET_SINGLE",
|
|
506
501
|
choiceSetId: "cs-1",
|
|
507
502
|
referenceFolderKey: "folder-1",
|
|
@@ -514,7 +509,7 @@ describe("findReferenceFolderKeyOnChoiceSet", () => {
|
|
|
514
509
|
it("flags referenceFolderKey on CHOICE_SET_MULTIPLE", () => {
|
|
515
510
|
const err = findReferenceFolderKeyOnChoiceSet([
|
|
516
511
|
{
|
|
517
|
-
|
|
512
|
+
name: "tags",
|
|
518
513
|
type: "CHOICE_SET_MULTIPLE",
|
|
519
514
|
choiceSetId: "cs-2",
|
|
520
515
|
referenceFolderKey: "folder-1",
|
|
@@ -527,7 +522,7 @@ describe("findReferenceFolderKeyOnChoiceSet", () => {
|
|
|
527
522
|
describe("findReferenceFieldsOnFile", () => {
|
|
528
523
|
it("accepts a FILE field without reference metadata", () => {
|
|
529
524
|
expect(
|
|
530
|
-
findReferenceFieldsOnFile([{
|
|
525
|
+
findReferenceFieldsOnFile([{ name: "receipt", type: "FILE" }]),
|
|
531
526
|
).toBeNull();
|
|
532
527
|
});
|
|
533
528
|
|
|
@@ -540,7 +535,7 @@ describe("findReferenceFieldsOnFile", () => {
|
|
|
540
535
|
]) {
|
|
541
536
|
const err = findReferenceFieldsOnFile([
|
|
542
537
|
{
|
|
543
|
-
|
|
538
|
+
name: "receipt",
|
|
544
539
|
type: "FILE",
|
|
545
540
|
[key]: "value",
|
|
546
541
|
},
|
|
@@ -555,7 +550,7 @@ describe("findReferenceFieldsOnFile", () => {
|
|
|
555
550
|
expect(
|
|
556
551
|
findReferenceFieldsOnFile([
|
|
557
552
|
{
|
|
558
|
-
|
|
553
|
+
name: "customer",
|
|
559
554
|
type: "RELATIONSHIP",
|
|
560
555
|
referenceEntityId: "entity-id",
|
|
561
556
|
referenceFieldId: "field-id",
|
|
@@ -571,30 +566,30 @@ describe("findMissingComplexFieldExtras", () => {
|
|
|
571
566
|
expect(
|
|
572
567
|
findMissingComplexFieldExtras([
|
|
573
568
|
{
|
|
574
|
-
|
|
569
|
+
name: "status",
|
|
575
570
|
type: "CHOICE_SET_SINGLE",
|
|
576
571
|
choiceSetId: "cs-1",
|
|
577
572
|
},
|
|
578
573
|
{
|
|
579
|
-
|
|
574
|
+
name: "tags",
|
|
580
575
|
type: "CHOICE_SET_MULTIPLE",
|
|
581
576
|
choiceSetId: "cs-2",
|
|
582
577
|
},
|
|
583
578
|
{
|
|
584
|
-
|
|
579
|
+
name: "customerId",
|
|
585
580
|
type: "RELATIONSHIP",
|
|
586
581
|
referenceEntityId: "e-1",
|
|
587
582
|
referenceFieldId: "f-1",
|
|
588
583
|
},
|
|
589
|
-
{
|
|
590
|
-
{
|
|
584
|
+
{ name: "receipt", type: "FILE" },
|
|
585
|
+
{ name: "title", type: "STRING" },
|
|
591
586
|
]),
|
|
592
587
|
).toBeNull();
|
|
593
588
|
});
|
|
594
589
|
|
|
595
590
|
it("flags CHOICE_SET_SINGLE without choiceSetId", () => {
|
|
596
591
|
const err = findMissingComplexFieldExtras([
|
|
597
|
-
{
|
|
592
|
+
{ name: "status", type: "CHOICE_SET_SINGLE" },
|
|
598
593
|
]);
|
|
599
594
|
expect(err?.message).toContain("'status'");
|
|
600
595
|
expect(err?.message).toContain("requires 'choiceSetId'");
|
|
@@ -602,7 +597,7 @@ describe("findMissingComplexFieldExtras", () => {
|
|
|
602
597
|
|
|
603
598
|
it("flags CHOICE_SET_MULTIPLE with empty-string choiceSetId", () => {
|
|
604
599
|
const err = findMissingComplexFieldExtras([
|
|
605
|
-
{
|
|
600
|
+
{ name: "tags", type: "CHOICE_SET_MULTIPLE", choiceSetId: "" },
|
|
606
601
|
]);
|
|
607
602
|
expect(err).not.toBeNull();
|
|
608
603
|
});
|
|
@@ -610,7 +605,7 @@ describe("findMissingComplexFieldExtras", () => {
|
|
|
610
605
|
it("flags RELATIONSHIP without referenceEntityId", () => {
|
|
611
606
|
const err = findMissingComplexFieldExtras([
|
|
612
607
|
{
|
|
613
|
-
|
|
608
|
+
name: "customerId",
|
|
614
609
|
type: "RELATIONSHIP",
|
|
615
610
|
referenceFieldId: "f-1",
|
|
616
611
|
},
|
|
@@ -621,7 +616,7 @@ describe("findMissingComplexFieldExtras", () => {
|
|
|
621
616
|
it("flags RELATIONSHIP without referenceFieldId", () => {
|
|
622
617
|
const err = findMissingComplexFieldExtras([
|
|
623
618
|
{
|
|
624
|
-
|
|
619
|
+
name: "customerId",
|
|
625
620
|
type: "RELATIONSHIP",
|
|
626
621
|
referenceEntityId: "e-1",
|
|
627
622
|
},
|
|
@@ -631,8 +626,8 @@ describe("findMissingComplexFieldExtras", () => {
|
|
|
631
626
|
|
|
632
627
|
it("stops at the first offender", () => {
|
|
633
628
|
const err = findMissingComplexFieldExtras([
|
|
634
|
-
{
|
|
635
|
-
{
|
|
629
|
+
{ name: "ok", type: "CHOICE_SET_SINGLE", choiceSetId: "cs-1" },
|
|
630
|
+
{ name: "bad", type: "RELATIONSHIP" },
|
|
636
631
|
]);
|
|
637
632
|
expect(err?.message).toContain("'bad'");
|
|
638
633
|
});
|
|
@@ -642,19 +637,19 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
642
637
|
it("returns null when constraints match their type + ranges", () => {
|
|
643
638
|
expect(
|
|
644
639
|
findInvalidFieldConstraints([
|
|
645
|
-
{
|
|
640
|
+
{ name: "title", type: "STRING", lengthLimit: 500 },
|
|
646
641
|
{
|
|
647
|
-
|
|
642
|
+
name: "body",
|
|
648
643
|
type: "MULTILINE_TEXT",
|
|
649
644
|
lengthLimit: 8000,
|
|
650
645
|
},
|
|
651
646
|
{
|
|
652
|
-
|
|
647
|
+
name: "note",
|
|
653
648
|
type: "MULTILINE_MAX",
|
|
654
649
|
lengthLimit: 100000,
|
|
655
650
|
},
|
|
656
651
|
{
|
|
657
|
-
|
|
652
|
+
name: "price",
|
|
658
653
|
type: "DECIMAL",
|
|
659
654
|
decimalPrecision: 2,
|
|
660
655
|
minValue: 0,
|
|
@@ -666,7 +661,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
666
661
|
|
|
667
662
|
it("rejects lengthLimit on a non-text type", () => {
|
|
668
663
|
const err = findInvalidFieldConstraints([
|
|
669
|
-
{
|
|
664
|
+
{ name: "score", type: "DECIMAL", lengthLimit: 100 },
|
|
670
665
|
]);
|
|
671
666
|
expect(err?.message).toContain(
|
|
672
667
|
"'DECIMAL' does not accept 'lengthLimit'",
|
|
@@ -675,7 +670,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
675
670
|
|
|
676
671
|
it("rejects lengthLimit outside the STRING range", () => {
|
|
677
672
|
const err = findInvalidFieldConstraints([
|
|
678
|
-
{
|
|
673
|
+
{ name: "title", type: "STRING", lengthLimit: 5000 },
|
|
679
674
|
]);
|
|
680
675
|
expect(err?.message).toContain("outside the allowed range");
|
|
681
676
|
expect(err?.instructions).toContain("1 and 4000");
|
|
@@ -683,7 +678,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
683
678
|
|
|
684
679
|
it("rejects lengthLimit outside the MULTILINE_MAX byte budget", () => {
|
|
685
680
|
const err = findInvalidFieldConstraints([
|
|
686
|
-
{
|
|
681
|
+
{ name: "body", type: "MULTILINE_MAX", lengthLimit: 200000 },
|
|
687
682
|
]);
|
|
688
683
|
expect(err?.message).toContain("MULTILINE_MAX");
|
|
689
684
|
expect(err?.instructions).toContain("131072");
|
|
@@ -691,7 +686,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
691
686
|
|
|
692
687
|
it("rejects maxValue on non-DECIMAL", () => {
|
|
693
688
|
const err = findInvalidFieldConstraints([
|
|
694
|
-
{
|
|
689
|
+
{ name: "title", type: "STRING", maxValue: 100 },
|
|
695
690
|
]);
|
|
696
691
|
expect(err?.message).toContain("does not accept 'maxValue'");
|
|
697
692
|
});
|
|
@@ -700,7 +695,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
700
695
|
expect(
|
|
701
696
|
findInvalidFieldConstraints([
|
|
702
697
|
{
|
|
703
|
-
|
|
698
|
+
name: "fixed",
|
|
704
699
|
type: "DECIMAL",
|
|
705
700
|
minValue: 100,
|
|
706
701
|
maxValue: 100,
|
|
@@ -709,7 +704,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
709
704
|
).toBeNull();
|
|
710
705
|
const err = findInvalidFieldConstraints([
|
|
711
706
|
{
|
|
712
|
-
|
|
707
|
+
name: "price",
|
|
713
708
|
type: "DECIMAL",
|
|
714
709
|
minValue: 101,
|
|
715
710
|
maxValue: 100,
|
|
@@ -721,13 +716,13 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
721
716
|
it("rejects fractional length limits and out-of-range numeric bounds", () => {
|
|
722
717
|
expect(
|
|
723
718
|
findInvalidFieldConstraints([
|
|
724
|
-
{
|
|
719
|
+
{ name: "title", type: "STRING", lengthLimit: 1.5 },
|
|
725
720
|
])?.message,
|
|
726
721
|
).toContain("integer");
|
|
727
722
|
expect(
|
|
728
723
|
findInvalidFieldConstraints([
|
|
729
724
|
{
|
|
730
|
-
|
|
725
|
+
name: "amount",
|
|
731
726
|
type: "DECIMAL",
|
|
732
727
|
maxValue: 9_007_199_254_740_992,
|
|
733
728
|
},
|
|
@@ -737,7 +732,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
737
732
|
|
|
738
733
|
it("rejects decimalPrecision outside 0-10", () => {
|
|
739
734
|
const err = findInvalidFieldConstraints([
|
|
740
|
-
{
|
|
735
|
+
{ name: "price", type: "DECIMAL", decimalPrecision: 15 },
|
|
741
736
|
]);
|
|
742
737
|
expect(err?.message).toContain("decimalPrecision");
|
|
743
738
|
expect(err?.instructions).toContain("0 and 10");
|
|
@@ -746,7 +741,7 @@ describe("findInvalidFieldConstraints", () => {
|
|
|
746
741
|
it("rejects decimalPrecision on non-DECIMAL", () => {
|
|
747
742
|
const err = findInvalidFieldConstraints([
|
|
748
743
|
{
|
|
749
|
-
|
|
744
|
+
name: "title",
|
|
750
745
|
type: "STRING",
|
|
751
746
|
decimalPrecision: 2,
|
|
752
747
|
},
|
package/src/utils/validate.ts
CHANGED
|
@@ -326,7 +326,7 @@ export function findInvalidFieldName(
|
|
|
326
326
|
): ValidationError | null {
|
|
327
327
|
for (const f of fields) {
|
|
328
328
|
if (typeof f !== "object" || f === null) continue;
|
|
329
|
-
const fieldName = (f as Record<string, unknown>).
|
|
329
|
+
const fieldName = (f as Record<string, unknown>).name;
|
|
330
330
|
if (typeof fieldName !== "string") continue;
|
|
331
331
|
const err = validateName(fieldName, "field");
|
|
332
332
|
if (err) return err;
|
|
@@ -390,19 +390,18 @@ export function findInvalidFieldMetadata(
|
|
|
390
390
|
for (const f of fields) {
|
|
391
391
|
if (typeof f !== "object" || f === null) continue;
|
|
392
392
|
const rec = f as Record<string, unknown>;
|
|
393
|
-
const fieldName =
|
|
394
|
-
typeof rec.fieldName === "string" ? rec.fieldName : "<field>";
|
|
393
|
+
const fieldName = typeof rec.name === "string" ? rec.name : "<field>";
|
|
395
394
|
const metadataErr = validateMetadataLengths(
|
|
396
395
|
rec,
|
|
397
396
|
`Field '${fieldName}'`,
|
|
398
397
|
);
|
|
399
398
|
if (metadataErr) return metadataErr;
|
|
400
399
|
|
|
401
|
-
if (typeof rec.
|
|
402
|
-
const normalizedName = rec.
|
|
400
|
+
if (typeof rec.name !== "string") continue;
|
|
401
|
+
const normalizedName = rec.name.toLowerCase();
|
|
403
402
|
if (names.has(normalizedName)) {
|
|
404
403
|
return {
|
|
405
|
-
message: `Duplicate field name '${rec.
|
|
404
|
+
message: `Duplicate field name '${rec.name}' (case-insensitive)`,
|
|
406
405
|
instructions:
|
|
407
406
|
"Every field in one create/addFields batch must have a unique name, ignoring case.",
|
|
408
407
|
};
|
|
@@ -414,7 +413,7 @@ export function findInvalidFieldMetadata(
|
|
|
414
413
|
normalizedName === entityName.toLowerCase()
|
|
415
414
|
) {
|
|
416
415
|
return {
|
|
417
|
-
message: `Field name '${rec.
|
|
416
|
+
message: `Field name '${rec.name}' cannot match entity name '${entityName}'`,
|
|
418
417
|
instructions:
|
|
419
418
|
"Entity and field names are compared case-insensitively. Pick a different field name.",
|
|
420
419
|
};
|
|
@@ -423,7 +422,7 @@ export function findInvalidFieldMetadata(
|
|
|
423
422
|
const effectiveDisplayName =
|
|
424
423
|
typeof rec.displayName === "string" && rec.displayName.length > 0
|
|
425
424
|
? rec.displayName
|
|
426
|
-
: rec.
|
|
425
|
+
: rec.name;
|
|
427
426
|
const normalizedDisplayName = effectiveDisplayName.toLowerCase();
|
|
428
427
|
if (displayNames.has(normalizedDisplayName)) {
|
|
429
428
|
return {
|
|
@@ -471,7 +470,7 @@ export function findUiBrokenFieldType(
|
|
|
471
470
|
if (typeof type !== "string") continue;
|
|
472
471
|
if (UI_BROKEN_FIELD_TYPES.has(type)) {
|
|
473
472
|
const fieldName =
|
|
474
|
-
typeof rec.
|
|
473
|
+
typeof rec.name === "string" ? rec.name : "<field>";
|
|
475
474
|
const substitute = UI_BROKEN_SUBSTITUTIONS[type];
|
|
476
475
|
return {
|
|
477
476
|
message: `Field '${fieldName}' uses type '${type}' which the Data Fabric UI cannot render`,
|
|
@@ -499,7 +498,7 @@ export function findReferenceFolderKeyOnChoiceSet(
|
|
|
499
498
|
rec.referenceFolderKey !== undefined
|
|
500
499
|
) {
|
|
501
500
|
const fieldName =
|
|
502
|
-
typeof rec.
|
|
501
|
+
typeof rec.name === "string" ? rec.name : "<field>";
|
|
503
502
|
return {
|
|
504
503
|
message: `CHOICE_SET_* field '${fieldName}' must not pass 'referenceFolderKey'`,
|
|
505
504
|
instructions:
|
|
@@ -534,8 +533,7 @@ export function findReferenceFieldsOnFile(
|
|
|
534
533
|
);
|
|
535
534
|
if (invalidKeys.length === 0) continue;
|
|
536
535
|
|
|
537
|
-
const fieldName =
|
|
538
|
-
typeof rec.fieldName === "string" ? rec.fieldName : "<field>";
|
|
536
|
+
const fieldName = typeof rec.name === "string" ? rec.name : "<field>";
|
|
539
537
|
return {
|
|
540
538
|
message: `FILE field '${fieldName}' must not pass reference fields`,
|
|
541
539
|
instructions: `FILE fields are auto-wired by the platform. Drop ${invalidKeys.map((key) => `'${key}'`).join(", ")} from the field definition.`,
|
|
@@ -556,8 +554,7 @@ export function findMissingComplexFieldExtras(
|
|
|
556
554
|
const rec = f as Record<string, unknown>;
|
|
557
555
|
const type = rec.type;
|
|
558
556
|
if (typeof type !== "string") continue;
|
|
559
|
-
const fieldName =
|
|
560
|
-
typeof rec.fieldName === "string" ? rec.fieldName : "<field>";
|
|
557
|
+
const fieldName = typeof rec.name === "string" ? rec.name : "<field>";
|
|
561
558
|
|
|
562
559
|
if (type === "CHOICE_SET_SINGLE" || type === "CHOICE_SET_MULTIPLE") {
|
|
563
560
|
if (
|
|
@@ -618,8 +615,7 @@ export function findInvalidFieldConstraints(
|
|
|
618
615
|
const rec = f as Record<string, unknown>;
|
|
619
616
|
const type = rec.type;
|
|
620
617
|
if (typeof type !== "string") continue;
|
|
621
|
-
const fieldName =
|
|
622
|
-
typeof rec.fieldName === "string" ? rec.fieldName : "<field>";
|
|
618
|
+
const fieldName = typeof rec.name === "string" ? rec.name : "<field>";
|
|
623
619
|
|
|
624
620
|
if (rec.lengthLimit !== undefined) {
|
|
625
621
|
if (
|