@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
|
@@ -301,7 +301,7 @@ describe("entities create", () => {
|
|
|
301
301
|
const sdk = mockSdk();
|
|
302
302
|
vi.mocked(sdk.entities.create).mockResolvedValue("new-entity-id");
|
|
303
303
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
304
|
-
fields: [{
|
|
304
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
305
305
|
});
|
|
306
306
|
|
|
307
307
|
const program = buildProgram();
|
|
@@ -312,12 +312,12 @@ describe("entities create", () => {
|
|
|
312
312
|
"create",
|
|
313
313
|
"MyEntity",
|
|
314
314
|
"--body",
|
|
315
|
-
'{"fields":[{"
|
|
315
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
316
316
|
]);
|
|
317
317
|
|
|
318
318
|
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
319
319
|
"MyEntity",
|
|
320
|
-
[{
|
|
320
|
+
[{ name: "title", type: "STRING" }],
|
|
321
321
|
undefined,
|
|
322
322
|
);
|
|
323
323
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -329,13 +329,58 @@ describe("entities create", () => {
|
|
|
329
329
|
);
|
|
330
330
|
});
|
|
331
331
|
|
|
332
|
+
it("should normalize fields copied from entities get read shape on create", async () => {
|
|
333
|
+
const sdk = mockSdk();
|
|
334
|
+
vi.mocked(sdk.entities.create).mockResolvedValue("roundtrip-id");
|
|
335
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
336
|
+
fields: [
|
|
337
|
+
{
|
|
338
|
+
ID: "f1000000-0000-0000-0000-000000000001",
|
|
339
|
+
Name: "note",
|
|
340
|
+
DisplayName: "Note",
|
|
341
|
+
FieldDataType: { Name: "STRING", LengthLimit: 200 },
|
|
342
|
+
IsRequired: true,
|
|
343
|
+
System: false,
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const program = buildProgram();
|
|
349
|
+
await program.parseAsync([
|
|
350
|
+
"node",
|
|
351
|
+
"test",
|
|
352
|
+
"entities",
|
|
353
|
+
"create",
|
|
354
|
+
"MyEntity",
|
|
355
|
+
"--body",
|
|
356
|
+
'{"fields":[{"ID":"f1000000-0000-0000-0000-000000000001","Name":"note","DisplayName":"Note","FieldDataType":{"Name":"STRING","LengthLimit":200},"IsRequired":true,"System":false}]}',
|
|
357
|
+
]);
|
|
358
|
+
|
|
359
|
+
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
360
|
+
"MyEntity",
|
|
361
|
+
[
|
|
362
|
+
{
|
|
363
|
+
name: "note",
|
|
364
|
+
displayName: "Note",
|
|
365
|
+
type: "STRING",
|
|
366
|
+
lengthLimit: 200,
|
|
367
|
+
isRequired: true,
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
undefined,
|
|
371
|
+
);
|
|
372
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
373
|
+
expect.objectContaining({ Code: "EntityCreated" }),
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
332
377
|
it("should accept MULTILINE_MAX as a field type on create", async () => {
|
|
333
378
|
const sdk = mockSdk();
|
|
334
379
|
vi.mocked(sdk.entities.create).mockResolvedValue("doc-entity-id");
|
|
335
380
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
336
381
|
fields: [
|
|
337
|
-
{
|
|
338
|
-
{
|
|
382
|
+
{ name: "title", type: "STRING" },
|
|
383
|
+
{ name: "body", type: "MULTILINE_MAX" },
|
|
339
384
|
],
|
|
340
385
|
});
|
|
341
386
|
|
|
@@ -347,14 +392,14 @@ describe("entities create", () => {
|
|
|
347
392
|
"create",
|
|
348
393
|
"Document",
|
|
349
394
|
"--body",
|
|
350
|
-
'{"fields":[{"
|
|
395
|
+
'{"fields":[{"name":"title","type":"STRING"},{"name":"body","type":"MULTILINE_MAX"}]}',
|
|
351
396
|
]);
|
|
352
397
|
|
|
353
398
|
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
354
399
|
"Document",
|
|
355
400
|
[
|
|
356
|
-
{
|
|
357
|
-
{
|
|
401
|
+
{ name: "title", type: "STRING" },
|
|
402
|
+
{ name: "body", type: "MULTILINE_MAX" },
|
|
358
403
|
],
|
|
359
404
|
undefined,
|
|
360
405
|
);
|
|
@@ -371,7 +416,7 @@ describe("entities create", () => {
|
|
|
371
416
|
const sdk = mockSdk();
|
|
372
417
|
vi.mocked(sdk.entities.create).mockResolvedValue("entity-with-opts");
|
|
373
418
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
374
|
-
fields: [{
|
|
419
|
+
fields: [{ name: "amount", type: "DECIMAL" }],
|
|
375
420
|
displayName: "My Entity",
|
|
376
421
|
description: "A test entity",
|
|
377
422
|
isRbacEnabled: true,
|
|
@@ -385,12 +430,12 @@ describe("entities create", () => {
|
|
|
385
430
|
"create",
|
|
386
431
|
"MyEntity",
|
|
387
432
|
"--body",
|
|
388
|
-
'{"fields":[{"
|
|
433
|
+
'{"fields":[{"name":"amount","type":"DECIMAL"}],"displayName":"My Entity","description":"A test entity","isRbacEnabled":true}',
|
|
389
434
|
]);
|
|
390
435
|
|
|
391
436
|
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
392
437
|
"MyEntity",
|
|
393
|
-
[{
|
|
438
|
+
[{ name: "amount", type: "DECIMAL" }],
|
|
394
439
|
expect.objectContaining({
|
|
395
440
|
displayName: "My Entity",
|
|
396
441
|
description: "A test entity",
|
|
@@ -405,11 +450,40 @@ describe("entities create", () => {
|
|
|
405
450
|
);
|
|
406
451
|
});
|
|
407
452
|
|
|
453
|
+
it("should forward isAnalyticsEnabled to create", async () => {
|
|
454
|
+
const sdk = mockSdk();
|
|
455
|
+
vi.mocked(sdk.entities.create).mockResolvedValue("analytics-entity");
|
|
456
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
457
|
+
fields: [{ name: "amount", type: "DECIMAL" }],
|
|
458
|
+
isAnalyticsEnabled: true,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const program = buildProgram();
|
|
462
|
+
await program.parseAsync([
|
|
463
|
+
"node",
|
|
464
|
+
"test",
|
|
465
|
+
"entities",
|
|
466
|
+
"create",
|
|
467
|
+
"MyEntity",
|
|
468
|
+
"--body",
|
|
469
|
+
'{"fields":[{"name":"amount","type":"DECIMAL"}],"isAnalyticsEnabled":true}',
|
|
470
|
+
]);
|
|
471
|
+
|
|
472
|
+
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
473
|
+
"MyEntity",
|
|
474
|
+
[{ name: "amount", type: "DECIMAL" }],
|
|
475
|
+
expect.objectContaining({ isAnalyticsEnabled: true }),
|
|
476
|
+
);
|
|
477
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
478
|
+
expect.objectContaining({ Code: "EntityCreated" }),
|
|
479
|
+
);
|
|
480
|
+
});
|
|
481
|
+
|
|
408
482
|
it("should create entity with only displayName (partial options)", async () => {
|
|
409
483
|
const sdk = mockSdk();
|
|
410
484
|
vi.mocked(sdk.entities.create).mockResolvedValue("entity-partial");
|
|
411
485
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
412
|
-
fields: [{
|
|
486
|
+
fields: [{ name: "name", type: "STRING" }],
|
|
413
487
|
displayName: "Partial Entity",
|
|
414
488
|
});
|
|
415
489
|
|
|
@@ -421,7 +495,7 @@ describe("entities create", () => {
|
|
|
421
495
|
"create",
|
|
422
496
|
"PartialEntity",
|
|
423
497
|
"--body",
|
|
424
|
-
'{"fields":[{"
|
|
498
|
+
'{"fields":[{"name":"name","type":"STRING"}],"displayName":"Partial Entity"}',
|
|
425
499
|
]);
|
|
426
500
|
|
|
427
501
|
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
@@ -441,7 +515,7 @@ describe("entities create", () => {
|
|
|
441
515
|
);
|
|
442
516
|
const fields = [
|
|
443
517
|
{
|
|
444
|
-
|
|
518
|
+
name: "opType",
|
|
445
519
|
type: "CHOICE_SET_SINGLE",
|
|
446
520
|
choiceSetId: "9db00b66-9952-f111-8ef3-6045bd07956d",
|
|
447
521
|
isRequired: true,
|
|
@@ -464,7 +538,7 @@ describe("entities create", () => {
|
|
|
464
538
|
"CliTestExpense",
|
|
465
539
|
expect.arrayContaining([
|
|
466
540
|
expect.objectContaining({
|
|
467
|
-
|
|
541
|
+
name: "opType",
|
|
468
542
|
type: "CHOICE_SET_SINGLE",
|
|
469
543
|
choiceSetId: "9db00b66-9952-f111-8ef3-6045bd07956d",
|
|
470
544
|
}),
|
|
@@ -481,7 +555,7 @@ describe("entities create", () => {
|
|
|
481
555
|
vi.mocked(sdk.entities.create).mockResolvedValue("entity-choice-multi");
|
|
482
556
|
const fields = [
|
|
483
557
|
{
|
|
484
|
-
|
|
558
|
+
name: "multiOpType",
|
|
485
559
|
type: "CHOICE_SET_MULTIPLE",
|
|
486
560
|
choiceSetId: "9db00b66-9952-f111-8ef3-6045bd07956d",
|
|
487
561
|
},
|
|
@@ -503,7 +577,7 @@ describe("entities create", () => {
|
|
|
503
577
|
"CliTestExpense",
|
|
504
578
|
expect.arrayContaining([
|
|
505
579
|
expect.objectContaining({
|
|
506
|
-
|
|
580
|
+
name: "multiOpType",
|
|
507
581
|
type: "CHOICE_SET_MULTIPLE",
|
|
508
582
|
choiceSetId: "9db00b66-9952-f111-8ef3-6045bd07956d",
|
|
509
583
|
}),
|
|
@@ -520,7 +594,7 @@ describe("entities create", () => {
|
|
|
520
594
|
vi.mocked(sdk.entities.create).mockResolvedValue("entity-rel");
|
|
521
595
|
const fields = [
|
|
522
596
|
{
|
|
523
|
-
|
|
597
|
+
name: "rel1",
|
|
524
598
|
type: "RELATIONSHIP",
|
|
525
599
|
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
526
600
|
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
@@ -543,7 +617,7 @@ describe("entities create", () => {
|
|
|
543
617
|
"CliTestExpense",
|
|
544
618
|
expect.arrayContaining([
|
|
545
619
|
expect.objectContaining({
|
|
546
|
-
|
|
620
|
+
name: "rel1",
|
|
547
621
|
type: "RELATIONSHIP",
|
|
548
622
|
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
549
623
|
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
@@ -561,7 +635,7 @@ describe("entities create", () => {
|
|
|
561
635
|
vi.mocked(sdk.entities.create).mockResolvedValue("entity-xfolder-rel");
|
|
562
636
|
const fields = [
|
|
563
637
|
{
|
|
564
|
-
|
|
638
|
+
name: "purchaseOrder",
|
|
565
639
|
type: "RELATIONSHIP",
|
|
566
640
|
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
567
641
|
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
@@ -587,7 +661,7 @@ describe("entities create", () => {
|
|
|
587
661
|
"OrderLine",
|
|
588
662
|
expect.arrayContaining([
|
|
589
663
|
expect.objectContaining({
|
|
590
|
-
|
|
664
|
+
name: "purchaseOrder",
|
|
591
665
|
type: "RELATIONSHIP",
|
|
592
666
|
referenceFolderKey: "f1000000-0000-0000-0000-000000000060",
|
|
593
667
|
}),
|
|
@@ -671,7 +745,7 @@ describe("entities create", () => {
|
|
|
671
745
|
expect(process.exitCode).toBe(1);
|
|
672
746
|
});
|
|
673
747
|
|
|
674
|
-
it("should error when a field is missing
|
|
748
|
+
it("should error when a field is missing name", async () => {
|
|
675
749
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
676
750
|
fields: [{ type: "STRING" }],
|
|
677
751
|
});
|
|
@@ -690,7 +764,7 @@ describe("entities create", () => {
|
|
|
690
764
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
691
765
|
expect.objectContaining({
|
|
692
766
|
Result: "Failure",
|
|
693
|
-
Message: "Each field must include a '
|
|
767
|
+
Message: "Each field must include a 'name' string",
|
|
694
768
|
}),
|
|
695
769
|
);
|
|
696
770
|
expect(process.exitCode).toBe(1);
|
|
@@ -698,7 +772,7 @@ describe("entities create", () => {
|
|
|
698
772
|
|
|
699
773
|
it("should error when a field has an invalid type", async () => {
|
|
700
774
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
701
|
-
fields: [{
|
|
775
|
+
fields: [{ name: "title", type: "text" }],
|
|
702
776
|
});
|
|
703
777
|
|
|
704
778
|
const program = buildProgram();
|
|
@@ -709,7 +783,7 @@ describe("entities create", () => {
|
|
|
709
783
|
"create",
|
|
710
784
|
"MyEntity",
|
|
711
785
|
"--body",
|
|
712
|
-
'{"fields":[{"
|
|
786
|
+
'{"fields":[{"name":"title","type":"text"}]}',
|
|
713
787
|
]);
|
|
714
788
|
|
|
715
789
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
@@ -724,7 +798,7 @@ describe("entities create", () => {
|
|
|
724
798
|
it("should bail when SDK connection fails on create", async () => {
|
|
725
799
|
const sdk = mockSdk();
|
|
726
800
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
727
|
-
fields: [{
|
|
801
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
728
802
|
});
|
|
729
803
|
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
730
804
|
|
|
@@ -736,7 +810,7 @@ describe("entities create", () => {
|
|
|
736
810
|
"create",
|
|
737
811
|
"MyEntity",
|
|
738
812
|
"--body",
|
|
739
|
-
'{"fields":[{"
|
|
813
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
740
814
|
]);
|
|
741
815
|
|
|
742
816
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -749,7 +823,7 @@ describe("entities create", () => {
|
|
|
749
823
|
new Error("Entity already exists"),
|
|
750
824
|
);
|
|
751
825
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
752
|
-
fields: [{
|
|
826
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
753
827
|
});
|
|
754
828
|
|
|
755
829
|
const program = buildProgram();
|
|
@@ -760,7 +834,7 @@ describe("entities create", () => {
|
|
|
760
834
|
"create",
|
|
761
835
|
"MyEntity",
|
|
762
836
|
"--body",
|
|
763
|
-
'{"fields":[{"
|
|
837
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
764
838
|
]);
|
|
765
839
|
|
|
766
840
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
@@ -775,7 +849,7 @@ describe("entities create", () => {
|
|
|
775
849
|
it("should reject an entity name that starts with a digit", async () => {
|
|
776
850
|
const sdk = mockSdk();
|
|
777
851
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
778
|
-
fields: [{
|
|
852
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
779
853
|
});
|
|
780
854
|
|
|
781
855
|
const program = buildProgram();
|
|
@@ -786,7 +860,7 @@ describe("entities create", () => {
|
|
|
786
860
|
"create",
|
|
787
861
|
"1Bad",
|
|
788
862
|
"--body",
|
|
789
|
-
'{"fields":[{"
|
|
863
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
790
864
|
]);
|
|
791
865
|
|
|
792
866
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -802,7 +876,7 @@ describe("entities create", () => {
|
|
|
802
876
|
it("should reject an entity name that is a reserved keyword", async () => {
|
|
803
877
|
const sdk = mockSdk();
|
|
804
878
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
805
|
-
fields: [{
|
|
879
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
806
880
|
});
|
|
807
881
|
|
|
808
882
|
const program = buildProgram();
|
|
@@ -813,7 +887,7 @@ describe("entities create", () => {
|
|
|
813
887
|
"create",
|
|
814
888
|
"Class",
|
|
815
889
|
"--body",
|
|
816
|
-
'{"fields":[{"
|
|
890
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
817
891
|
]);
|
|
818
892
|
|
|
819
893
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -829,7 +903,7 @@ describe("entities create", () => {
|
|
|
829
903
|
it("should reject an entity name that is a confirmed SQL keyword", async () => {
|
|
830
904
|
const sdk = mockSdk();
|
|
831
905
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
832
|
-
fields: [{
|
|
906
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
833
907
|
});
|
|
834
908
|
|
|
835
909
|
const program = buildProgram();
|
|
@@ -840,7 +914,7 @@ describe("entities create", () => {
|
|
|
840
914
|
"create",
|
|
841
915
|
"Order",
|
|
842
916
|
"--body",
|
|
843
|
-
'{"fields":[{"
|
|
917
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
844
918
|
]);
|
|
845
919
|
|
|
846
920
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -853,10 +927,10 @@ describe("entities create", () => {
|
|
|
853
927
|
expect(process.exitCode).toBe(1);
|
|
854
928
|
});
|
|
855
929
|
|
|
856
|
-
it("should reject a
|
|
930
|
+
it("should reject a name that is a reserved system field", async () => {
|
|
857
931
|
const sdk = mockSdk();
|
|
858
932
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
859
|
-
fields: [{
|
|
933
|
+
fields: [{ name: "Id", type: "STRING" }],
|
|
860
934
|
});
|
|
861
935
|
|
|
862
936
|
const program = buildProgram();
|
|
@@ -867,7 +941,7 @@ describe("entities create", () => {
|
|
|
867
941
|
"create",
|
|
868
942
|
"MyEntity",
|
|
869
943
|
"--body",
|
|
870
|
-
'{"fields":[{"
|
|
944
|
+
'{"fields":[{"name":"Id","type":"STRING"}]}',
|
|
871
945
|
]);
|
|
872
946
|
|
|
873
947
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -885,7 +959,7 @@ describe("entities create", () => {
|
|
|
885
959
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
886
960
|
fields: [
|
|
887
961
|
{
|
|
888
|
-
|
|
962
|
+
name: "receipt",
|
|
889
963
|
type: "FILE",
|
|
890
964
|
referenceFolderKey: "folder-id",
|
|
891
965
|
},
|
|
@@ -900,7 +974,7 @@ describe("entities create", () => {
|
|
|
900
974
|
"create",
|
|
901
975
|
"MyEntity",
|
|
902
976
|
"--body",
|
|
903
|
-
'{"fields":[{"
|
|
977
|
+
'{"fields":[{"name":"receipt","type":"FILE","referenceFolderKey":"folder-id"}]}',
|
|
904
978
|
]);
|
|
905
979
|
|
|
906
980
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -918,7 +992,7 @@ describe("entities create", () => {
|
|
|
918
992
|
it("should reject a UI-broken field type", async () => {
|
|
919
993
|
const sdk = mockSdk();
|
|
920
994
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
921
|
-
fields: [{
|
|
995
|
+
fields: [{ name: "count", type: "INTEGER" }],
|
|
922
996
|
});
|
|
923
997
|
|
|
924
998
|
const program = buildProgram();
|
|
@@ -929,7 +1003,7 @@ describe("entities create", () => {
|
|
|
929
1003
|
"create",
|
|
930
1004
|
"MyEntity",
|
|
931
1005
|
"--body",
|
|
932
|
-
'{"fields":[{"
|
|
1006
|
+
'{"fields":[{"name":"count","type":"INTEGER"}]}',
|
|
933
1007
|
]);
|
|
934
1008
|
|
|
935
1009
|
expect(sdk.entities.create).not.toHaveBeenCalled();
|
|
@@ -952,10 +1026,25 @@ describe("entities update", () => {
|
|
|
952
1026
|
process.exitCode = undefined;
|
|
953
1027
|
});
|
|
954
1028
|
|
|
1029
|
+
it("should document update field shapes in --help", () => {
|
|
1030
|
+
const program = buildProgram();
|
|
1031
|
+
const cmd = program.commands.find((c) => c.name() === "entities");
|
|
1032
|
+
const updateCmd = cmd?.commands.find((c) => c.name() === "update");
|
|
1033
|
+
expect(updateCmd).toBeDefined();
|
|
1034
|
+
|
|
1035
|
+
const help = updateCmd?.helpInformation() ?? "";
|
|
1036
|
+
expect(help).toContain("addFields [{name,type,...}]");
|
|
1037
|
+
expect(help).toContain("updateFields [{id,...}]");
|
|
1038
|
+
expect(help).toContain("removeFields [{name}]");
|
|
1039
|
+
expect(help.replace(/\s+/g, " ")).toContain(
|
|
1040
|
+
"Do not paste a full copied field object into updateFields",
|
|
1041
|
+
);
|
|
1042
|
+
});
|
|
1043
|
+
|
|
955
1044
|
it("should update entity by adding new fields (addFields)", async () => {
|
|
956
1045
|
const sdk = mockSdk();
|
|
957
1046
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
958
|
-
addFields: [{
|
|
1047
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
959
1048
|
});
|
|
960
1049
|
|
|
961
1050
|
const program = buildProgram();
|
|
@@ -966,13 +1055,13 @@ describe("entities update", () => {
|
|
|
966
1055
|
"update",
|
|
967
1056
|
"entity-id",
|
|
968
1057
|
"--body",
|
|
969
|
-
'{"addFields":[{"
|
|
1058
|
+
'{"addFields":[{"name":"newField","type":"STRING"}]}',
|
|
970
1059
|
]);
|
|
971
1060
|
|
|
972
1061
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
973
1062
|
"entity-id",
|
|
974
1063
|
expect.objectContaining({
|
|
975
|
-
addFields: [{
|
|
1064
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
976
1065
|
}),
|
|
977
1066
|
);
|
|
978
1067
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -984,11 +1073,167 @@ describe("entities update", () => {
|
|
|
984
1073
|
);
|
|
985
1074
|
});
|
|
986
1075
|
|
|
1076
|
+
it("should normalize addFields copied from entities get read shape", async () => {
|
|
1077
|
+
const sdk = mockSdk();
|
|
1078
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1079
|
+
addFields: [
|
|
1080
|
+
{
|
|
1081
|
+
Name: "note",
|
|
1082
|
+
DisplayName: "Note",
|
|
1083
|
+
FieldDataType: {
|
|
1084
|
+
Name: "STRING",
|
|
1085
|
+
LengthLimit: 200,
|
|
1086
|
+
},
|
|
1087
|
+
IsRequired: true,
|
|
1088
|
+
},
|
|
1089
|
+
],
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
const program = buildProgram();
|
|
1093
|
+
await program.parseAsync([
|
|
1094
|
+
"node",
|
|
1095
|
+
"test",
|
|
1096
|
+
"entities",
|
|
1097
|
+
"update",
|
|
1098
|
+
"entity-id",
|
|
1099
|
+
"--body",
|
|
1100
|
+
'{"addFields":[{"Name":"note","DisplayName":"Note","FieldDataType":{"Name":"STRING","LengthLimit":200},"IsRequired":true}]}',
|
|
1101
|
+
]);
|
|
1102
|
+
|
|
1103
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1104
|
+
"entity-id",
|
|
1105
|
+
expect.objectContaining({
|
|
1106
|
+
addFields: [
|
|
1107
|
+
{
|
|
1108
|
+
name: "note",
|
|
1109
|
+
displayName: "Note",
|
|
1110
|
+
type: "STRING",
|
|
1111
|
+
lengthLimit: 200,
|
|
1112
|
+
isRequired: true,
|
|
1113
|
+
},
|
|
1114
|
+
],
|
|
1115
|
+
}),
|
|
1116
|
+
);
|
|
1117
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1118
|
+
expect.objectContaining({ Code: "EntityUpdated" }),
|
|
1119
|
+
);
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
it("should normalize a copied RELATIONSHIP field into referenceEntityId and referenceFieldId", async () => {
|
|
1123
|
+
const sdk = mockSdk();
|
|
1124
|
+
// Shape a user copies out of `df entities get`: the SDK returns
|
|
1125
|
+
// referenceEntity/referenceField objects and the formatter PascalCases
|
|
1126
|
+
// every key on the way out.
|
|
1127
|
+
const copiedField = {
|
|
1128
|
+
Id: "f1000000-0000-0000-0000-000000000200",
|
|
1129
|
+
Name: "submitter",
|
|
1130
|
+
DisplayName: "Submitter",
|
|
1131
|
+
FieldDataType: { Name: "RELATIONSHIP" },
|
|
1132
|
+
IsRequired: true,
|
|
1133
|
+
ReferenceType: "ManyToOne",
|
|
1134
|
+
ReferenceEntity: {
|
|
1135
|
+
Id: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
1136
|
+
Name: "Person",
|
|
1137
|
+
DisplayName: "Person",
|
|
1138
|
+
},
|
|
1139
|
+
ReferenceField: {
|
|
1140
|
+
Id: "f1000000-0000-0000-0000-000000000100",
|
|
1141
|
+
Definition: {
|
|
1142
|
+
Id: "f1000000-0000-0000-0000-000000000100",
|
|
1143
|
+
Name: "email",
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
};
|
|
1147
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1148
|
+
addFields: [copiedField],
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
const program = buildProgram();
|
|
1152
|
+
await program.parseAsync([
|
|
1153
|
+
"node",
|
|
1154
|
+
"test",
|
|
1155
|
+
"entities",
|
|
1156
|
+
"update",
|
|
1157
|
+
"entity-id",
|
|
1158
|
+
"--body",
|
|
1159
|
+
JSON.stringify({ addFields: [copiedField] }),
|
|
1160
|
+
]);
|
|
1161
|
+
|
|
1162
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1163
|
+
"entity-id",
|
|
1164
|
+
expect.objectContaining({
|
|
1165
|
+
addFields: [
|
|
1166
|
+
{
|
|
1167
|
+
name: "submitter",
|
|
1168
|
+
type: "RELATIONSHIP",
|
|
1169
|
+
displayName: "Submitter",
|
|
1170
|
+
isRequired: true,
|
|
1171
|
+
referenceEntityId:
|
|
1172
|
+
"a1b2c3d4-0000-0000-0000-000000000010",
|
|
1173
|
+
referenceFieldId:
|
|
1174
|
+
"f1000000-0000-0000-0000-000000000100",
|
|
1175
|
+
},
|
|
1176
|
+
],
|
|
1177
|
+
}),
|
|
1178
|
+
);
|
|
1179
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1180
|
+
expect.objectContaining({ Code: "EntityUpdated" }),
|
|
1181
|
+
);
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
it("should normalize a copied CHOICE_SET_SINGLE field into choiceSetId", async () => {
|
|
1185
|
+
const sdk = mockSdk();
|
|
1186
|
+
const copiedField = {
|
|
1187
|
+
Id: "f1000000-0000-0000-0000-000000000201",
|
|
1188
|
+
Name: "category",
|
|
1189
|
+
DisplayName: "Category",
|
|
1190
|
+
FieldDataType: { Name: "CHOICE_SET_SINGLE" },
|
|
1191
|
+
IsRequired: true,
|
|
1192
|
+
ReferenceChoiceSet: {
|
|
1193
|
+
Id: "c1d2e3f4-0000-0000-0000-000000000001",
|
|
1194
|
+
Name: "ExpenseCategory",
|
|
1195
|
+
DisplayName: "Expense Category",
|
|
1196
|
+
},
|
|
1197
|
+
};
|
|
1198
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1199
|
+
addFields: [copiedField],
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
const program = buildProgram();
|
|
1203
|
+
await program.parseAsync([
|
|
1204
|
+
"node",
|
|
1205
|
+
"test",
|
|
1206
|
+
"entities",
|
|
1207
|
+
"update",
|
|
1208
|
+
"entity-id",
|
|
1209
|
+
"--body",
|
|
1210
|
+
JSON.stringify({ addFields: [copiedField] }),
|
|
1211
|
+
]);
|
|
1212
|
+
|
|
1213
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1214
|
+
"entity-id",
|
|
1215
|
+
expect.objectContaining({
|
|
1216
|
+
addFields: [
|
|
1217
|
+
{
|
|
1218
|
+
name: "category",
|
|
1219
|
+
type: "CHOICE_SET_SINGLE",
|
|
1220
|
+
displayName: "Category",
|
|
1221
|
+
isRequired: true,
|
|
1222
|
+
choiceSetId: "c1d2e3f4-0000-0000-0000-000000000001",
|
|
1223
|
+
},
|
|
1224
|
+
],
|
|
1225
|
+
}),
|
|
1226
|
+
);
|
|
1227
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1228
|
+
expect.objectContaining({ Code: "EntityUpdated" }),
|
|
1229
|
+
);
|
|
1230
|
+
});
|
|
1231
|
+
|
|
987
1232
|
it("should update entity by adding a RELATIONSHIP field with referenceEntityId and referenceFieldId", async () => {
|
|
988
1233
|
const sdk = mockSdk();
|
|
989
1234
|
const addFields = [
|
|
990
1235
|
{
|
|
991
|
-
|
|
1236
|
+
name: "rel1",
|
|
992
1237
|
type: "RELATIONSHIP",
|
|
993
1238
|
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
994
1239
|
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
@@ -1020,7 +1265,7 @@ describe("entities update", () => {
|
|
|
1020
1265
|
const sdk = mockSdk();
|
|
1021
1266
|
const addFields = [
|
|
1022
1267
|
{
|
|
1023
|
-
|
|
1268
|
+
name: "purchaseOrder",
|
|
1024
1269
|
type: "RELATIONSHIP",
|
|
1025
1270
|
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
1026
1271
|
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
@@ -1057,7 +1302,7 @@ describe("entities update", () => {
|
|
|
1057
1302
|
const sdk = mockSdk();
|
|
1058
1303
|
const addFields = [
|
|
1059
1304
|
{
|
|
1060
|
-
|
|
1305
|
+
name: "multiOpType",
|
|
1061
1306
|
type: "CHOICE_SET_MULTIPLE",
|
|
1062
1307
|
choiceSetId: "9db00b66-9952-f111-8ef3-6045bd07956d",
|
|
1063
1308
|
},
|
|
@@ -1191,7 +1436,7 @@ describe("entities update", () => {
|
|
|
1191
1436
|
it("should remove a CHOICE_SET_SINGLE field via removeFields with --confirm and --reason", async () => {
|
|
1192
1437
|
const sdk = mockSdk();
|
|
1193
1438
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1194
|
-
removeFields: [{
|
|
1439
|
+
removeFields: [{ name: "opType" }],
|
|
1195
1440
|
});
|
|
1196
1441
|
|
|
1197
1442
|
const program = buildProgram();
|
|
@@ -1202,7 +1447,7 @@ describe("entities update", () => {
|
|
|
1202
1447
|
"update",
|
|
1203
1448
|
"entity-id",
|
|
1204
1449
|
"--body",
|
|
1205
|
-
'{"removeFields":[{"
|
|
1450
|
+
'{"removeFields":[{"name":"opType"}]}',
|
|
1206
1451
|
"--confirm",
|
|
1207
1452
|
"--reason",
|
|
1208
1453
|
"dropping single-select choice-set field",
|
|
@@ -1211,7 +1456,7 @@ describe("entities update", () => {
|
|
|
1211
1456
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1212
1457
|
"entity-id",
|
|
1213
1458
|
expect.objectContaining({
|
|
1214
|
-
removeFields: [{
|
|
1459
|
+
removeFields: [{ name: "opType" }],
|
|
1215
1460
|
}),
|
|
1216
1461
|
);
|
|
1217
1462
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -1229,7 +1474,7 @@ describe("entities update", () => {
|
|
|
1229
1474
|
it("should remove a CHOICE_SET_MULTIPLE field via removeFields with --confirm and --reason", async () => {
|
|
1230
1475
|
const sdk = mockSdk();
|
|
1231
1476
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1232
|
-
removeFields: [{
|
|
1477
|
+
removeFields: [{ name: "multiOpType" }],
|
|
1233
1478
|
});
|
|
1234
1479
|
|
|
1235
1480
|
const program = buildProgram();
|
|
@@ -1240,7 +1485,7 @@ describe("entities update", () => {
|
|
|
1240
1485
|
"update",
|
|
1241
1486
|
"entity-id",
|
|
1242
1487
|
"--body",
|
|
1243
|
-
'{"removeFields":[{"
|
|
1488
|
+
'{"removeFields":[{"name":"multiOpType"}]}',
|
|
1244
1489
|
"--confirm",
|
|
1245
1490
|
"--reason",
|
|
1246
1491
|
"dropping multi-select choice-set field",
|
|
@@ -1249,7 +1494,7 @@ describe("entities update", () => {
|
|
|
1249
1494
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1250
1495
|
"entity-id",
|
|
1251
1496
|
expect.objectContaining({
|
|
1252
|
-
removeFields: [{
|
|
1497
|
+
removeFields: [{ name: "multiOpType" }],
|
|
1253
1498
|
}),
|
|
1254
1499
|
);
|
|
1255
1500
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -1265,10 +1510,7 @@ describe("entities update", () => {
|
|
|
1265
1510
|
it("should remove both choice-set fields in a single removeFields call", async () => {
|
|
1266
1511
|
const sdk = mockSdk();
|
|
1267
1512
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1268
|
-
removeFields: [
|
|
1269
|
-
{ fieldName: "opType" },
|
|
1270
|
-
{ fieldName: "multiOpType" },
|
|
1271
|
-
],
|
|
1513
|
+
removeFields: [{ name: "opType" }, { name: "multiOpType" }],
|
|
1272
1514
|
});
|
|
1273
1515
|
|
|
1274
1516
|
const program = buildProgram();
|
|
@@ -1279,7 +1521,7 @@ describe("entities update", () => {
|
|
|
1279
1521
|
"update",
|
|
1280
1522
|
"entity-id",
|
|
1281
1523
|
"--body",
|
|
1282
|
-
'{"removeFields":[{"
|
|
1524
|
+
'{"removeFields":[{"name":"opType"},{"name":"multiOpType"}]}',
|
|
1283
1525
|
"--confirm",
|
|
1284
1526
|
"--reason",
|
|
1285
1527
|
"dropping all choice-set fields",
|
|
@@ -1288,10 +1530,7 @@ describe("entities update", () => {
|
|
|
1288
1530
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1289
1531
|
"entity-id",
|
|
1290
1532
|
expect.objectContaining({
|
|
1291
|
-
removeFields: [
|
|
1292
|
-
{ fieldName: "opType" },
|
|
1293
|
-
{ fieldName: "multiOpType" },
|
|
1294
|
-
],
|
|
1533
|
+
removeFields: [{ name: "opType" }, { name: "multiOpType" }],
|
|
1295
1534
|
}),
|
|
1296
1535
|
);
|
|
1297
1536
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -1336,10 +1575,37 @@ describe("entities update", () => {
|
|
|
1336
1575
|
);
|
|
1337
1576
|
});
|
|
1338
1577
|
|
|
1578
|
+
it("should forward isAnalyticsEnabled to updateById", async () => {
|
|
1579
|
+
const sdk = mockSdk();
|
|
1580
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1581
|
+
displayName: "My Entity",
|
|
1582
|
+
isAnalyticsEnabled: true,
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
const program = buildProgram();
|
|
1586
|
+
await program.parseAsync([
|
|
1587
|
+
"node",
|
|
1588
|
+
"test",
|
|
1589
|
+
"entities",
|
|
1590
|
+
"update",
|
|
1591
|
+
"entity-id",
|
|
1592
|
+
"--body",
|
|
1593
|
+
'{"displayName":"My Entity","isAnalyticsEnabled":true}',
|
|
1594
|
+
]);
|
|
1595
|
+
|
|
1596
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1597
|
+
"entity-id",
|
|
1598
|
+
expect.objectContaining({ isAnalyticsEnabled: true }),
|
|
1599
|
+
);
|
|
1600
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1601
|
+
expect.objectContaining({ Code: "EntityUpdated" }),
|
|
1602
|
+
);
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1339
1605
|
it("should update entity with combined addFields, updateFields, and metadata", async () => {
|
|
1340
1606
|
const sdk = mockSdk();
|
|
1341
1607
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1342
|
-
addFields: [{
|
|
1608
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
1343
1609
|
updateFields: [{ id: "field-uuid-1", isRequired: false }],
|
|
1344
1610
|
displayName: "Combined Update",
|
|
1345
1611
|
isRbacEnabled: true,
|
|
@@ -1353,7 +1619,7 @@ describe("entities update", () => {
|
|
|
1353
1619
|
"update",
|
|
1354
1620
|
"entity-id",
|
|
1355
1621
|
"--body",
|
|
1356
|
-
'{"addFields":[{"
|
|
1622
|
+
'{"addFields":[{"name":"newField","type":"STRING"}],"updateFields":[{"id":"field-uuid-1","isRequired":false}],"displayName":"Combined Update","isRbacEnabled":true}',
|
|
1357
1623
|
]);
|
|
1358
1624
|
|
|
1359
1625
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
@@ -1373,7 +1639,8 @@ describe("entities update", () => {
|
|
|
1373
1639
|
);
|
|
1374
1640
|
});
|
|
1375
1641
|
|
|
1376
|
-
it("should reject addFields items missing
|
|
1642
|
+
it("should reject addFields items missing name", async () => {
|
|
1643
|
+
const sdk = mockSdk();
|
|
1377
1644
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1378
1645
|
addFields: [{ type: "STRING" }],
|
|
1379
1646
|
});
|
|
@@ -1393,15 +1660,152 @@ describe("entities update", () => {
|
|
|
1393
1660
|
expect.objectContaining({
|
|
1394
1661
|
Result: "Failure",
|
|
1395
1662
|
Message:
|
|
1396
|
-
"Each field in addFields must include a non-empty '
|
|
1663
|
+
"Each field in addFields must include a non-empty 'name' string",
|
|
1664
|
+
}),
|
|
1665
|
+
);
|
|
1666
|
+
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
1667
|
+
expect(process.exitCode).toBe(1);
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
it("should reject a name in the update body since the entity name is immutable", async () => {
|
|
1671
|
+
const sdk = mockSdk();
|
|
1672
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1673
|
+
name: "RenamedEntity",
|
|
1674
|
+
displayName: "Renamed Entity",
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
const program = buildProgram();
|
|
1678
|
+
await program.parseAsync([
|
|
1679
|
+
"node",
|
|
1680
|
+
"test",
|
|
1681
|
+
"entities",
|
|
1682
|
+
"update",
|
|
1683
|
+
"entity-id",
|
|
1684
|
+
"--body",
|
|
1685
|
+
'{"name":"RenamedEntity","displayName":"Renamed Entity"}',
|
|
1686
|
+
]);
|
|
1687
|
+
|
|
1688
|
+
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
1689
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1690
|
+
expect.objectContaining({
|
|
1691
|
+
Result: "Failure",
|
|
1692
|
+
Message: expect.stringContaining(
|
|
1693
|
+
"Entity name cannot be changed after creation",
|
|
1694
|
+
),
|
|
1695
|
+
Instructions: expect.stringContaining(
|
|
1696
|
+
"Remove 'name' from the update body",
|
|
1697
|
+
),
|
|
1698
|
+
}),
|
|
1699
|
+
);
|
|
1700
|
+
expect(process.exitCode).toBe(1);
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
it("should require displayName when updating description without it", async () => {
|
|
1704
|
+
const sdk = mockSdk();
|
|
1705
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1706
|
+
description: "New description",
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
const program = buildProgram();
|
|
1710
|
+
await program.parseAsync([
|
|
1711
|
+
"node",
|
|
1712
|
+
"test",
|
|
1713
|
+
"entities",
|
|
1714
|
+
"update",
|
|
1715
|
+
"entity-id",
|
|
1716
|
+
"--body",
|
|
1717
|
+
'{"description":"New description"}',
|
|
1718
|
+
]);
|
|
1719
|
+
|
|
1720
|
+
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
1721
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1722
|
+
expect.objectContaining({
|
|
1723
|
+
Result: "Failure",
|
|
1724
|
+
Message: expect.stringContaining(
|
|
1725
|
+
"'displayName' is required when updating entity metadata",
|
|
1726
|
+
),
|
|
1397
1727
|
}),
|
|
1398
1728
|
);
|
|
1399
1729
|
expect(process.exitCode).toBe(1);
|
|
1400
1730
|
});
|
|
1401
1731
|
|
|
1732
|
+
it("should allow a metadata update that includes displayName", async () => {
|
|
1733
|
+
const sdk = mockSdk();
|
|
1734
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1735
|
+
displayName: "Current Name",
|
|
1736
|
+
description: "New description",
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
const program = buildProgram();
|
|
1740
|
+
await program.parseAsync([
|
|
1741
|
+
"node",
|
|
1742
|
+
"test",
|
|
1743
|
+
"entities",
|
|
1744
|
+
"update",
|
|
1745
|
+
"entity-id",
|
|
1746
|
+
"--body",
|
|
1747
|
+
'{"displayName":"Current Name","description":"New description"}',
|
|
1748
|
+
]);
|
|
1749
|
+
|
|
1750
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1751
|
+
"entity-id",
|
|
1752
|
+
expect.objectContaining({
|
|
1753
|
+
displayName: "Current Name",
|
|
1754
|
+
description: "New description",
|
|
1755
|
+
}),
|
|
1756
|
+
);
|
|
1757
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1758
|
+
expect.objectContaining({ Code: "EntityUpdated" }),
|
|
1759
|
+
);
|
|
1760
|
+
});
|
|
1761
|
+
|
|
1762
|
+
it("should report malformed addFields and removeFields together", async () => {
|
|
1763
|
+
const sdk = mockSdk();
|
|
1764
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1765
|
+
addFields: [{ type: "STRING" }],
|
|
1766
|
+
removeFields: ["oldField"],
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
const program = buildProgram();
|
|
1770
|
+
await program.parseAsync([
|
|
1771
|
+
"node",
|
|
1772
|
+
"test",
|
|
1773
|
+
"entities",
|
|
1774
|
+
"update",
|
|
1775
|
+
"entity-id",
|
|
1776
|
+
"--body",
|
|
1777
|
+
'{"addFields":[{"type":"STRING"}],"removeFields":["oldField"]}',
|
|
1778
|
+
"--yes",
|
|
1779
|
+
"--reason",
|
|
1780
|
+
"temporary validation",
|
|
1781
|
+
]);
|
|
1782
|
+
|
|
1783
|
+
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
1784
|
+
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
1785
|
+
expect.objectContaining({
|
|
1786
|
+
Result: "Failure",
|
|
1787
|
+
Message: expect.stringContaining(
|
|
1788
|
+
"Each field in addFields must include a non-empty 'name' string",
|
|
1789
|
+
),
|
|
1790
|
+
Instructions: expect.stringContaining(
|
|
1791
|
+
'Example: {"removeFields":[{"name":"oldField"}]}',
|
|
1792
|
+
),
|
|
1793
|
+
ErrorCode: "invalid_argument",
|
|
1794
|
+
Retry: "RetryWillNotFix",
|
|
1795
|
+
}),
|
|
1796
|
+
);
|
|
1797
|
+
const error = vi.mocked(OutputFormatter.error).mock.calls[0][0] as {
|
|
1798
|
+
Message: string;
|
|
1799
|
+
};
|
|
1800
|
+
expect(error.Message).toContain(
|
|
1801
|
+
"Each field in removeFields must include a non-empty 'name' string",
|
|
1802
|
+
);
|
|
1803
|
+
expect(process.exitCode).toBe(1);
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1402
1806
|
it("should error when addFields has an invalid type", async () => {
|
|
1403
1807
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1404
|
-
addFields: [{
|
|
1808
|
+
addFields: [{ name: "price", type: "text" }],
|
|
1405
1809
|
});
|
|
1406
1810
|
|
|
1407
1811
|
const program = buildProgram();
|
|
@@ -1412,7 +1816,7 @@ describe("entities update", () => {
|
|
|
1412
1816
|
"update",
|
|
1413
1817
|
"entity-id",
|
|
1414
1818
|
"--body",
|
|
1415
|
-
'{"addFields":[{"
|
|
1819
|
+
'{"addFields":[{"name":"price","type":"text"}]}',
|
|
1416
1820
|
]);
|
|
1417
1821
|
|
|
1418
1822
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
@@ -1478,7 +1882,7 @@ describe("entities update", () => {
|
|
|
1478
1882
|
expect(process.exitCode).toBe(1);
|
|
1479
1883
|
});
|
|
1480
1884
|
|
|
1481
|
-
it("should reject removeFields entries missing
|
|
1885
|
+
it("should reject removeFields entries missing name", async () => {
|
|
1482
1886
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1483
1887
|
removeFields: [{}],
|
|
1484
1888
|
});
|
|
@@ -1501,7 +1905,7 @@ describe("entities update", () => {
|
|
|
1501
1905
|
expect.objectContaining({
|
|
1502
1906
|
Result: "Failure",
|
|
1503
1907
|
Message:
|
|
1504
|
-
"Each field in removeFields must include a non-empty '
|
|
1908
|
+
"Each field in removeFields must include a non-empty 'name' string",
|
|
1505
1909
|
}),
|
|
1506
1910
|
);
|
|
1507
1911
|
expect(process.exitCode).toBe(1);
|
|
@@ -1510,7 +1914,7 @@ describe("entities update", () => {
|
|
|
1510
1914
|
it("should reject removeFields without confirmation: exit 1, SDK not called", async () => {
|
|
1511
1915
|
const sdk = mockSdk();
|
|
1512
1916
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1513
|
-
removeFields: [{
|
|
1917
|
+
removeFields: [{ name: "oldField" }],
|
|
1514
1918
|
});
|
|
1515
1919
|
|
|
1516
1920
|
const program = buildProgram();
|
|
@@ -1521,7 +1925,7 @@ describe("entities update", () => {
|
|
|
1521
1925
|
"update",
|
|
1522
1926
|
"entity-id",
|
|
1523
1927
|
"--body",
|
|
1524
|
-
'{"removeFields":[{"
|
|
1928
|
+
'{"removeFields":[{"name":"oldField"}]}',
|
|
1525
1929
|
"--reason",
|
|
1526
1930
|
"drop legacy",
|
|
1527
1931
|
]);
|
|
@@ -1534,7 +1938,7 @@ describe("entities update", () => {
|
|
|
1534
1938
|
it("should accept removeFields with --yes (canonical flag)", async () => {
|
|
1535
1939
|
const sdk = mockSdk();
|
|
1536
1940
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1537
|
-
removeFields: [{
|
|
1941
|
+
removeFields: [{ name: "oldField" }],
|
|
1538
1942
|
});
|
|
1539
1943
|
|
|
1540
1944
|
const program = buildProgram();
|
|
@@ -1545,7 +1949,7 @@ describe("entities update", () => {
|
|
|
1545
1949
|
"update",
|
|
1546
1950
|
"entity-id",
|
|
1547
1951
|
"--body",
|
|
1548
|
-
'{"removeFields":[{"
|
|
1952
|
+
'{"removeFields":[{"name":"oldField"}]}',
|
|
1549
1953
|
"--yes",
|
|
1550
1954
|
"--reason",
|
|
1551
1955
|
"drop legacy",
|
|
@@ -1560,7 +1964,7 @@ describe("entities update", () => {
|
|
|
1560
1964
|
it("should reject removeFields without --reason", async () => {
|
|
1561
1965
|
const sdk = mockSdk();
|
|
1562
1966
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1563
|
-
removeFields: [{
|
|
1967
|
+
removeFields: [{ name: "oldField" }],
|
|
1564
1968
|
});
|
|
1565
1969
|
|
|
1566
1970
|
const program = buildProgram();
|
|
@@ -1571,7 +1975,7 @@ describe("entities update", () => {
|
|
|
1571
1975
|
"update",
|
|
1572
1976
|
"entity-id",
|
|
1573
1977
|
"--body",
|
|
1574
|
-
'{"removeFields":[{"
|
|
1978
|
+
'{"removeFields":[{"name":"oldField"}]}',
|
|
1575
1979
|
"--confirm",
|
|
1576
1980
|
]);
|
|
1577
1981
|
|
|
@@ -1580,6 +1984,7 @@ describe("entities update", () => {
|
|
|
1580
1984
|
expect.objectContaining({
|
|
1581
1985
|
Result: "Failure",
|
|
1582
1986
|
Message: "Reason required for destructive operation",
|
|
1987
|
+
ErrorCode: "invalid_argument",
|
|
1583
1988
|
}),
|
|
1584
1989
|
);
|
|
1585
1990
|
expect(process.exitCode).toBe(1);
|
|
@@ -1588,10 +1993,7 @@ describe("entities update", () => {
|
|
|
1588
1993
|
it("should remove fields when --confirm and --reason are present", async () => {
|
|
1589
1994
|
const sdk = mockSdk();
|
|
1590
1995
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1591
|
-
removeFields: [
|
|
1592
|
-
{ fieldName: "oldField" },
|
|
1593
|
-
{ fieldName: "anotherOld" },
|
|
1594
|
-
],
|
|
1996
|
+
removeFields: [{ name: "oldField" }, { name: "anotherOld" }],
|
|
1595
1997
|
});
|
|
1596
1998
|
|
|
1597
1999
|
const program = buildProgram();
|
|
@@ -1602,7 +2004,7 @@ describe("entities update", () => {
|
|
|
1602
2004
|
"update",
|
|
1603
2005
|
"entity-id",
|
|
1604
2006
|
"--body",
|
|
1605
|
-
'{"removeFields":[{"
|
|
2007
|
+
'{"removeFields":[{"name":"oldField"},{"name":"anotherOld"}]}',
|
|
1606
2008
|
"--confirm",
|
|
1607
2009
|
"--reason",
|
|
1608
2010
|
"drop legacy fields",
|
|
@@ -1611,10 +2013,7 @@ describe("entities update", () => {
|
|
|
1611
2013
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1612
2014
|
"entity-id",
|
|
1613
2015
|
expect.objectContaining({
|
|
1614
|
-
removeFields: [
|
|
1615
|
-
{ fieldName: "oldField" },
|
|
1616
|
-
{ fieldName: "anotherOld" },
|
|
1617
|
-
],
|
|
2016
|
+
removeFields: [{ name: "oldField" }, { name: "anotherOld" }],
|
|
1618
2017
|
}),
|
|
1619
2018
|
);
|
|
1620
2019
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
@@ -1656,8 +2055,8 @@ describe("entities update", () => {
|
|
|
1656
2055
|
it("should accept addFields + removeFields + updateFields + metadata in one call", async () => {
|
|
1657
2056
|
const sdk = mockSdk();
|
|
1658
2057
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1659
|
-
addFields: [{
|
|
1660
|
-
removeFields: [{
|
|
2058
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
2059
|
+
removeFields: [{ name: "oldField" }],
|
|
1661
2060
|
updateFields: [
|
|
1662
2061
|
{
|
|
1663
2062
|
id: "field-uuid-1",
|
|
@@ -1678,7 +2077,7 @@ describe("entities update", () => {
|
|
|
1678
2077
|
"update",
|
|
1679
2078
|
"entity-id",
|
|
1680
2079
|
"--body",
|
|
1681
|
-
'{"addFields":[{"
|
|
2080
|
+
'{"addFields":[{"name":"newField","type":"STRING"}],"removeFields":[{"name":"oldField"}],"updateFields":[{"id":"field-uuid-1","displayName":"Renamed","isRequired":true}],"displayName":"Combined","description":"all-in-one update","isRbacEnabled":true}',
|
|
1682
2081
|
"--confirm",
|
|
1683
2082
|
"--reason",
|
|
1684
2083
|
"schema overhaul",
|
|
@@ -1687,8 +2086,8 @@ describe("entities update", () => {
|
|
|
1687
2086
|
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
1688
2087
|
"entity-id",
|
|
1689
2088
|
expect.objectContaining({
|
|
1690
|
-
addFields: [{
|
|
1691
|
-
removeFields: [{
|
|
2089
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
2090
|
+
removeFields: [{ name: "oldField" }],
|
|
1692
2091
|
updateFields: [
|
|
1693
2092
|
{
|
|
1694
2093
|
id: "field-uuid-1",
|
|
@@ -1714,11 +2113,11 @@ describe("entities update", () => {
|
|
|
1714
2113
|
);
|
|
1715
2114
|
});
|
|
1716
2115
|
|
|
1717
|
-
it("should reject when same
|
|
2116
|
+
it("should reject when same name appears in addFields and removeFields", async () => {
|
|
1718
2117
|
const sdk = mockSdk();
|
|
1719
2118
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1720
|
-
addFields: [{
|
|
1721
|
-
removeFields: [{
|
|
2119
|
+
addFields: [{ name: "overlap", type: "STRING" }],
|
|
2120
|
+
removeFields: [{ name: "overlap" }],
|
|
1722
2121
|
});
|
|
1723
2122
|
|
|
1724
2123
|
const program = buildProgram();
|
|
@@ -1729,7 +2128,7 @@ describe("entities update", () => {
|
|
|
1729
2128
|
"update",
|
|
1730
2129
|
"entity-id",
|
|
1731
2130
|
"--body",
|
|
1732
|
-
'{"addFields":[{"
|
|
2131
|
+
'{"addFields":[{"name":"overlap","type":"STRING"}],"removeFields":[{"name":"overlap"}]}',
|
|
1733
2132
|
"--confirm",
|
|
1734
2133
|
"--reason",
|
|
1735
2134
|
"recreate",
|
|
@@ -1746,12 +2145,12 @@ describe("entities update", () => {
|
|
|
1746
2145
|
expect(process.exitCode).toBe(1);
|
|
1747
2146
|
});
|
|
1748
2147
|
|
|
1749
|
-
it("should reject duplicate
|
|
2148
|
+
it("should reject duplicate names within addFields", async () => {
|
|
1750
2149
|
const sdk = mockSdk();
|
|
1751
2150
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1752
2151
|
addFields: [
|
|
1753
|
-
{
|
|
1754
|
-
{
|
|
2152
|
+
{ name: "dup", type: "STRING" },
|
|
2153
|
+
{ name: "dup", type: "DECIMAL" },
|
|
1755
2154
|
],
|
|
1756
2155
|
});
|
|
1757
2156
|
|
|
@@ -1763,7 +2162,7 @@ describe("entities update", () => {
|
|
|
1763
2162
|
"update",
|
|
1764
2163
|
"entity-id",
|
|
1765
2164
|
"--body",
|
|
1766
|
-
'{"addFields":[{"
|
|
2165
|
+
'{"addFields":[{"name":"dup","type":"STRING"},{"name":"dup","type":"DECIMAL"}]}',
|
|
1767
2166
|
]);
|
|
1768
2167
|
|
|
1769
2168
|
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
@@ -1883,10 +2282,10 @@ describe("entities update", () => {
|
|
|
1883
2282
|
expect(call.Data).toEqual({ ID: "entity-id" });
|
|
1884
2283
|
});
|
|
1885
2284
|
|
|
1886
|
-
it("should reject empty
|
|
2285
|
+
it("should reject empty name in addFields", async () => {
|
|
1887
2286
|
const sdk = mockSdk();
|
|
1888
2287
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1889
|
-
addFields: [{
|
|
2288
|
+
addFields: [{ name: " ", type: "STRING" }],
|
|
1890
2289
|
});
|
|
1891
2290
|
|
|
1892
2291
|
const program = buildProgram();
|
|
@@ -1897,7 +2296,7 @@ describe("entities update", () => {
|
|
|
1897
2296
|
"update",
|
|
1898
2297
|
"entity-id",
|
|
1899
2298
|
"--body",
|
|
1900
|
-
'{"addFields":[{"
|
|
2299
|
+
'{"addFields":[{"name":" ","type":"STRING"}]}',
|
|
1901
2300
|
]);
|
|
1902
2301
|
|
|
1903
2302
|
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
@@ -1905,7 +2304,7 @@ describe("entities update", () => {
|
|
|
1905
2304
|
expect.objectContaining({
|
|
1906
2305
|
Result: "Failure",
|
|
1907
2306
|
Message:
|
|
1908
|
-
"Each field in addFields must include a non-empty '
|
|
2307
|
+
"Each field in addFields must include a non-empty 'name' string",
|
|
1909
2308
|
}),
|
|
1910
2309
|
);
|
|
1911
2310
|
expect(process.exitCode).toBe(1);
|
|
@@ -2015,7 +2414,7 @@ describe("entities update", () => {
|
|
|
2015
2414
|
new Error("Update failed"),
|
|
2016
2415
|
);
|
|
2017
2416
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2018
|
-
addFields: [{
|
|
2417
|
+
addFields: [{ name: "newField", type: "STRING" }],
|
|
2019
2418
|
});
|
|
2020
2419
|
|
|
2021
2420
|
const program = buildProgram();
|
|
@@ -2026,7 +2425,7 @@ describe("entities update", () => {
|
|
|
2026
2425
|
"update",
|
|
2027
2426
|
"entity-id",
|
|
2028
2427
|
"--body",
|
|
2029
|
-
'{"addFields":[{"
|
|
2428
|
+
'{"addFields":[{"name":"newField","type":"STRING"}]}',
|
|
2030
2429
|
]);
|
|
2031
2430
|
|
|
2032
2431
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
@@ -2041,7 +2440,7 @@ describe("entities update", () => {
|
|
|
2041
2440
|
it("should reject a UI-broken type in addFields", async () => {
|
|
2042
2441
|
const sdk = mockSdk();
|
|
2043
2442
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2044
|
-
addFields: [{
|
|
2443
|
+
addFields: [{ name: "count", type: "FLOAT" }],
|
|
2045
2444
|
});
|
|
2046
2445
|
|
|
2047
2446
|
const program = buildProgram();
|
|
@@ -2052,7 +2451,7 @@ describe("entities update", () => {
|
|
|
2052
2451
|
"update",
|
|
2053
2452
|
"entity-id",
|
|
2054
2453
|
"--body",
|
|
2055
|
-
'{"addFields":[{"
|
|
2454
|
+
'{"addFields":[{"name":"count","type":"FLOAT"}]}',
|
|
2056
2455
|
]);
|
|
2057
2456
|
|
|
2058
2457
|
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
@@ -2070,7 +2469,7 @@ describe("entities update", () => {
|
|
|
2070
2469
|
it("should reject a reserved keyword in addFields", async () => {
|
|
2071
2470
|
const sdk = mockSdk();
|
|
2072
2471
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2073
|
-
addFields: [{
|
|
2472
|
+
addFields: [{ name: "New", type: "STRING" }],
|
|
2074
2473
|
});
|
|
2075
2474
|
|
|
2076
2475
|
const program = buildProgram();
|
|
@@ -2081,7 +2480,7 @@ describe("entities update", () => {
|
|
|
2081
2480
|
"update",
|
|
2082
2481
|
"entity-id",
|
|
2083
2482
|
"--body",
|
|
2084
|
-
'{"addFields":[{"
|
|
2483
|
+
'{"addFields":[{"name":"New","type":"STRING"}]}',
|
|
2085
2484
|
]);
|
|
2086
2485
|
|
|
2087
2486
|
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
@@ -2225,6 +2624,7 @@ describe("entities delete", () => {
|
|
|
2225
2624
|
expect.objectContaining({
|
|
2226
2625
|
Result: "Failure",
|
|
2227
2626
|
Message: "Reason required for destructive operation",
|
|
2627
|
+
ErrorCode: "invalid_argument",
|
|
2228
2628
|
}),
|
|
2229
2629
|
);
|
|
2230
2630
|
expect(process.exitCode).toBe(1);
|
|
@@ -2250,6 +2650,7 @@ describe("entities delete", () => {
|
|
|
2250
2650
|
expect.objectContaining({
|
|
2251
2651
|
Result: "Failure",
|
|
2252
2652
|
Message: "Reason required for destructive operation",
|
|
2653
|
+
ErrorCode: "invalid_argument",
|
|
2253
2654
|
}),
|
|
2254
2655
|
);
|
|
2255
2656
|
expect(process.exitCode).toBe(1);
|
|
@@ -2336,7 +2737,7 @@ describe("entities --folder-key forwarding", () => {
|
|
|
2336
2737
|
const sdk = mockSdk();
|
|
2337
2738
|
vi.mocked(sdk.entities.create).mockResolvedValue("new-entity-id");
|
|
2338
2739
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2339
|
-
fields: [{
|
|
2740
|
+
fields: [{ name: "title", type: "STRING" }],
|
|
2340
2741
|
});
|
|
2341
2742
|
|
|
2342
2743
|
const program = buildProgram();
|
|
@@ -2347,14 +2748,14 @@ describe("entities --folder-key forwarding", () => {
|
|
|
2347
2748
|
"create",
|
|
2348
2749
|
"MyEntity",
|
|
2349
2750
|
"--body",
|
|
2350
|
-
'{"fields":[{"
|
|
2751
|
+
'{"fields":[{"name":"title","type":"STRING"}]}',
|
|
2351
2752
|
"--folder-key",
|
|
2352
2753
|
"folder-guid-2",
|
|
2353
2754
|
]);
|
|
2354
2755
|
|
|
2355
2756
|
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
2356
2757
|
"MyEntity",
|
|
2357
|
-
[{
|
|
2758
|
+
[{ name: "title", type: "STRING" }],
|
|
2358
2759
|
expect.objectContaining({ folderKey: "folder-guid-2" }),
|
|
2359
2760
|
);
|
|
2360
2761
|
});
|