@uipath/data-fabric-tool 1.196.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 +1820 -2205
- package/package.json +2 -2
- package/src/commands/choice-sets.spec.ts +218 -2
- package/src/commands/choice-sets.ts +113 -5
- package/src/commands/entities.spec.ts +252 -1
- package/src/commands/entities.ts +93 -7
- package/src/commands/files.spec.ts +63 -0
- package/src/commands/files.ts +24 -0
- package/src/commands/records.spec.ts +236 -0
- package/src/commands/records.ts +96 -13
|
@@ -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 = [
|
|
@@ -1772,6 +1851,7 @@ describe("entities delete", () => {
|
|
|
1772
1851
|
|
|
1773
1852
|
expect(sdk.entities.deleteById).toHaveBeenCalledWith(
|
|
1774
1853
|
"entity-to-delete",
|
|
1854
|
+
undefined,
|
|
1775
1855
|
);
|
|
1776
1856
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1777
1857
|
expect.objectContaining({
|
|
@@ -1819,7 +1899,10 @@ describe("entities delete", () => {
|
|
|
1819
1899
|
"cleanup",
|
|
1820
1900
|
]);
|
|
1821
1901
|
|
|
1822
|
-
expect(sdk.entities.deleteById).toHaveBeenCalledWith(
|
|
1902
|
+
expect(sdk.entities.deleteById).toHaveBeenCalledWith(
|
|
1903
|
+
"entity-id",
|
|
1904
|
+
undefined,
|
|
1905
|
+
);
|
|
1823
1906
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
1824
1907
|
expect.objectContaining({ Result: "Success" }),
|
|
1825
1908
|
);
|
|
@@ -1920,3 +2003,171 @@ describe("entities delete", () => {
|
|
|
1920
2003
|
expect(process.exitCode).toBe(1);
|
|
1921
2004
|
});
|
|
1922
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,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@uipath/common";
|
|
10
10
|
import type {
|
|
11
11
|
EntityCreateFieldOptions,
|
|
12
|
+
EntityGetAllOptions,
|
|
12
13
|
EntityServiceModel,
|
|
13
14
|
EntityUpdateByIdOptions,
|
|
14
15
|
ExternalSourceFields,
|
|
@@ -23,16 +24,20 @@ 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 {
|
|
@@ -42,6 +47,7 @@ interface UpdateEntityOptions {
|
|
|
42
47
|
yes?: boolean;
|
|
43
48
|
confirm?: boolean;
|
|
44
49
|
reason?: string;
|
|
50
|
+
folderKey?: string;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
interface DeleteOptions {
|
|
@@ -49,11 +55,14 @@ interface DeleteOptions {
|
|
|
49
55
|
yes?: boolean;
|
|
50
56
|
confirm?: boolean;
|
|
51
57
|
reason?: string;
|
|
58
|
+
folderKey?: string;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
55
62
|
{
|
|
56
|
-
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.",
|
|
57
66
|
Command: "uip df entities list --native-only",
|
|
58
67
|
Output: {
|
|
59
68
|
Code: "EntityList",
|
|
@@ -64,6 +73,7 @@ const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
|
64
73
|
displayName: "Invoice",
|
|
65
74
|
entityType: "Standard",
|
|
66
75
|
description: "Invoice records",
|
|
76
|
+
folderId: "f1000000-0000-0000-0000-000000000001",
|
|
67
77
|
isRbacEnabled: false,
|
|
68
78
|
fields: [
|
|
69
79
|
{
|
|
@@ -146,6 +156,19 @@ const ENTITIES_CREATE_EXAMPLES: CommandExample[] = [
|
|
|
146
156
|
Data: { ID: "a1b2c3d4-0000-0000-0000-000000000004" },
|
|
147
157
|
},
|
|
148
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
|
+
},
|
|
149
172
|
];
|
|
150
173
|
|
|
151
174
|
const VALID_FIELD_TYPES = new Set(Object.values(EntityFieldDataType));
|
|
@@ -195,12 +218,43 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
195
218
|
"--native-only",
|
|
196
219
|
"Show only native entities (exclude federated entities with external connections)",
|
|
197
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
|
+
)
|
|
198
229
|
.examples(ENTITIES_LIST_EXAMPLES)
|
|
199
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
|
+
|
|
200
241
|
const sdk = await connectOrFail(options.tenant);
|
|
201
242
|
if (!sdk) return;
|
|
202
243
|
|
|
203
|
-
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
|
+
);
|
|
204
258
|
|
|
205
259
|
if (listError) {
|
|
206
260
|
return fail(
|
|
@@ -231,6 +285,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
231
285
|
.addOption(
|
|
232
286
|
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
233
287
|
)
|
|
288
|
+
.option(
|
|
289
|
+
"--folder-key <key>",
|
|
290
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
291
|
+
)
|
|
234
292
|
.examples(ENTITIES_GET_EXAMPLES)
|
|
235
293
|
.trackedAction(
|
|
236
294
|
processContext,
|
|
@@ -239,7 +297,12 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
239
297
|
if (!sdk) return;
|
|
240
298
|
|
|
241
299
|
const [getError, entity] = await catchError(
|
|
242
|
-
sdk.entities.getById(
|
|
300
|
+
sdk.entities.getById(
|
|
301
|
+
id,
|
|
302
|
+
options.folderKey !== undefined
|
|
303
|
+
? { folderKey: options.folderKey }
|
|
304
|
+
: undefined,
|
|
305
|
+
),
|
|
243
306
|
);
|
|
244
307
|
|
|
245
308
|
if (getError) {
|
|
@@ -280,9 +343,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
280
343
|
"-f, --file <path>",
|
|
281
344
|
"Path to JSON file with entity definition (fields array required; displayName, description, isRbacEnabled optional)",
|
|
282
345
|
)
|
|
346
|
+
.option("--body <json>", "Inline JSON entity definition")
|
|
283
347
|
.option(
|
|
284
|
-
"--
|
|
285
|
-
"
|
|
348
|
+
"--folder-key <key>",
|
|
349
|
+
"Folder key (GUID) of the target folder (for folder-scoped entities)",
|
|
286
350
|
)
|
|
287
351
|
.examples(ENTITIES_CREATE_EXAMPLES)
|
|
288
352
|
.trackedAction(
|
|
@@ -356,6 +420,9 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
356
420
|
...(definition.isRbacEnabled !== undefined && {
|
|
357
421
|
isRbacEnabled: definition.isRbacEnabled as boolean,
|
|
358
422
|
}),
|
|
423
|
+
...(options.folderKey !== undefined && {
|
|
424
|
+
folderKey: options.folderKey,
|
|
425
|
+
}),
|
|
359
426
|
};
|
|
360
427
|
|
|
361
428
|
const entityService: EntityServiceModel = sdk.entities;
|
|
@@ -410,6 +477,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
410
477
|
"--reason <reason>",
|
|
411
478
|
"Required when 'removeFields' is non-empty — echoed back in the response so the caller can log it",
|
|
412
479
|
)
|
|
480
|
+
.option(
|
|
481
|
+
"--folder-key <key>",
|
|
482
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
483
|
+
)
|
|
413
484
|
.trackedAction(
|
|
414
485
|
processContext,
|
|
415
486
|
async (id: string, options: UpdateEntityOptions) => {
|
|
@@ -573,8 +644,14 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
573
644
|
if (!sdk) return;
|
|
574
645
|
|
|
575
646
|
const entityService: EntityServiceModel = sdk.entities;
|
|
647
|
+
const updatePayload: EntityUpdateByIdOptions = {
|
|
648
|
+
...pickKnownUpdateKeys(input),
|
|
649
|
+
...(options.folderKey !== undefined && {
|
|
650
|
+
folderKey: options.folderKey,
|
|
651
|
+
}),
|
|
652
|
+
};
|
|
576
653
|
const [updateError] = await catchError(
|
|
577
|
-
entityService.updateById(id,
|
|
654
|
+
entityService.updateById(id, updatePayload),
|
|
578
655
|
);
|
|
579
656
|
|
|
580
657
|
if (updateError) {
|
|
@@ -613,6 +690,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
613
690
|
"--reason <reason>",
|
|
614
691
|
"Reason for the deletion — echoed back in the response so the caller can log it",
|
|
615
692
|
)
|
|
693
|
+
.option(
|
|
694
|
+
"--folder-key <key>",
|
|
695
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
696
|
+
)
|
|
616
697
|
.examples(ENTITIES_DELETE_EXAMPLES)
|
|
617
698
|
.trackedAction(
|
|
618
699
|
processContext,
|
|
@@ -629,7 +710,12 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
629
710
|
|
|
630
711
|
const entityService: EntityServiceModel = sdk.entities;
|
|
631
712
|
const [deleteError] = await catchError(
|
|
632
|
-
entityService.deleteById(
|
|
713
|
+
entityService.deleteById(
|
|
714
|
+
id,
|
|
715
|
+
options.folderKey !== undefined
|
|
716
|
+
? { folderKey: options.folderKey }
|
|
717
|
+
: undefined,
|
|
718
|
+
),
|
|
633
719
|
);
|
|
634
720
|
|
|
635
721
|
if (deleteError) {
|
|
@@ -109,6 +109,7 @@ describe("files upload", () => {
|
|
|
109
109
|
"record-1",
|
|
110
110
|
"attachment",
|
|
111
111
|
expect.any(File),
|
|
112
|
+
undefined,
|
|
112
113
|
);
|
|
113
114
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
114
115
|
expect.objectContaining({
|
|
@@ -132,6 +133,7 @@ describe("files upload", () => {
|
|
|
132
133
|
"record-1",
|
|
133
134
|
"attachment",
|
|
134
135
|
expect.objectContaining({ name: "upload" }),
|
|
136
|
+
undefined,
|
|
135
137
|
);
|
|
136
138
|
});
|
|
137
139
|
|
|
@@ -245,6 +247,7 @@ describe("files delete", () => {
|
|
|
245
247
|
"entity-1",
|
|
246
248
|
"record-1",
|
|
247
249
|
"attachment",
|
|
250
|
+
undefined,
|
|
248
251
|
);
|
|
249
252
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
250
253
|
expect.objectContaining({
|
|
@@ -266,6 +269,7 @@ describe("files delete", () => {
|
|
|
266
269
|
"entity-1",
|
|
267
270
|
"record-1",
|
|
268
271
|
"attachment",
|
|
272
|
+
undefined,
|
|
269
273
|
);
|
|
270
274
|
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
271
275
|
expect.objectContaining({ Result: "Success", Code: "FileDeleted" }),
|
|
@@ -370,3 +374,62 @@ describe("files download edge cases", () => {
|
|
|
370
374
|
);
|
|
371
375
|
});
|
|
372
376
|
});
|
|
377
|
+
|
|
378
|
+
describe("files --folder-key forwarding", () => {
|
|
379
|
+
beforeEach(() => {
|
|
380
|
+
vi.clearAllMocks();
|
|
381
|
+
vi.mocked(OutputFormatter.success).mockReset();
|
|
382
|
+
vi.mocked(OutputFormatter.error).mockReset();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("should forward --folder-key to uploadAttachment", async () => {
|
|
386
|
+
const sdk = mockSdk();
|
|
387
|
+
mockFs.readFile.mockResolvedValue(new Uint8Array([1, 2, 3]));
|
|
388
|
+
const program = buildProgram();
|
|
389
|
+
await runCommand(
|
|
390
|
+
program,
|
|
391
|
+
"files upload entity-1 record-1 attachment --file /tmp/report.pdf --folder-key folder-guid-1",
|
|
392
|
+
);
|
|
393
|
+
expect(sdk.entities.uploadAttachment).toHaveBeenCalledWith(
|
|
394
|
+
"entity-1",
|
|
395
|
+
"record-1",
|
|
396
|
+
"attachment",
|
|
397
|
+
expect.any(File),
|
|
398
|
+
{ folderKey: "folder-guid-1" },
|
|
399
|
+
);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("should forward --folder-key to downloadAttachment", async () => {
|
|
403
|
+
const blob = new Blob(["data"]);
|
|
404
|
+
const sdk = mockSdk({
|
|
405
|
+
downloadAttachment: vi.fn().mockResolvedValue(blob),
|
|
406
|
+
});
|
|
407
|
+
mockFs.writeFile.mockResolvedValue(undefined);
|
|
408
|
+
const program = buildProgram();
|
|
409
|
+
await runCommand(
|
|
410
|
+
program,
|
|
411
|
+
"files download entity-1 record-1 attachment --folder-key folder-guid-2",
|
|
412
|
+
);
|
|
413
|
+
expect(sdk.entities.downloadAttachment).toHaveBeenCalledWith(
|
|
414
|
+
"entity-1",
|
|
415
|
+
"record-1",
|
|
416
|
+
"attachment",
|
|
417
|
+
{ folderKey: "folder-guid-2" },
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it("should forward --folder-key to deleteAttachment", async () => {
|
|
422
|
+
const sdk = mockSdk();
|
|
423
|
+
const program = buildProgram();
|
|
424
|
+
await runCommand(
|
|
425
|
+
program,
|
|
426
|
+
"files delete entity-1 record-1 attachment --yes --reason cleanup --folder-key folder-guid-3",
|
|
427
|
+
);
|
|
428
|
+
expect(sdk.entities.deleteAttachment).toHaveBeenCalledWith(
|
|
429
|
+
"entity-1",
|
|
430
|
+
"record-1",
|
|
431
|
+
"attachment",
|
|
432
|
+
{ folderKey: "folder-guid-3" },
|
|
433
|
+
);
|
|
434
|
+
});
|
|
435
|
+
});
|
package/src/commands/files.ts
CHANGED
|
@@ -16,11 +16,13 @@ import { connectOrFail } from "../utils/sdk-client";
|
|
|
16
16
|
interface UploadOptions {
|
|
17
17
|
tenant?: string;
|
|
18
18
|
file?: string;
|
|
19
|
+
folderKey?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
interface DownloadOptions {
|
|
22
23
|
tenant?: string;
|
|
23
24
|
destination?: string;
|
|
25
|
+
folderKey?: string;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
interface DeleteOptions {
|
|
@@ -28,6 +30,7 @@ interface DeleteOptions {
|
|
|
28
30
|
yes?: boolean;
|
|
29
31
|
confirm?: boolean;
|
|
30
32
|
reason?: string;
|
|
33
|
+
folderKey?: string;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
const FILES_UPLOAD_EXAMPLES: CommandExample[] = [
|
|
@@ -95,6 +98,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
95
98
|
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
96
99
|
)
|
|
97
100
|
.option("-f, --file <path>", "Path to the file to upload")
|
|
101
|
+
.option(
|
|
102
|
+
"--folder-key <key>",
|
|
103
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
104
|
+
)
|
|
98
105
|
.examples(FILES_UPLOAD_EXAMPLES)
|
|
99
106
|
.trackedAction(
|
|
100
107
|
processContext,
|
|
@@ -134,6 +141,9 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
134
141
|
[fileContent as Uint8Array<ArrayBuffer>],
|
|
135
142
|
fileName,
|
|
136
143
|
),
|
|
144
|
+
options.folderKey !== undefined
|
|
145
|
+
? { folderKey: options.folderKey }
|
|
146
|
+
: undefined,
|
|
137
147
|
),
|
|
138
148
|
);
|
|
139
149
|
|
|
@@ -170,6 +180,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
170
180
|
"--destination <path>",
|
|
171
181
|
"Output file path (defaults to <record-id>_<field-name>.bin)",
|
|
172
182
|
)
|
|
183
|
+
.option(
|
|
184
|
+
"--folder-key <key>",
|
|
185
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
186
|
+
)
|
|
173
187
|
.examples(FILES_DOWNLOAD_EXAMPLES)
|
|
174
188
|
.trackedAction(
|
|
175
189
|
processContext,
|
|
@@ -187,6 +201,9 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
187
201
|
entityId,
|
|
188
202
|
recordId,
|
|
189
203
|
fieldName,
|
|
204
|
+
options.folderKey !== undefined
|
|
205
|
+
? { folderKey: options.folderKey }
|
|
206
|
+
: undefined,
|
|
190
207
|
),
|
|
191
208
|
);
|
|
192
209
|
|
|
@@ -252,6 +269,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
252
269
|
"--reason <reason>",
|
|
253
270
|
"Reason for the deletion — echoed back in the response so the caller can log it",
|
|
254
271
|
)
|
|
272
|
+
.option(
|
|
273
|
+
"--folder-key <key>",
|
|
274
|
+
"Folder key (GUID) of the folder containing the entity (for folder-scoped entities)",
|
|
275
|
+
)
|
|
255
276
|
.examples(FILES_DELETE_EXAMPLES)
|
|
256
277
|
.trackedAction(
|
|
257
278
|
processContext,
|
|
@@ -276,6 +297,9 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
276
297
|
entityId,
|
|
277
298
|
recordId,
|
|
278
299
|
fieldName,
|
|
300
|
+
options.folderKey !== undefined
|
|
301
|
+
? { folderKey: options.folderKey }
|
|
302
|
+
: undefined,
|
|
279
303
|
),
|
|
280
304
|
);
|
|
281
305
|
|