@uipath/data-fabric-tool 1.195.0 → 1.197.0-preview.59
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 +2 -0
- package/dist/tool.js +6037 -6015
- package/package.json +2 -2
- package/src/commands/choice-sets.spec.ts +262 -13
- package/src/commands/choice-sets.ts +126 -8
- package/src/commands/entities.spec.ts +302 -14
- package/src/commands/entities.ts +111 -9
- package/src/commands/files.spec.ts +108 -3
- package/src/commands/files.ts +45 -2
- package/src/commands/records.spec.ts +323 -0
- package/src/commands/records.ts +120 -18
- package/src/utils/input.spec.ts +127 -0
- package/src/utils/input.ts +30 -1
- package/src/utils/output.spec.ts +22 -9
- package/src/utils/output.ts +27 -9
|
@@ -518,6 +518,48 @@ describe("entities create", () => {
|
|
|
518
518
|
);
|
|
519
519
|
});
|
|
520
520
|
|
|
521
|
+
it("should preserve per-field referenceFolderKey on a cross-folder RELATIONSHIP", async () => {
|
|
522
|
+
const sdk = mockSdk();
|
|
523
|
+
vi.mocked(sdk.entities.create).mockResolvedValue("entity-xfolder-rel");
|
|
524
|
+
const fields = [
|
|
525
|
+
{
|
|
526
|
+
fieldName: "order",
|
|
527
|
+
type: "RELATIONSHIP",
|
|
528
|
+
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
529
|
+
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
530
|
+
referenceFolderKey: "f1000000-0000-0000-0000-000000000060",
|
|
531
|
+
},
|
|
532
|
+
];
|
|
533
|
+
vi.mocked(readJsonInput).mockResolvedValue({ fields });
|
|
534
|
+
|
|
535
|
+
const program = buildProgram();
|
|
536
|
+
await program.parseAsync([
|
|
537
|
+
"node",
|
|
538
|
+
"test",
|
|
539
|
+
"entities",
|
|
540
|
+
"create",
|
|
541
|
+
"OrderLine",
|
|
542
|
+
"--body",
|
|
543
|
+
JSON.stringify({ fields }),
|
|
544
|
+
"--folder-key",
|
|
545
|
+
"f1000000-0000-0000-0000-000000000050",
|
|
546
|
+
]);
|
|
547
|
+
|
|
548
|
+
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
549
|
+
"OrderLine",
|
|
550
|
+
expect.arrayContaining([
|
|
551
|
+
expect.objectContaining({
|
|
552
|
+
fieldName: "order",
|
|
553
|
+
type: "RELATIONSHIP",
|
|
554
|
+
referenceFolderKey: "f1000000-0000-0000-0000-000000000060",
|
|
555
|
+
}),
|
|
556
|
+
]),
|
|
557
|
+
expect.objectContaining({
|
|
558
|
+
folderKey: "f1000000-0000-0000-0000-000000000050",
|
|
559
|
+
}),
|
|
560
|
+
);
|
|
561
|
+
});
|
|
562
|
+
|
|
521
563
|
it("should error when no --body or --file provided", async () => {
|
|
522
564
|
vi.mocked(readJsonInput).mockRejectedValue(
|
|
523
565
|
new Error("Provide entity definition via --file or --body."),
|
|
@@ -763,6 +805,43 @@ describe("entities update", () => {
|
|
|
763
805
|
);
|
|
764
806
|
});
|
|
765
807
|
|
|
808
|
+
it("should preserve per-field referenceFolderKey when addFields includes a cross-folder RELATIONSHIP", async () => {
|
|
809
|
+
const sdk = mockSdk();
|
|
810
|
+
const addFields = [
|
|
811
|
+
{
|
|
812
|
+
fieldName: "order",
|
|
813
|
+
type: "RELATIONSHIP",
|
|
814
|
+
referenceEntityId: "a1b2c3d4-0000-0000-0000-000000000010",
|
|
815
|
+
referenceFieldId: "f1000000-0000-0000-0000-000000000100",
|
|
816
|
+
referenceFolderKey: "f1000000-0000-0000-0000-000000000060",
|
|
817
|
+
},
|
|
818
|
+
];
|
|
819
|
+
vi.mocked(readJsonInput).mockResolvedValue({ addFields });
|
|
820
|
+
|
|
821
|
+
const program = buildProgram();
|
|
822
|
+
await program.parseAsync([
|
|
823
|
+
"node",
|
|
824
|
+
"test",
|
|
825
|
+
"entities",
|
|
826
|
+
"update",
|
|
827
|
+
"entity-id",
|
|
828
|
+
"--body",
|
|
829
|
+
JSON.stringify({ addFields }),
|
|
830
|
+
]);
|
|
831
|
+
|
|
832
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
833
|
+
"entity-id",
|
|
834
|
+
expect.objectContaining({
|
|
835
|
+
addFields: expect.arrayContaining([
|
|
836
|
+
expect.objectContaining({
|
|
837
|
+
referenceFolderKey:
|
|
838
|
+
"f1000000-0000-0000-0000-000000000060",
|
|
839
|
+
}),
|
|
840
|
+
]),
|
|
841
|
+
}),
|
|
842
|
+
);
|
|
843
|
+
});
|
|
844
|
+
|
|
766
845
|
it("should update entity by adding a CHOICE_SET_MULTIPLE field with choiceSetId", async () => {
|
|
767
846
|
const sdk = mockSdk();
|
|
768
847
|
const addFields = [
|
|
@@ -1217,7 +1296,7 @@ describe("entities update", () => {
|
|
|
1217
1296
|
expect(process.exitCode).toBe(1);
|
|
1218
1297
|
});
|
|
1219
1298
|
|
|
1220
|
-
it("should reject removeFields without
|
|
1299
|
+
it("should reject removeFields without confirmation: exit 1, SDK not called", async () => {
|
|
1221
1300
|
const sdk = mockSdk();
|
|
1222
1301
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1223
1302
|
removeFields: [{ fieldName: "oldField" }],
|
|
@@ -1237,15 +1316,36 @@ describe("entities update", () => {
|
|
|
1237
1316
|
]);
|
|
1238
1317
|
|
|
1239
1318
|
expect(sdk.entities.updateById).not.toHaveBeenCalled();
|
|
1240
|
-
expect(OutputFormatter.
|
|
1241
|
-
expect.objectContaining({
|
|
1242
|
-
Result: "Failure",
|
|
1243
|
-
Message: "Confirmation required for destructive operation",
|
|
1244
|
-
}),
|
|
1245
|
-
);
|
|
1319
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
1246
1320
|
expect(process.exitCode).toBe(1);
|
|
1247
1321
|
});
|
|
1248
1322
|
|
|
1323
|
+
it("should accept removeFields with --yes (canonical flag)", async () => {
|
|
1324
|
+
const sdk = mockSdk();
|
|
1325
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
1326
|
+
removeFields: [{ fieldName: "oldField" }],
|
|
1327
|
+
});
|
|
1328
|
+
|
|
1329
|
+
const program = buildProgram();
|
|
1330
|
+
await program.parseAsync([
|
|
1331
|
+
"node",
|
|
1332
|
+
"test",
|
|
1333
|
+
"entities",
|
|
1334
|
+
"update",
|
|
1335
|
+
"entity-id",
|
|
1336
|
+
"--body",
|
|
1337
|
+
'{"removeFields":[{"fieldName":"oldField"}]}',
|
|
1338
|
+
"--yes",
|
|
1339
|
+
"--reason",
|
|
1340
|
+
"drop legacy",
|
|
1341
|
+
]);
|
|
1342
|
+
|
|
1343
|
+
expect(sdk.entities.updateById).toHaveBeenCalled();
|
|
1344
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1345
|
+
expect.objectContaining({ Result: "Success" }),
|
|
1346
|
+
);
|
|
1347
|
+
});
|
|
1348
|
+
|
|
1249
1349
|
it("should reject removeFields without --reason", async () => {
|
|
1250
1350
|
const sdk = mockSdk();
|
|
1251
1351
|
vi.mocked(readJsonInput).mockResolvedValue({
|
|
@@ -1751,6 +1851,7 @@ describe("entities delete", () => {
|
|
|
1751
1851
|
|
|
1752
1852
|
expect(sdk.entities.deleteById).toHaveBeenCalledWith(
|
|
1753
1853
|
"entity-to-delete",
|
|
1854
|
+
undefined,
|
|
1754
1855
|
);
|
|
1755
1856
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1756
1857
|
expect.objectContaining({
|
|
@@ -1764,7 +1865,7 @@ describe("entities delete", () => {
|
|
|
1764
1865
|
);
|
|
1765
1866
|
});
|
|
1766
1867
|
|
|
1767
|
-
it("should error when
|
|
1868
|
+
it("should error when confirmation is missing: exit 1, SDK not called", async () => {
|
|
1768
1869
|
const sdk = mockSdk();
|
|
1769
1870
|
|
|
1770
1871
|
const program = buildProgram();
|
|
@@ -1779,15 +1880,34 @@ describe("entities delete", () => {
|
|
|
1779
1880
|
]);
|
|
1780
1881
|
|
|
1781
1882
|
expect(sdk.entities.deleteById).not.toHaveBeenCalled();
|
|
1782
|
-
expect(OutputFormatter.
|
|
1783
|
-
expect.objectContaining({
|
|
1784
|
-
Result: "Failure",
|
|
1785
|
-
Message: "Confirmation required for destructive operation",
|
|
1786
|
-
}),
|
|
1787
|
-
);
|
|
1883
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
1788
1884
|
expect(process.exitCode).toBe(1);
|
|
1789
1885
|
});
|
|
1790
1886
|
|
|
1887
|
+
it("should delete with --yes (canonical confirmation flag)", async () => {
|
|
1888
|
+
const sdk = mockSdk();
|
|
1889
|
+
|
|
1890
|
+
const program = buildProgram();
|
|
1891
|
+
await program.parseAsync([
|
|
1892
|
+
"node",
|
|
1893
|
+
"test",
|
|
1894
|
+
"entities",
|
|
1895
|
+
"delete",
|
|
1896
|
+
"entity-id",
|
|
1897
|
+
"--yes",
|
|
1898
|
+
"--reason",
|
|
1899
|
+
"cleanup",
|
|
1900
|
+
]);
|
|
1901
|
+
|
|
1902
|
+
expect(sdk.entities.deleteById).toHaveBeenCalledWith(
|
|
1903
|
+
"entity-id",
|
|
1904
|
+
undefined,
|
|
1905
|
+
);
|
|
1906
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1907
|
+
expect.objectContaining({ Result: "Success" }),
|
|
1908
|
+
);
|
|
1909
|
+
});
|
|
1910
|
+
|
|
1791
1911
|
it("should error when --reason is missing", async () => {
|
|
1792
1912
|
const sdk = mockSdk();
|
|
1793
1913
|
|
|
@@ -1883,3 +2003,171 @@ describe("entities delete", () => {
|
|
|
1883
2003
|
expect(process.exitCode).toBe(1);
|
|
1884
2004
|
});
|
|
1885
2005
|
});
|
|
2006
|
+
|
|
2007
|
+
describe("entities --folder-key forwarding", () => {
|
|
2008
|
+
beforeEach(() => {
|
|
2009
|
+
vi.resetAllMocks();
|
|
2010
|
+
process.exitCode = undefined;
|
|
2011
|
+
});
|
|
2012
|
+
|
|
2013
|
+
it("should forward --folder-key to getById", async () => {
|
|
2014
|
+
const sdk = mockSdk();
|
|
2015
|
+
vi.mocked(sdk.entities.getById).mockResolvedValue({
|
|
2016
|
+
id: "ent-1",
|
|
2017
|
+
fields: [],
|
|
2018
|
+
} as never);
|
|
2019
|
+
|
|
2020
|
+
const program = buildProgram();
|
|
2021
|
+
await program.parseAsync([
|
|
2022
|
+
"node",
|
|
2023
|
+
"test",
|
|
2024
|
+
"entities",
|
|
2025
|
+
"get",
|
|
2026
|
+
"ent-1",
|
|
2027
|
+
"--folder-key",
|
|
2028
|
+
"folder-guid-1",
|
|
2029
|
+
]);
|
|
2030
|
+
|
|
2031
|
+
expect(sdk.entities.getById).toHaveBeenCalledWith("ent-1", {
|
|
2032
|
+
folderKey: "folder-guid-1",
|
|
2033
|
+
});
|
|
2034
|
+
});
|
|
2035
|
+
|
|
2036
|
+
it("should forward --folder-key into create options", async () => {
|
|
2037
|
+
const sdk = mockSdk();
|
|
2038
|
+
vi.mocked(sdk.entities.create).mockResolvedValue("new-entity-id");
|
|
2039
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2040
|
+
fields: [{ fieldName: "title", type: "STRING" }],
|
|
2041
|
+
});
|
|
2042
|
+
|
|
2043
|
+
const program = buildProgram();
|
|
2044
|
+
await program.parseAsync([
|
|
2045
|
+
"node",
|
|
2046
|
+
"test",
|
|
2047
|
+
"entities",
|
|
2048
|
+
"create",
|
|
2049
|
+
"MyEntity",
|
|
2050
|
+
"--body",
|
|
2051
|
+
'{"fields":[{"fieldName":"title","type":"STRING"}]}',
|
|
2052
|
+
"--folder-key",
|
|
2053
|
+
"folder-guid-2",
|
|
2054
|
+
]);
|
|
2055
|
+
|
|
2056
|
+
expect(sdk.entities.create).toHaveBeenCalledWith(
|
|
2057
|
+
"MyEntity",
|
|
2058
|
+
[{ fieldName: "title", type: "STRING" }],
|
|
2059
|
+
expect.objectContaining({ folderKey: "folder-guid-2" }),
|
|
2060
|
+
);
|
|
2061
|
+
});
|
|
2062
|
+
|
|
2063
|
+
it("should forward --folder-key into updateById options", async () => {
|
|
2064
|
+
const sdk = mockSdk();
|
|
2065
|
+
vi.mocked(readJsonInput).mockResolvedValue({
|
|
2066
|
+
displayName: "Renamed",
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
const program = buildProgram();
|
|
2070
|
+
await program.parseAsync([
|
|
2071
|
+
"node",
|
|
2072
|
+
"test",
|
|
2073
|
+
"entities",
|
|
2074
|
+
"update",
|
|
2075
|
+
"ent-1",
|
|
2076
|
+
"--body",
|
|
2077
|
+
'{"displayName":"Renamed"}',
|
|
2078
|
+
"--folder-key",
|
|
2079
|
+
"folder-guid-3",
|
|
2080
|
+
]);
|
|
2081
|
+
|
|
2082
|
+
expect(sdk.entities.updateById).toHaveBeenCalledWith(
|
|
2083
|
+
"ent-1",
|
|
2084
|
+
expect.objectContaining({
|
|
2085
|
+
displayName: "Renamed",
|
|
2086
|
+
folderKey: "folder-guid-3",
|
|
2087
|
+
}),
|
|
2088
|
+
);
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
it("should forward --folder-key to deleteById", async () => {
|
|
2092
|
+
const sdk = mockSdk();
|
|
2093
|
+
|
|
2094
|
+
const program = buildProgram();
|
|
2095
|
+
await program.parseAsync([
|
|
2096
|
+
"node",
|
|
2097
|
+
"test",
|
|
2098
|
+
"entities",
|
|
2099
|
+
"delete",
|
|
2100
|
+
"ent-1",
|
|
2101
|
+
"--confirm",
|
|
2102
|
+
"--reason",
|
|
2103
|
+
"cleanup",
|
|
2104
|
+
"--folder-key",
|
|
2105
|
+
"folder-guid-4",
|
|
2106
|
+
]);
|
|
2107
|
+
|
|
2108
|
+
expect(sdk.entities.deleteById).toHaveBeenCalledWith("ent-1", {
|
|
2109
|
+
folderKey: "folder-guid-4",
|
|
2110
|
+
});
|
|
2111
|
+
});
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
describe("entities list --include-folders", () => {
|
|
2115
|
+
beforeEach(() => {
|
|
2116
|
+
vi.resetAllMocks();
|
|
2117
|
+
process.exitCode = undefined;
|
|
2118
|
+
});
|
|
2119
|
+
|
|
2120
|
+
it("should call getAll with no options when neither --folder-key nor --include-folders is passed", async () => {
|
|
2121
|
+
const sdk = mockSdk();
|
|
2122
|
+
const program = buildProgram();
|
|
2123
|
+
await program.parseAsync(["node", "test", "entities", "list"]);
|
|
2124
|
+
expect(sdk.entities.getAll).toHaveBeenCalledWith(undefined);
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
it("should forward --include-folders as includeFolderEntities: true", async () => {
|
|
2128
|
+
const sdk = mockSdk();
|
|
2129
|
+
const program = buildProgram();
|
|
2130
|
+
await program.parseAsync([
|
|
2131
|
+
"node",
|
|
2132
|
+
"test",
|
|
2133
|
+
"entities",
|
|
2134
|
+
"list",
|
|
2135
|
+
"--include-folders",
|
|
2136
|
+
]);
|
|
2137
|
+
expect(sdk.entities.getAll).toHaveBeenCalledWith({
|
|
2138
|
+
includeFolderEntities: true,
|
|
2139
|
+
});
|
|
2140
|
+
});
|
|
2141
|
+
|
|
2142
|
+
it("should forward --folder-key as folderKey to getAll", async () => {
|
|
2143
|
+
const sdk = mockSdk();
|
|
2144
|
+
const program = buildProgram();
|
|
2145
|
+
await program.parseAsync([
|
|
2146
|
+
"node",
|
|
2147
|
+
"test",
|
|
2148
|
+
"entities",
|
|
2149
|
+
"list",
|
|
2150
|
+
"--folder-key",
|
|
2151
|
+
"folder-guid-1",
|
|
2152
|
+
]);
|
|
2153
|
+
expect(sdk.entities.getAll).toHaveBeenCalledWith({
|
|
2154
|
+
folderKey: "folder-guid-1",
|
|
2155
|
+
});
|
|
2156
|
+
});
|
|
2157
|
+
|
|
2158
|
+
it("should reject --folder-key combined with --include-folders", async () => {
|
|
2159
|
+
const sdk = mockSdk();
|
|
2160
|
+
const program = buildProgram();
|
|
2161
|
+
await program.parseAsync([
|
|
2162
|
+
"node",
|
|
2163
|
+
"test",
|
|
2164
|
+
"entities",
|
|
2165
|
+
"list",
|
|
2166
|
+
"--folder-key",
|
|
2167
|
+
"folder-guid-1",
|
|
2168
|
+
"--include-folders",
|
|
2169
|
+
]);
|
|
2170
|
+
expect(sdk.entities.getAll).not.toHaveBeenCalled();
|
|
2171
|
+
expect(process.exitCode).toBe(1);
|
|
2172
|
+
});
|
|
2173
|
+
});
|
package/src/commands/entities.ts
CHANGED
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
} from "@uipath/common";
|
|
10
10
|
import type {
|
|
11
11
|
EntityCreateFieldOptions,
|
|
12
|
+
EntityGetAllOptions,
|
|
12
13
|
EntityServiceModel,
|
|
13
14
|
EntityUpdateByIdOptions,
|
|
14
15
|
ExternalSourceFields,
|
|
15
16
|
RawEntityGetResponse,
|
|
16
17
|
} from "@uipath/uipath-typescript";
|
|
17
18
|
import { EntityFieldDataType } from "@uipath/uipath-typescript";
|
|
18
|
-
import type
|
|
19
|
+
import { type Command, Option } from "commander";
|
|
19
20
|
import { readJsonInput } from "../utils/input";
|
|
20
21
|
import { fail, requireDestructiveConfirmation } from "../utils/output";
|
|
21
22
|
import { connectOrFail } from "../utils/sdk-client";
|
|
@@ -23,35 +24,45 @@ import { connectOrFail } from "../utils/sdk-client";
|
|
|
23
24
|
interface ListOptions {
|
|
24
25
|
tenant?: string;
|
|
25
26
|
nativeOnly?: boolean;
|
|
27
|
+
folderKey?: string;
|
|
28
|
+
includeFolders?: boolean;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
interface GetOptions {
|
|
29
32
|
tenant?: string;
|
|
33
|
+
folderKey?: string;
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
interface CreateOptions {
|
|
33
37
|
tenant?: string;
|
|
34
38
|
file?: string;
|
|
35
39
|
body?: string;
|
|
40
|
+
folderKey?: string;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
43
|
interface UpdateEntityOptions {
|
|
39
44
|
tenant?: string;
|
|
40
45
|
file?: string;
|
|
41
46
|
body?: string;
|
|
47
|
+
yes?: boolean;
|
|
42
48
|
confirm?: boolean;
|
|
43
49
|
reason?: string;
|
|
50
|
+
folderKey?: string;
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
interface DeleteOptions {
|
|
47
54
|
tenant?: string;
|
|
55
|
+
yes?: boolean;
|
|
48
56
|
confirm?: boolean;
|
|
49
57
|
reason?: string;
|
|
58
|
+
folderKey?: string;
|
|
50
59
|
}
|
|
51
60
|
|
|
52
61
|
const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
53
62
|
{
|
|
54
|
-
Description:
|
|
63
|
+
Description:
|
|
64
|
+
"List Data Fabric entities. Each entity includes the 'folderId' it lives in — pass that as '--folder-key' on follow-up commands targeting folder-scoped entities. " +
|
|
65
|
+
"Scope modes: omit both flags to see only tenant-level entities (the default); pass '--folder-key <uuid>' to see only that folder's entities; pass '--include-folders' to see tenant + folder entities together. '--folder-key' and '--include-folders' are mutually exclusive.",
|
|
55
66
|
Command: "uip df entities list --native-only",
|
|
56
67
|
Output: {
|
|
57
68
|
Code: "EntityList",
|
|
@@ -62,6 +73,7 @@ const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
|
62
73
|
displayName: "Invoice",
|
|
63
74
|
entityType: "Standard",
|
|
64
75
|
description: "Invoice records",
|
|
76
|
+
folderId: "f1000000-0000-0000-0000-000000000001",
|
|
65
77
|
isRbacEnabled: false,
|
|
66
78
|
fields: [
|
|
67
79
|
{
|
|
@@ -144,6 +156,19 @@ const ENTITIES_CREATE_EXAMPLES: CommandExample[] = [
|
|
|
144
156
|
Data: { ID: "a1b2c3d4-0000-0000-0000-000000000004" },
|
|
145
157
|
},
|
|
146
158
|
},
|
|
159
|
+
{
|
|
160
|
+
Description:
|
|
161
|
+
"Create a folder-scoped entity with cross-folder references. " +
|
|
162
|
+
"Pass '--folder-key' to place the new entity in a folder. " +
|
|
163
|
+
"Per-field 'referenceFolderKey' points a RELATIONSHIP, FILE, or CHOICE_SET_* field at a target that lives in a different folder (or at the tenant level when omitted). " +
|
|
164
|
+
"Get target entity/field IDs from 'df entities list --folder-key <key>' and 'df entities get <id> --folder-key <key>'; get choice-set IDs from 'df choice-sets list --folder-key <key>' (omit '--folder-key' for tenant-level choice sets).",
|
|
165
|
+
Command:
|
|
166
|
+
'uip df entities create OrderLine --folder-key f1000000-0000-0000-0000-000000000050 --body \'{"displayName":"Order Line","fields":[{"fieldName":"order","type":"RELATIONSHIP","referenceEntityId":"a1b2c3d4-0000-0000-0000-000000000010","referenceFieldId":"f1000000-0000-0000-0000-000000000100","referenceFolderKey":"f1000000-0000-0000-0000-000000000060","isRequired":true},{"fieldName":"userType","type":"CHOICE_SET_SINGLE","choiceSetId":"c1d2e3f4-0000-0000-0000-000000000077"}]}\'',
|
|
167
|
+
Output: {
|
|
168
|
+
Code: "EntityCreated",
|
|
169
|
+
Data: { ID: "a1b2c3d4-0000-0000-0000-000000000005" },
|
|
170
|
+
},
|
|
171
|
+
},
|
|
147
172
|
];
|
|
148
173
|
|
|
149
174
|
const VALID_FIELD_TYPES = new Set(Object.values(EntityFieldDataType));
|
|
@@ -193,12 +218,43 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
193
218
|
"--native-only",
|
|
194
219
|
"Show only native entities (exclude federated entities with external connections)",
|
|
195
220
|
)
|
|
221
|
+
.option(
|
|
222
|
+
"--folder-key <key>",
|
|
223
|
+
"Folder key (GUID) to scope the listing to a specific folder",
|
|
224
|
+
)
|
|
225
|
+
.option(
|
|
226
|
+
"--include-folders",
|
|
227
|
+
"List tenant-level entities together with entities from every folder you can see. Mutually exclusive with --folder-key.",
|
|
228
|
+
)
|
|
196
229
|
.examples(ENTITIES_LIST_EXAMPLES)
|
|
197
230
|
.trackedAction(processContext, async (options: ListOptions) => {
|
|
231
|
+
if (
|
|
232
|
+
options.folderKey !== undefined &&
|
|
233
|
+
options.includeFolders === true
|
|
234
|
+
) {
|
|
235
|
+
return fail(
|
|
236
|
+
"--folder-key and --include-folders are mutually exclusive",
|
|
237
|
+
"Use --folder-key to list a single folder, or --include-folders to list tenant + folder entities together.",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
198
241
|
const sdk = await connectOrFail(options.tenant);
|
|
199
242
|
if (!sdk) return;
|
|
200
243
|
|
|
201
|
-
const
|
|
244
|
+
const listOpts: EntityGetAllOptions = {
|
|
245
|
+
...(options.folderKey !== undefined && {
|
|
246
|
+
folderKey: options.folderKey,
|
|
247
|
+
}),
|
|
248
|
+
...(options.includeFolders === true && {
|
|
249
|
+
includeFolderEntities: true,
|
|
250
|
+
}),
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const [listError, result] = await catchError(
|
|
254
|
+
sdk.entities.getAll(
|
|
255
|
+
Object.keys(listOpts).length > 0 ? listOpts : undefined,
|
|
256
|
+
),
|
|
257
|
+
);
|
|
202
258
|
|
|
203
259
|
if (listError) {
|
|
204
260
|
return fail(
|
|
@@ -229,6 +285,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
229
285
|
.addOption(
|
|
230
286
|
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
231
287
|
)
|
|
288
|
+
.option(
|
|
289
|
+
"--folder-key <key>",
|
|
290
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
291
|
+
)
|
|
232
292
|
.examples(ENTITIES_GET_EXAMPLES)
|
|
233
293
|
.trackedAction(
|
|
234
294
|
processContext,
|
|
@@ -237,7 +297,12 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
237
297
|
if (!sdk) return;
|
|
238
298
|
|
|
239
299
|
const [getError, entity] = await catchError(
|
|
240
|
-
sdk.entities.getById(
|
|
300
|
+
sdk.entities.getById(
|
|
301
|
+
id,
|
|
302
|
+
options.folderKey !== undefined
|
|
303
|
+
? { folderKey: options.folderKey }
|
|
304
|
+
: undefined,
|
|
305
|
+
),
|
|
241
306
|
);
|
|
242
307
|
|
|
243
308
|
if (getError) {
|
|
@@ -279,6 +344,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
279
344
|
"Path to JSON file with entity definition (fields array required; displayName, description, isRbacEnabled optional)",
|
|
280
345
|
)
|
|
281
346
|
.option("--body <json>", "Inline JSON entity definition")
|
|
347
|
+
.option(
|
|
348
|
+
"--folder-key <key>",
|
|
349
|
+
"Folder key (GUID) of the target folder (for folder-scoped entities)",
|
|
350
|
+
)
|
|
282
351
|
.examples(ENTITIES_CREATE_EXAMPLES)
|
|
283
352
|
.trackedAction(
|
|
284
353
|
processContext,
|
|
@@ -351,6 +420,9 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
351
420
|
...(definition.isRbacEnabled !== undefined && {
|
|
352
421
|
isRbacEnabled: definition.isRbacEnabled as boolean,
|
|
353
422
|
}),
|
|
423
|
+
...(options.folderKey !== undefined && {
|
|
424
|
+
folderKey: options.folderKey,
|
|
425
|
+
}),
|
|
354
426
|
};
|
|
355
427
|
|
|
356
428
|
const entityService: EntityServiceModel = sdk.entities;
|
|
@@ -390,15 +462,25 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
390
462
|
"-f, --file <path>",
|
|
391
463
|
"Path to JSON file with update options (addFields, updateFields, removeFields, displayName, description, isRbacEnabled)",
|
|
392
464
|
)
|
|
393
|
-
.option("--body <json>", "Inline JSON update options")
|
|
394
465
|
.option(
|
|
395
|
-
"--
|
|
466
|
+
"--body <json>",
|
|
467
|
+
"Inline JSON update options (use `-` to read from stdin)",
|
|
468
|
+
)
|
|
469
|
+
.option(
|
|
470
|
+
"-y, --yes",
|
|
396
471
|
"Required when 'removeFields' is non-empty — acknowledges the field deletion is irreversible",
|
|
397
472
|
)
|
|
473
|
+
.addOption(
|
|
474
|
+
new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
|
|
475
|
+
)
|
|
398
476
|
.option(
|
|
399
477
|
"--reason <reason>",
|
|
400
478
|
"Required when 'removeFields' is non-empty — echoed back in the response so the caller can log it",
|
|
401
479
|
)
|
|
480
|
+
.option(
|
|
481
|
+
"--folder-key <key>",
|
|
482
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
483
|
+
)
|
|
402
484
|
.trackedAction(
|
|
403
485
|
processContext,
|
|
404
486
|
async (id: string, options: UpdateEntityOptions) => {
|
|
@@ -482,6 +564,7 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
482
564
|
|
|
483
565
|
const reason = requireDestructiveConfirmation(
|
|
484
566
|
options,
|
|
567
|
+
`remove fields from entity '${id}'`,
|
|
485
568
|
'Pass --reason "<text>" to record why fields are being removed.',
|
|
486
569
|
);
|
|
487
570
|
if (reason === null) return;
|
|
@@ -561,8 +644,14 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
561
644
|
if (!sdk) return;
|
|
562
645
|
|
|
563
646
|
const entityService: EntityServiceModel = sdk.entities;
|
|
647
|
+
const updatePayload: EntityUpdateByIdOptions = {
|
|
648
|
+
...pickKnownUpdateKeys(input),
|
|
649
|
+
...(options.folderKey !== undefined && {
|
|
650
|
+
folderKey: options.folderKey,
|
|
651
|
+
}),
|
|
652
|
+
};
|
|
564
653
|
const [updateError] = await catchError(
|
|
565
|
-
entityService.updateById(id,
|
|
654
|
+
entityService.updateById(id, updatePayload),
|
|
566
655
|
);
|
|
567
656
|
|
|
568
657
|
if (updateError) {
|
|
@@ -593,17 +682,25 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
593
682
|
.addOption(
|
|
594
683
|
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
595
684
|
)
|
|
596
|
-
.option("--
|
|
685
|
+
.option("-y, --yes", "Acknowledge this is an irreversible operation")
|
|
686
|
+
.addOption(
|
|
687
|
+
new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
|
|
688
|
+
)
|
|
597
689
|
.option(
|
|
598
690
|
"--reason <reason>",
|
|
599
691
|
"Reason for the deletion — echoed back in the response so the caller can log it",
|
|
600
692
|
)
|
|
693
|
+
.option(
|
|
694
|
+
"--folder-key <key>",
|
|
695
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
696
|
+
)
|
|
601
697
|
.examples(ENTITIES_DELETE_EXAMPLES)
|
|
602
698
|
.trackedAction(
|
|
603
699
|
processContext,
|
|
604
700
|
async (id: string, options: DeleteOptions) => {
|
|
605
701
|
const reason = requireDestructiveConfirmation(
|
|
606
702
|
options,
|
|
703
|
+
`delete entity '${id}'`,
|
|
607
704
|
'Pass --reason "<text>" to record why the entity is being deleted.',
|
|
608
705
|
);
|
|
609
706
|
if (reason === null) return;
|
|
@@ -613,7 +710,12 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
613
710
|
|
|
614
711
|
const entityService: EntityServiceModel = sdk.entities;
|
|
615
712
|
const [deleteError] = await catchError(
|
|
616
|
-
entityService.deleteById(
|
|
713
|
+
entityService.deleteById(
|
|
714
|
+
id,
|
|
715
|
+
options.folderKey !== undefined
|
|
716
|
+
? { folderKey: options.folderKey }
|
|
717
|
+
: undefined,
|
|
718
|
+
),
|
|
617
719
|
);
|
|
618
720
|
|
|
619
721
|
if (deleteError) {
|