@uipath/data-fabric-tool 1.1.0 → 1.196.0
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/tool.js +6246 -5365
- package/package.json +16 -24
- package/src/commands/choice-sets.spec.ts +571 -83
- package/src/commands/choice-sets.ts +561 -147
- package/src/commands/entities.spec.ts +109 -159
- package/src/commands/entities.ts +181 -372
- package/src/commands/files.spec.ts +62 -34
- package/src/commands/files.ts +51 -88
- package/src/commands/records.spec.ts +188 -206
- package/src/commands/records.ts +133 -330
- package/src/tool.ts +5 -1
- package/src/utils/input.spec.ts +127 -0
- package/src/utils/input.ts +30 -1
- package/src/utils/output.spec.ts +91 -0
- package/src/utils/output.ts +69 -0
- package/src/utils/sdk-client.spec.ts +59 -0
- package/src/utils/sdk-client.ts +23 -0
- package/src/utils/pagination.ts +0 -10
|
@@ -3,6 +3,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
3
3
|
|
|
4
4
|
vi.mock("../utils/sdk-client", () => ({
|
|
5
5
|
createDataFabricClient: vi.fn(),
|
|
6
|
+
connectOrFail: vi.fn(),
|
|
6
7
|
}));
|
|
7
8
|
|
|
8
9
|
vi.mock("@uipath/common", async (importOriginal) => {
|
|
@@ -27,7 +28,7 @@ vi.mock("@uipath/filesystem", () => ({
|
|
|
27
28
|
}));
|
|
28
29
|
|
|
29
30
|
import { OutputFormatter } from "@uipath/common";
|
|
30
|
-
import {
|
|
31
|
+
import { connectOrFail } from "../utils/sdk-client";
|
|
31
32
|
import { registerFilesCommand } from "./files";
|
|
32
33
|
|
|
33
34
|
function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
@@ -39,7 +40,7 @@ function mockSdk(overrides: Record<string, unknown> = {}) {
|
|
|
39
40
|
...overrides,
|
|
40
41
|
},
|
|
41
42
|
};
|
|
42
|
-
vi.mocked(
|
|
43
|
+
vi.mocked(connectOrFail).mockResolvedValue(sdk as never);
|
|
43
44
|
return sdk;
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -134,22 +135,17 @@ describe("files upload", () => {
|
|
|
134
135
|
);
|
|
135
136
|
});
|
|
136
137
|
|
|
137
|
-
it("should
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
);
|
|
138
|
+
it("should bail when SDK connection fails on upload", async () => {
|
|
139
|
+
const sdk = mockSdk();
|
|
140
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
141
141
|
mockFs.readFile.mockResolvedValue(new Uint8Array([1, 2, 3]));
|
|
142
142
|
const program = buildProgram();
|
|
143
143
|
await runCommand(
|
|
144
144
|
program,
|
|
145
145
|
"files upload entity-1 record-1 attachment --file /tmp/report.pdf",
|
|
146
146
|
);
|
|
147
|
-
expect(
|
|
148
|
-
|
|
149
|
-
Result: "Failure",
|
|
150
|
-
Message: "Error connecting to Data Fabric",
|
|
151
|
-
}),
|
|
152
|
-
);
|
|
147
|
+
expect(sdk.entities.uploadAttachment).not.toHaveBeenCalled();
|
|
148
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
153
149
|
});
|
|
154
150
|
|
|
155
151
|
it("should error when upload API fails", async () => {
|
|
@@ -218,21 +214,16 @@ describe("files download", () => {
|
|
|
218
214
|
);
|
|
219
215
|
});
|
|
220
216
|
|
|
221
|
-
it("should
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
);
|
|
217
|
+
it("should bail when SDK connection fails", async () => {
|
|
218
|
+
const sdk = mockSdk();
|
|
219
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
225
220
|
const program = buildProgram();
|
|
226
221
|
await runCommand(
|
|
227
222
|
program,
|
|
228
223
|
"files download entity-1 record-1 attachment",
|
|
229
224
|
);
|
|
230
|
-
expect(
|
|
231
|
-
|
|
232
|
-
Result: "Failure",
|
|
233
|
-
Message: "Error connecting to Data Fabric",
|
|
234
|
-
}),
|
|
235
|
-
);
|
|
225
|
+
expect(sdk.entities.downloadAttachment).not.toHaveBeenCalled();
|
|
226
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
236
227
|
});
|
|
237
228
|
});
|
|
238
229
|
|
|
@@ -246,7 +237,31 @@ describe("files delete", () => {
|
|
|
246
237
|
it("should delete and return FileDeleted on success", async () => {
|
|
247
238
|
const sdk = mockSdk();
|
|
248
239
|
const program = buildProgram();
|
|
249
|
-
await runCommand(
|
|
240
|
+
await runCommand(
|
|
241
|
+
program,
|
|
242
|
+
"files delete entity-1 record-1 attachment --yes --reason cleanup",
|
|
243
|
+
);
|
|
244
|
+
expect(sdk.entities.deleteAttachment).toHaveBeenCalledWith(
|
|
245
|
+
"entity-1",
|
|
246
|
+
"record-1",
|
|
247
|
+
"attachment",
|
|
248
|
+
);
|
|
249
|
+
expect(OutputFormatter.success).toHaveBeenCalledWith(
|
|
250
|
+
expect.objectContaining({
|
|
251
|
+
Result: "Success",
|
|
252
|
+
Code: "FileDeleted",
|
|
253
|
+
Data: expect.objectContaining({ Reason: "cleanup" }),
|
|
254
|
+
}),
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it("should accept --confirm as a deprecated alias for --yes", async () => {
|
|
259
|
+
const sdk = mockSdk();
|
|
260
|
+
const program = buildProgram();
|
|
261
|
+
await runCommand(
|
|
262
|
+
program,
|
|
263
|
+
"files delete entity-1 record-1 attachment --confirm --reason cleanup",
|
|
264
|
+
);
|
|
250
265
|
expect(sdk.entities.deleteAttachment).toHaveBeenCalledWith(
|
|
251
266
|
"entity-1",
|
|
252
267
|
"record-1",
|
|
@@ -257,11 +272,26 @@ describe("files delete", () => {
|
|
|
257
272
|
);
|
|
258
273
|
});
|
|
259
274
|
|
|
275
|
+
it("should require confirmation: without --yes, exit 1 and SDK not called", async () => {
|
|
276
|
+
const sdk = mockSdk();
|
|
277
|
+
const program = buildProgram();
|
|
278
|
+
await runCommand(
|
|
279
|
+
program,
|
|
280
|
+
"files delete entity-1 record-1 attachment --reason cleanup",
|
|
281
|
+
);
|
|
282
|
+
expect(sdk.entities.deleteAttachment).not.toHaveBeenCalled();
|
|
283
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
284
|
+
expect(process.exitCode).toBe(1);
|
|
285
|
+
});
|
|
286
|
+
|
|
260
287
|
it("should error when delete API fails", async () => {
|
|
261
288
|
const sdk = mockSdk();
|
|
262
289
|
sdk.entities.deleteAttachment.mockRejectedValue(new Error("Forbidden"));
|
|
263
290
|
const program = buildProgram();
|
|
264
|
-
await runCommand(
|
|
291
|
+
await runCommand(
|
|
292
|
+
program,
|
|
293
|
+
"files delete entity-1 record-1 attachment --yes --reason cleanup",
|
|
294
|
+
);
|
|
265
295
|
expect(OutputFormatter.error).toHaveBeenCalledWith(
|
|
266
296
|
expect.objectContaining({
|
|
267
297
|
Result: "Failure",
|
|
@@ -270,18 +300,16 @@ describe("files delete", () => {
|
|
|
270
300
|
);
|
|
271
301
|
});
|
|
272
302
|
|
|
273
|
-
it("should
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
);
|
|
303
|
+
it("should bail when SDK connection fails on delete", async () => {
|
|
304
|
+
const sdk = mockSdk();
|
|
305
|
+
vi.mocked(connectOrFail).mockResolvedValue(undefined);
|
|
277
306
|
const program = buildProgram();
|
|
278
|
-
await runCommand(
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
Result: "Failure",
|
|
282
|
-
Message: "Error connecting to Data Fabric",
|
|
283
|
-
}),
|
|
307
|
+
await runCommand(
|
|
308
|
+
program,
|
|
309
|
+
"files delete entity-1 record-1 attachment --yes --reason cleanup",
|
|
284
310
|
);
|
|
311
|
+
expect(sdk.entities.deleteAttachment).not.toHaveBeenCalled();
|
|
312
|
+
expect(OutputFormatter.success).not.toHaveBeenCalled();
|
|
285
313
|
});
|
|
286
314
|
});
|
|
287
315
|
|
package/src/commands/files.ts
CHANGED
|
@@ -8,9 +8,10 @@ import {
|
|
|
8
8
|
RESULTS,
|
|
9
9
|
} from "@uipath/common";
|
|
10
10
|
import { getFileSystem } from "@uipath/filesystem";
|
|
11
|
-
import type
|
|
11
|
+
import { type Command, Option } from "commander";
|
|
12
12
|
import { readFileBinary } from "../utils/input";
|
|
13
|
-
import {
|
|
13
|
+
import { fail, requireDestructiveConfirmation } from "../utils/output";
|
|
14
|
+
import { connectOrFail } from "../utils/sdk-client";
|
|
14
15
|
|
|
15
16
|
interface UploadOptions {
|
|
16
17
|
tenant?: string;
|
|
@@ -24,6 +25,9 @@ interface DownloadOptions {
|
|
|
24
25
|
|
|
25
26
|
interface DeleteOptions {
|
|
26
27
|
tenant?: string;
|
|
28
|
+
yes?: boolean;
|
|
29
|
+
confirm?: boolean;
|
|
30
|
+
reason?: string;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
const FILES_UPLOAD_EXAMPLES: CommandExample[] = [
|
|
@@ -101,41 +105,21 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
101
105
|
options: UploadOptions,
|
|
102
106
|
) => {
|
|
103
107
|
if (!options.file) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
});
|
|
109
|
-
processContext.exit(1);
|
|
110
|
-
return;
|
|
108
|
+
return fail(
|
|
109
|
+
"A file path is required",
|
|
110
|
+
"Provide a file path via --file.",
|
|
111
|
+
);
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
if (clientError) {
|
|
118
|
-
OutputFormatter.error({
|
|
119
|
-
Result: RESULTS.Failure,
|
|
120
|
-
Message: "Error connecting to Data Fabric",
|
|
121
|
-
Instructions: await extractErrorMessage(clientError),
|
|
122
|
-
});
|
|
123
|
-
processContext.exit(1);
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
114
|
+
const sdk = await connectOrFail(options.tenant);
|
|
115
|
+
if (!sdk) return;
|
|
126
116
|
|
|
127
117
|
const [readError, fileContent] = await catchError(
|
|
128
118
|
readFileBinary(options.file),
|
|
129
119
|
);
|
|
130
120
|
|
|
131
121
|
if (readError) {
|
|
132
|
-
|
|
133
|
-
Result: RESULTS.Failure,
|
|
134
|
-
Message: "Error reading file",
|
|
135
|
-
Instructions: readError.message,
|
|
136
|
-
});
|
|
137
|
-
processContext.exit(1);
|
|
138
|
-
return;
|
|
122
|
+
return fail("Error reading file", readError.message);
|
|
139
123
|
}
|
|
140
124
|
|
|
141
125
|
const fsInst = getFileSystem();
|
|
@@ -154,13 +138,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
154
138
|
);
|
|
155
139
|
|
|
156
140
|
if (uploadError) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
processContext.exit(1);
|
|
163
|
-
return;
|
|
141
|
+
return fail(
|
|
142
|
+
`Error uploading file to field '${fieldName}'`,
|
|
143
|
+
await extractErrorMessage(uploadError),
|
|
144
|
+
);
|
|
164
145
|
}
|
|
165
146
|
|
|
166
147
|
OutputFormatter.success({
|
|
@@ -198,19 +179,8 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
198
179
|
fieldName: string,
|
|
199
180
|
options: DownloadOptions,
|
|
200
181
|
) => {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
);
|
|
204
|
-
|
|
205
|
-
if (clientError) {
|
|
206
|
-
OutputFormatter.error({
|
|
207
|
-
Result: RESULTS.Failure,
|
|
208
|
-
Message: "Error connecting to Data Fabric",
|
|
209
|
-
Instructions: await extractErrorMessage(clientError),
|
|
210
|
-
});
|
|
211
|
-
processContext.exit(1);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
182
|
+
const sdk = await connectOrFail(options.tenant);
|
|
183
|
+
if (!sdk) return;
|
|
214
184
|
|
|
215
185
|
const [downloadError, blob] = await catchError(
|
|
216
186
|
sdk.entities.downloadAttachment(
|
|
@@ -221,13 +191,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
221
191
|
);
|
|
222
192
|
|
|
223
193
|
if (downloadError) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
229
|
-
processContext.exit(1);
|
|
230
|
-
return;
|
|
194
|
+
return fail(
|
|
195
|
+
`Error downloading file from field '${fieldName}'`,
|
|
196
|
+
await extractErrorMessage(downloadError),
|
|
197
|
+
);
|
|
231
198
|
}
|
|
232
199
|
|
|
233
200
|
const outputPath =
|
|
@@ -238,13 +205,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
238
205
|
);
|
|
239
206
|
|
|
240
207
|
if (bufferError) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
});
|
|
246
|
-
processContext.exit(1);
|
|
247
|
-
return;
|
|
208
|
+
return fail(
|
|
209
|
+
"Error reading downloaded file content",
|
|
210
|
+
bufferError.message,
|
|
211
|
+
);
|
|
248
212
|
}
|
|
249
213
|
|
|
250
214
|
const [writeError] = await catchError(
|
|
@@ -252,13 +216,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
252
216
|
);
|
|
253
217
|
|
|
254
218
|
if (writeError) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
});
|
|
260
|
-
processContext.exit(1);
|
|
261
|
-
return;
|
|
219
|
+
return fail(
|
|
220
|
+
"Error writing downloaded file",
|
|
221
|
+
writeError.message,
|
|
222
|
+
);
|
|
262
223
|
}
|
|
263
224
|
|
|
264
225
|
OutputFormatter.success({
|
|
@@ -283,6 +244,14 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
283
244
|
.addOption(
|
|
284
245
|
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
285
246
|
)
|
|
247
|
+
.option("-y, --yes", "Acknowledge this is an irreversible operation")
|
|
248
|
+
.addOption(
|
|
249
|
+
new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
|
|
250
|
+
)
|
|
251
|
+
.option(
|
|
252
|
+
"--reason <reason>",
|
|
253
|
+
"Reason for the deletion — echoed back in the response so the caller can log it",
|
|
254
|
+
)
|
|
286
255
|
.examples(FILES_DELETE_EXAMPLES)
|
|
287
256
|
.trackedAction(
|
|
288
257
|
processContext,
|
|
@@ -292,19 +261,15 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
292
261
|
fieldName: string,
|
|
293
262
|
options: DeleteOptions,
|
|
294
263
|
) => {
|
|
295
|
-
const
|
|
296
|
-
|
|
264
|
+
const reason = requireDestructiveConfirmation(
|
|
265
|
+
options,
|
|
266
|
+
`delete the file in field '${fieldName}'`,
|
|
267
|
+
'Pass --reason "<text>" to record why the file is being deleted.',
|
|
297
268
|
);
|
|
269
|
+
if (reason === null) return;
|
|
298
270
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
Result: RESULTS.Failure,
|
|
302
|
-
Message: "Error connecting to Data Fabric",
|
|
303
|
-
Instructions: await extractErrorMessage(clientError),
|
|
304
|
-
});
|
|
305
|
-
processContext.exit(1);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
271
|
+
const sdk = await connectOrFail(options.tenant);
|
|
272
|
+
if (!sdk) return;
|
|
308
273
|
|
|
309
274
|
const [deleteError] = await catchError(
|
|
310
275
|
sdk.entities.deleteAttachment(
|
|
@@ -315,13 +280,10 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
315
280
|
);
|
|
316
281
|
|
|
317
282
|
if (deleteError) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
});
|
|
323
|
-
processContext.exit(1);
|
|
324
|
-
return;
|
|
283
|
+
return fail(
|
|
284
|
+
`Error deleting file from field '${fieldName}'`,
|
|
285
|
+
await extractErrorMessage(deleteError),
|
|
286
|
+
);
|
|
325
287
|
}
|
|
326
288
|
|
|
327
289
|
OutputFormatter.success({
|
|
@@ -331,6 +293,7 @@ export const registerFilesCommand = (program: Command) => {
|
|
|
331
293
|
EntityId: entityId,
|
|
332
294
|
RecordId: recordId,
|
|
333
295
|
FieldName: fieldName,
|
|
296
|
+
Reason: reason,
|
|
334
297
|
},
|
|
335
298
|
});
|
|
336
299
|
},
|