@uipath/data-fabric-tool 1.0.4 → 1.195.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/browser.json +3 -0
- package/dist/index.js +20 -43167
- package/dist/tool.js +29174 -28399
- package/package.json +16 -32
- package/src/commands/choice-sets.spec.ts +849 -0
- package/src/commands/choice-sets.ts +690 -0
- package/src/commands/entities.spec.ts +411 -137
- package/src/commands/entities.ts +189 -368
- package/src/commands/files.spec.ts +18 -32
- package/src/commands/files.ts +43 -92
- package/src/commands/records.spec.ts +171 -207
- package/src/commands/records.ts +150 -342
- package/src/index.ts +5 -10
- package/src/tool.ts +6 -0
- package/src/utils/output.spec.ts +78 -0
- package/src/utils/output.ts +51 -0
- package/src/utils/sdk-client.spec.ts +59 -0
- package/src/utils/sdk-client.ts +23 -0
package/src/commands/entities.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type CommandExample,
|
|
3
3
|
catchError,
|
|
4
|
+
createHiddenDeprecatedTenantOption,
|
|
4
5
|
extractErrorMessage,
|
|
5
6
|
OutputFormatter,
|
|
6
7
|
processContext,
|
|
@@ -16,7 +17,8 @@ import type {
|
|
|
16
17
|
import { EntityFieldDataType } from "@uipath/uipath-typescript";
|
|
17
18
|
import type { Command } from "commander";
|
|
18
19
|
import { readJsonInput } from "../utils/input";
|
|
19
|
-
import {
|
|
20
|
+
import { fail, requireDestructiveConfirmation } from "../utils/output";
|
|
21
|
+
import { connectOrFail } from "../utils/sdk-client";
|
|
20
22
|
|
|
21
23
|
interface ListOptions {
|
|
22
24
|
tenant?: string;
|
|
@@ -49,28 +51,25 @@ interface DeleteOptions {
|
|
|
49
51
|
|
|
50
52
|
const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
51
53
|
{
|
|
52
|
-
Description: "List Data Fabric entities",
|
|
54
|
+
Description: "List Data Fabric entities.",
|
|
53
55
|
Command: "uip df entities list --native-only",
|
|
54
56
|
Output: {
|
|
55
57
|
Code: "EntityList",
|
|
56
58
|
Data: [
|
|
57
59
|
{
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Source: "Native",
|
|
72
|
-
Description: "Customer records",
|
|
73
|
-
FieldCount: 6,
|
|
60
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
61
|
+
name: "Invoice",
|
|
62
|
+
displayName: "Invoice",
|
|
63
|
+
entityType: "Standard",
|
|
64
|
+
description: "Invoice records",
|
|
65
|
+
isRbacEnabled: false,
|
|
66
|
+
fields: [
|
|
67
|
+
{
|
|
68
|
+
id: "f1000000-0000-0000-0000-000000000001",
|
|
69
|
+
name: "amount",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
externalFields: [],
|
|
74
73
|
},
|
|
75
74
|
],
|
|
76
75
|
},
|
|
@@ -79,34 +78,35 @@ const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
|
|
|
79
78
|
|
|
80
79
|
const ENTITIES_GET_EXAMPLES: CommandExample[] = [
|
|
81
80
|
{
|
|
82
|
-
Description: "Get entity schema by ID",
|
|
81
|
+
Description: "Get entity schema by ID.",
|
|
83
82
|
Command: "uip df entities get a1b2c3d4-0000-0000-0000-000000000001",
|
|
84
83
|
Output: {
|
|
85
84
|
Code: "EntitySchema",
|
|
86
85
|
Data: {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
ID: "f1000000-0000-0000-0000-000000000001",
|
|
95
|
-
Name: "id",
|
|
96
|
-
DisplayName: "ID",
|
|
97
|
-
Type: "Guid",
|
|
98
|
-
Required: true,
|
|
99
|
-
PrimaryKey: true,
|
|
100
|
-
System: true,
|
|
101
|
-
},
|
|
86
|
+
id: "a1b2c3d4-0000-0000-0000-000000000001",
|
|
87
|
+
name: "Invoice",
|
|
88
|
+
displayName: "Invoice",
|
|
89
|
+
entityType: "Standard",
|
|
90
|
+
description: "Invoice records",
|
|
91
|
+
isRbacEnabled: false,
|
|
92
|
+
fields: [
|
|
102
93
|
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
94
|
+
id: "f1000000-0000-0000-0000-000000000002",
|
|
95
|
+
name: "amount",
|
|
96
|
+
displayName: "Amount",
|
|
97
|
+
fieldDataType: { name: "DECIMAL" },
|
|
98
|
+
sqlType: {
|
|
99
|
+
name: "DECIMAL",
|
|
100
|
+
decimalPrecision: 2,
|
|
101
|
+
minValue: 0,
|
|
102
|
+
maxValue: 999999,
|
|
103
|
+
},
|
|
104
|
+
isRequired: true,
|
|
105
|
+
isUnique: false,
|
|
106
|
+
isEncrypted: false,
|
|
107
|
+
isRbacEnabled: false,
|
|
108
|
+
isPrimaryKey: false,
|
|
109
|
+
isSystemField: false,
|
|
110
110
|
},
|
|
111
111
|
],
|
|
112
112
|
},
|
|
@@ -130,6 +130,22 @@ const ENTITIES_DELETE_EXAMPLES: CommandExample[] = [
|
|
|
130
130
|
},
|
|
131
131
|
];
|
|
132
132
|
|
|
133
|
+
const ENTITIES_CREATE_EXAMPLES: CommandExample[] = [
|
|
134
|
+
{
|
|
135
|
+
Description:
|
|
136
|
+
"Create an entity with choice-set, relationship, and file fields. " +
|
|
137
|
+
"CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE require 'choiceSetId' (from 'df choice-sets list'). " +
|
|
138
|
+
"RELATIONSHIP and FILE both require 'referenceEntityId' (UUID of the target entity, from 'df entities list') and 'referenceFieldId' (UUID of the field on the target entity, from 'df entities get <target-id>'). " +
|
|
139
|
+
"Note: a RELATIONSHIP column on a record always stores the target record's UUID 'Id' (regardless of which 'referenceFieldId' configured the join) — see 'df records insert' for how to write the value.",
|
|
140
|
+
Command:
|
|
141
|
+
'uip df entities create Expense --body \'{"displayName":"Expense","fields":[{"fieldName":"category","type":"CHOICE_SET_SINGLE","choiceSetId":"c1d2e3f4-0000-0000-0000-000000000001","isRequired":true},{"fieldName":"tags","type":"CHOICE_SET_MULTIPLE","choiceSetId":"c1d2e3f4-0000-0000-0000-000000000002"},{"fieldName":"submitter","type":"RELATIONSHIP","referenceEntityId":"a1b2c3d4-0000-0000-0000-000000000010","referenceFieldId":"f1000000-0000-0000-0000-000000000100","isRequired":true},{"fieldName":"receipt","type":"FILE","referenceEntityId":"a1b2c3d4-0000-0000-0000-000000000099","referenceFieldId":"f1000000-0000-0000-0000-000000000199"}]}\'',
|
|
142
|
+
Output: {
|
|
143
|
+
Code: "EntityCreated",
|
|
144
|
+
Data: { ID: "a1b2c3d4-0000-0000-0000-000000000004" },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
];
|
|
148
|
+
|
|
133
149
|
const VALID_FIELD_TYPES = new Set(Object.values(EntityFieldDataType));
|
|
134
150
|
const VALID_FIELD_TYPES_LIST = [...VALID_FIELD_TYPES].join(", ");
|
|
135
151
|
|
|
@@ -170,61 +186,34 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
170
186
|
entities
|
|
171
187
|
.command("list")
|
|
172
188
|
.description("List all Data Fabric entities")
|
|
173
|
-
.
|
|
189
|
+
.addOption(
|
|
190
|
+
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
191
|
+
)
|
|
174
192
|
.option(
|
|
175
193
|
"--native-only",
|
|
176
194
|
"Show only native entities (exclude federated entities with external connections)",
|
|
177
195
|
)
|
|
178
196
|
.examples(ENTITIES_LIST_EXAMPLES)
|
|
179
197
|
.trackedAction(processContext, async (options: ListOptions) => {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
if (clientError) {
|
|
185
|
-
OutputFormatter.error({
|
|
186
|
-
Result: RESULTS.Failure,
|
|
187
|
-
Message: "Error connecting to Data Fabric",
|
|
188
|
-
Instructions: await extractErrorMessage(clientError),
|
|
189
|
-
});
|
|
190
|
-
processContext.exit(1);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
198
|
+
const sdk = await connectOrFail(options.tenant);
|
|
199
|
+
if (!sdk) return;
|
|
193
200
|
|
|
194
201
|
const [listError, result] = await catchError(sdk.entities.getAll());
|
|
195
202
|
|
|
196
203
|
if (listError) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
processContext.exit(1);
|
|
203
|
-
return;
|
|
204
|
+
return fail(
|
|
205
|
+
"Error listing entities",
|
|
206
|
+
await extractErrorMessage(listError),
|
|
207
|
+
);
|
|
204
208
|
}
|
|
205
209
|
|
|
206
|
-
const entityList = (result ?? [])
|
|
207
|
-
.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
externalFields[0]?.externalConnectionDetail
|
|
214
|
-
?.connectorName;
|
|
215
|
-
return {
|
|
216
|
-
Name: entity.name,
|
|
217
|
-
DisplayName: entity.displayName || entity.name,
|
|
218
|
-
ID: entity.id,
|
|
219
|
-
Type: entity.entityType,
|
|
220
|
-
Source: isNative
|
|
221
|
-
? "Native"
|
|
222
|
-
: `Federated${connectorName ? ` (${connectorName})` : ""}`,
|
|
223
|
-
Description: entity.description || "",
|
|
224
|
-
FieldCount: entity.fields.length,
|
|
225
|
-
};
|
|
226
|
-
})
|
|
227
|
-
.filter((e) => !options.nativeOnly || e.Source === "Native");
|
|
210
|
+
const entityList = (result ?? []).filter((e) => {
|
|
211
|
+
if (!options.nativeOnly) return true;
|
|
212
|
+
const entity = e as unknown as RawEntityGetResponse;
|
|
213
|
+
const externalFields: ExternalSourceFields[] =
|
|
214
|
+
entity.externalFields ?? [];
|
|
215
|
+
return externalFields.length === 0;
|
|
216
|
+
});
|
|
228
217
|
|
|
229
218
|
OutputFormatter.success({
|
|
230
219
|
Result: RESULTS.Success,
|
|
@@ -237,73 +226,40 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
237
226
|
.command("get")
|
|
238
227
|
.description("Get schema details of a Data Fabric entity")
|
|
239
228
|
.argument("<id>", "Entity ID")
|
|
240
|
-
.
|
|
229
|
+
.addOption(
|
|
230
|
+
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
231
|
+
)
|
|
241
232
|
.examples(ENTITIES_GET_EXAMPLES)
|
|
242
233
|
.trackedAction(
|
|
243
234
|
processContext,
|
|
244
235
|
async (id: string, options: GetOptions) => {
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
if (clientError) {
|
|
250
|
-
OutputFormatter.error({
|
|
251
|
-
Result: RESULTS.Failure,
|
|
252
|
-
Message: "Error connecting to Data Fabric",
|
|
253
|
-
Instructions: await extractErrorMessage(clientError),
|
|
254
|
-
});
|
|
255
|
-
processContext.exit(1);
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
236
|
+
const sdk = await connectOrFail(options.tenant);
|
|
237
|
+
if (!sdk) return;
|
|
258
238
|
|
|
259
239
|
const [getError, entity] = await catchError(
|
|
260
240
|
sdk.entities.getById(id),
|
|
261
241
|
);
|
|
262
242
|
|
|
263
243
|
if (getError) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
});
|
|
269
|
-
processContext.exit(1);
|
|
270
|
-
return;
|
|
244
|
+
return fail(
|
|
245
|
+
`Error getting entity schema '${id}'`,
|
|
246
|
+
await extractErrorMessage(getError),
|
|
247
|
+
);
|
|
271
248
|
}
|
|
272
249
|
|
|
273
250
|
const e = entity as unknown as RawEntityGetResponse;
|
|
274
251
|
|
|
275
252
|
if (!e?.fields) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
"Verify the entity ID exists. Use 'df entities list' to see available entities.",
|
|
281
|
-
});
|
|
282
|
-
processContext.exit(1);
|
|
283
|
-
return;
|
|
253
|
+
return fail(
|
|
254
|
+
`Entity '${id}' not found`,
|
|
255
|
+
"Verify the entity ID exists. Use 'df entities list' to see available entities.",
|
|
256
|
+
);
|
|
284
257
|
}
|
|
285
258
|
|
|
286
|
-
const fields = e.fields.map((field) => ({
|
|
287
|
-
ID: field.id,
|
|
288
|
-
Name: field.name,
|
|
289
|
-
DisplayName: field.displayName,
|
|
290
|
-
Type: field.fieldDataType?.name,
|
|
291
|
-
Required: field.isRequired,
|
|
292
|
-
PrimaryKey: field.isPrimaryKey,
|
|
293
|
-
System: field.isSystemField,
|
|
294
|
-
}));
|
|
295
|
-
|
|
296
259
|
OutputFormatter.success({
|
|
297
260
|
Result: RESULTS.Success,
|
|
298
261
|
Code: "EntitySchema",
|
|
299
|
-
Data:
|
|
300
|
-
Name: e.name,
|
|
301
|
-
DisplayName: e.displayName || e.name,
|
|
302
|
-
ID: e.id,
|
|
303
|
-
Type: e.entityType,
|
|
304
|
-
Description: e.description || "",
|
|
305
|
-
Fields: fields,
|
|
306
|
-
},
|
|
262
|
+
Data: e,
|
|
307
263
|
});
|
|
308
264
|
},
|
|
309
265
|
);
|
|
@@ -315,12 +271,15 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
315
271
|
"<name>",
|
|
316
272
|
"Entity name (must start with a letter; letters, numbers, and underscores only)",
|
|
317
273
|
)
|
|
318
|
-
.
|
|
274
|
+
.addOption(
|
|
275
|
+
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
276
|
+
)
|
|
319
277
|
.option(
|
|
320
278
|
"-f, --file <path>",
|
|
321
279
|
"Path to JSON file with entity definition (fields array required; displayName, description, isRbacEnabled optional)",
|
|
322
280
|
)
|
|
323
281
|
.option("--body <json>", "Inline JSON entity definition")
|
|
282
|
+
.examples(ENTITIES_CREATE_EXAMPLES)
|
|
324
283
|
.trackedAction(
|
|
325
284
|
processContext,
|
|
326
285
|
async (name: string, options: CreateOptions) => {
|
|
@@ -333,13 +292,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
333
292
|
);
|
|
334
293
|
|
|
335
294
|
if (parseError) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
});
|
|
341
|
-
processContext.exit(1);
|
|
342
|
-
return;
|
|
295
|
+
return fail(
|
|
296
|
+
"Error parsing entity definition",
|
|
297
|
+
parseError.message,
|
|
298
|
+
);
|
|
343
299
|
}
|
|
344
300
|
|
|
345
301
|
if (
|
|
@@ -347,27 +303,18 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
347
303
|
parsed === null ||
|
|
348
304
|
Array.isArray(parsed)
|
|
349
305
|
) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
"Provide a JSON object with a 'fields' array and optional displayName, description, isRbacEnabled.",
|
|
355
|
-
});
|
|
356
|
-
processContext.exit(1);
|
|
357
|
-
return;
|
|
306
|
+
return fail(
|
|
307
|
+
"Entity definition must be a JSON object",
|
|
308
|
+
"Provide a JSON object with a 'fields' array and optional displayName, description, isRbacEnabled.",
|
|
309
|
+
);
|
|
358
310
|
}
|
|
359
311
|
|
|
360
312
|
const definition = parsed as Record<string, unknown>;
|
|
361
313
|
if (!Array.isArray(definition.fields)) {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
Instructions:
|
|
367
|
-
"Provide a JSON object with a 'fields' array containing field definitions.",
|
|
368
|
-
});
|
|
369
|
-
processContext.exit(1);
|
|
370
|
-
return;
|
|
314
|
+
return fail(
|
|
315
|
+
"Entity definition must include a 'fields' array",
|
|
316
|
+
"Provide a JSON object with a 'fields' array containing field definitions.",
|
|
317
|
+
);
|
|
371
318
|
}
|
|
372
319
|
|
|
373
320
|
const hasInvalidField = (definition.fields as unknown[]).some(
|
|
@@ -378,39 +325,21 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
378
325
|
"string",
|
|
379
326
|
);
|
|
380
327
|
if (hasInvalidField) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
'Example: {"fieldName":"title","type":"STRING"}',
|
|
386
|
-
});
|
|
387
|
-
processContext.exit(1);
|
|
388
|
-
return;
|
|
328
|
+
return fail(
|
|
329
|
+
"Each field must include a 'fieldName' string",
|
|
330
|
+
'Example: {"fieldName":"title","type":"STRING"}',
|
|
331
|
+
);
|
|
389
332
|
}
|
|
390
333
|
|
|
391
334
|
if (hasInvalidFieldType(definition.fields as unknown[])) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
});
|
|
397
|
-
processContext.exit(1);
|
|
398
|
-
return;
|
|
335
|
+
return fail(
|
|
336
|
+
"Invalid field type in fields",
|
|
337
|
+
`Valid types: ${VALID_FIELD_TYPES_LIST}`,
|
|
338
|
+
);
|
|
399
339
|
}
|
|
400
340
|
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
if (clientError) {
|
|
406
|
-
OutputFormatter.error({
|
|
407
|
-
Result: RESULTS.Failure,
|
|
408
|
-
Message: "Error connecting to Data Fabric",
|
|
409
|
-
Instructions: await extractErrorMessage(clientError),
|
|
410
|
-
});
|
|
411
|
-
processContext.exit(1);
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
341
|
+
const sdk = await connectOrFail(options.tenant);
|
|
342
|
+
if (!sdk) return;
|
|
414
343
|
|
|
415
344
|
const createOpts = {
|
|
416
345
|
...(definition.displayName !== undefined && {
|
|
@@ -436,13 +365,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
436
365
|
);
|
|
437
366
|
|
|
438
367
|
if (createError) {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
});
|
|
444
|
-
processContext.exit(1);
|
|
445
|
-
return;
|
|
368
|
+
return fail(
|
|
369
|
+
"Error creating entity",
|
|
370
|
+
await extractErrorMessage(createError),
|
|
371
|
+
);
|
|
446
372
|
}
|
|
447
373
|
|
|
448
374
|
OutputFormatter.success({
|
|
@@ -457,7 +383,9 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
457
383
|
.command("update")
|
|
458
384
|
.description("Update schema or metadata of a Data Fabric entity")
|
|
459
385
|
.argument("<id>", "Entity ID")
|
|
460
|
-
.
|
|
386
|
+
.addOption(
|
|
387
|
+
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
388
|
+
)
|
|
461
389
|
.option(
|
|
462
390
|
"-f, --file <path>",
|
|
463
391
|
"Path to JSON file with update options (addFields, updateFields, removeFields, displayName, description, isRbacEnabled)",
|
|
@@ -483,13 +411,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
483
411
|
);
|
|
484
412
|
|
|
485
413
|
if (parseError) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
});
|
|
491
|
-
processContext.exit(1);
|
|
492
|
-
return;
|
|
414
|
+
return fail(
|
|
415
|
+
"Error parsing update options",
|
|
416
|
+
parseError.message,
|
|
417
|
+
);
|
|
493
418
|
}
|
|
494
419
|
|
|
495
420
|
if (
|
|
@@ -497,14 +422,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
497
422
|
parsed === null ||
|
|
498
423
|
Array.isArray(parsed)
|
|
499
424
|
) {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
"Provide a JSON object with addFields, updateFields, removeFields, displayName, description, or isRbacEnabled.",
|
|
505
|
-
});
|
|
506
|
-
processContext.exit(1);
|
|
507
|
-
return;
|
|
425
|
+
return fail(
|
|
426
|
+
"Update options must be a JSON object",
|
|
427
|
+
"Provide a JSON object with addFields, updateFields, removeFields, displayName, description, or isRbacEnabled.",
|
|
428
|
+
);
|
|
508
429
|
}
|
|
509
430
|
|
|
510
431
|
const input = parsed as Record<string, unknown>;
|
|
@@ -513,42 +434,30 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
513
434
|
input.addFields !== undefined &&
|
|
514
435
|
!Array.isArray(input.addFields)
|
|
515
436
|
) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
'Example: {"addFields":[{"fieldName":"title","type":"STRING"}]}',
|
|
521
|
-
});
|
|
522
|
-
processContext.exit(1);
|
|
523
|
-
return;
|
|
437
|
+
return fail(
|
|
438
|
+
"'addFields' must be an array",
|
|
439
|
+
'Example: {"addFields":[{"fieldName":"title","type":"STRING"}]}',
|
|
440
|
+
);
|
|
524
441
|
}
|
|
525
442
|
|
|
526
443
|
if (
|
|
527
444
|
input.updateFields !== undefined &&
|
|
528
445
|
!Array.isArray(input.updateFields)
|
|
529
446
|
) {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
'Example: {"updateFields":[{"id":"<fieldId>","displayName":"New Name"}]}',
|
|
535
|
-
});
|
|
536
|
-
processContext.exit(1);
|
|
537
|
-
return;
|
|
447
|
+
return fail(
|
|
448
|
+
"'updateFields' must be an array",
|
|
449
|
+
'Example: {"updateFields":[{"id":"<fieldId>","displayName":"New Name"}]}',
|
|
450
|
+
);
|
|
538
451
|
}
|
|
539
452
|
|
|
540
453
|
if (
|
|
541
454
|
input.removeFields !== undefined &&
|
|
542
455
|
!Array.isArray(input.removeFields)
|
|
543
456
|
) {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
'Example: {"removeFields":[{"fieldName":"old_field"}]}',
|
|
549
|
-
});
|
|
550
|
-
processContext.exit(1);
|
|
551
|
-
return;
|
|
457
|
+
return fail(
|
|
458
|
+
"'removeFields' must be an array",
|
|
459
|
+
'Example: {"removeFields":[{"fieldName":"old_field"}]}',
|
|
460
|
+
);
|
|
552
461
|
}
|
|
553
462
|
|
|
554
463
|
let trimmedReason: string | undefined;
|
|
@@ -565,41 +474,18 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
565
474
|
return typeof fn !== "string" || fn.trim() === "";
|
|
566
475
|
});
|
|
567
476
|
if (hasInvalidRemove) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
Instructions:
|
|
573
|
-
'Example: {"removeFields":[{"fieldName":"old_field"}]}',
|
|
574
|
-
});
|
|
575
|
-
processContext.exit(1);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (options.confirm !== true) {
|
|
580
|
-
OutputFormatter.error({
|
|
581
|
-
Result: RESULTS.Failure,
|
|
582
|
-
Message:
|
|
583
|
-
"Confirmation required for destructive operation",
|
|
584
|
-
Instructions:
|
|
585
|
-
"Pass --confirm to acknowledge field deletion is irreversible.",
|
|
586
|
-
});
|
|
587
|
-
processContext.exit(1);
|
|
588
|
-
return;
|
|
477
|
+
return fail(
|
|
478
|
+
"Each field in removeFields must include a non-empty 'fieldName' string",
|
|
479
|
+
'Example: {"removeFields":[{"fieldName":"old_field"}]}',
|
|
480
|
+
);
|
|
589
481
|
}
|
|
590
482
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
Instructions:
|
|
598
|
-
'Pass --reason "<text>" to record why fields are being removed.',
|
|
599
|
-
});
|
|
600
|
-
processContext.exit(1);
|
|
601
|
-
return;
|
|
602
|
-
}
|
|
483
|
+
const reason = requireDestructiveConfirmation(
|
|
484
|
+
options,
|
|
485
|
+
'Pass --reason "<text>" to record why fields are being removed.',
|
|
486
|
+
);
|
|
487
|
+
if (reason === null) return;
|
|
488
|
+
trimmedReason = reason;
|
|
603
489
|
|
|
604
490
|
removedFieldNames = (
|
|
605
491
|
removeFields as { fieldName: string }[]
|
|
@@ -616,25 +502,17 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
616
502
|
},
|
|
617
503
|
);
|
|
618
504
|
if (hasInvalidField) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
Instructions:
|
|
624
|
-
'Example: {"fieldName":"title","type":"STRING"}',
|
|
625
|
-
});
|
|
626
|
-
processContext.exit(1);
|
|
627
|
-
return;
|
|
505
|
+
return fail(
|
|
506
|
+
"Each field in addFields must include a non-empty 'fieldName' string",
|
|
507
|
+
'Example: {"fieldName":"title","type":"STRING"}',
|
|
508
|
+
);
|
|
628
509
|
}
|
|
629
510
|
|
|
630
511
|
if (hasInvalidFieldType(input.addFields as unknown[])) {
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
});
|
|
636
|
-
processContext.exit(1);
|
|
637
|
-
return;
|
|
512
|
+
return fail(
|
|
513
|
+
"Invalid field type in addFields",
|
|
514
|
+
`Valid types: ${VALID_FIELD_TYPES_LIST}`,
|
|
515
|
+
);
|
|
638
516
|
}
|
|
639
517
|
|
|
640
518
|
const addNames = (
|
|
@@ -644,14 +522,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
644
522
|
(name, i) => addNames.indexOf(name) !== i,
|
|
645
523
|
);
|
|
646
524
|
if (duplicateAdd !== undefined) {
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
"Each entry in addFields must have a unique fieldName.",
|
|
652
|
-
});
|
|
653
|
-
processContext.exit(1);
|
|
654
|
-
return;
|
|
525
|
+
return fail(
|
|
526
|
+
`Duplicate fieldName '${duplicateAdd}' in addFields`,
|
|
527
|
+
"Each entry in addFields must have a unique fieldName.",
|
|
528
|
+
);
|
|
655
529
|
}
|
|
656
530
|
|
|
657
531
|
if (removedFieldNames.length > 0) {
|
|
@@ -659,14 +533,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
659
533
|
removedFieldNames.includes(name),
|
|
660
534
|
);
|
|
661
535
|
if (conflict !== undefined) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
"A single update cannot add and remove the same field. Split into two calls if you need to recreate it.",
|
|
667
|
-
});
|
|
668
|
-
processContext.exit(1);
|
|
669
|
-
return;
|
|
536
|
+
return fail(
|
|
537
|
+
`Field '${conflict}' appears in both addFields and removeFields`,
|
|
538
|
+
"A single update cannot add and remove the same field. Split into two calls if you need to recreate it.",
|
|
539
|
+
);
|
|
670
540
|
}
|
|
671
541
|
}
|
|
672
542
|
}
|
|
@@ -680,31 +550,15 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
680
550
|
return typeof fid !== "string" || fid.trim() === "";
|
|
681
551
|
});
|
|
682
552
|
if (hasInvalidField) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
Instructions:
|
|
688
|
-
'Use \'df entities get <entityId>\' to find field IDs. Example: {"id":"<fieldId>","displayName":"Total Amount","isRequired":true}',
|
|
689
|
-
});
|
|
690
|
-
processContext.exit(1);
|
|
691
|
-
return;
|
|
553
|
+
return fail(
|
|
554
|
+
"Each field in updateFields must include a non-empty 'id' string",
|
|
555
|
+
'Use \'df entities get <entityId>\' to find field IDs. Example: {"id":"<fieldId>","displayName":"Total Amount","isRequired":true}',
|
|
556
|
+
);
|
|
692
557
|
}
|
|
693
558
|
}
|
|
694
559
|
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
);
|
|
698
|
-
|
|
699
|
-
if (clientError) {
|
|
700
|
-
OutputFormatter.error({
|
|
701
|
-
Result: RESULTS.Failure,
|
|
702
|
-
Message: "Error connecting to Data Fabric",
|
|
703
|
-
Instructions: await extractErrorMessage(clientError),
|
|
704
|
-
});
|
|
705
|
-
processContext.exit(1);
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
560
|
+
const sdk = await connectOrFail(options.tenant);
|
|
561
|
+
if (!sdk) return;
|
|
708
562
|
|
|
709
563
|
const entityService: EntityServiceModel = sdk.entities;
|
|
710
564
|
const [updateError] = await catchError(
|
|
@@ -712,13 +566,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
712
566
|
);
|
|
713
567
|
|
|
714
568
|
if (updateError) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
});
|
|
720
|
-
processContext.exit(1);
|
|
721
|
-
return;
|
|
569
|
+
return fail(
|
|
570
|
+
"Error updating entity",
|
|
571
|
+
await extractErrorMessage(updateError),
|
|
572
|
+
);
|
|
722
573
|
}
|
|
723
574
|
|
|
724
575
|
OutputFormatter.success({
|
|
@@ -739,7 +590,9 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
739
590
|
.command("delete")
|
|
740
591
|
.description("Delete a Data Fabric entity (irreversible)")
|
|
741
592
|
.argument("<id>", "Entity ID")
|
|
742
|
-
.
|
|
593
|
+
.addOption(
|
|
594
|
+
createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
|
|
595
|
+
)
|
|
743
596
|
.option("--confirm", "Acknowledge this is an irreversible operation")
|
|
744
597
|
.option(
|
|
745
598
|
"--reason <reason>",
|
|
@@ -749,43 +602,14 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
749
602
|
.trackedAction(
|
|
750
603
|
processContext,
|
|
751
604
|
async (id: string, options: DeleteOptions) => {
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
Message:
|
|
756
|
-
"Confirmation required for destructive operation",
|
|
757
|
-
Instructions:
|
|
758
|
-
"Pass --confirm to acknowledge this is irreversible.",
|
|
759
|
-
});
|
|
760
|
-
processContext.exit(1);
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
const reason = options.reason?.trim();
|
|
765
|
-
if (reason === undefined || reason === "") {
|
|
766
|
-
OutputFormatter.error({
|
|
767
|
-
Result: RESULTS.Failure,
|
|
768
|
-
Message: "Reason required for destructive operation",
|
|
769
|
-
Instructions:
|
|
770
|
-
'Pass --reason "<text>" to record why the entity is being deleted.',
|
|
771
|
-
});
|
|
772
|
-
processContext.exit(1);
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
const [clientError, sdk] = await catchError(
|
|
777
|
-
createDataFabricClient(options.tenant),
|
|
605
|
+
const reason = requireDestructiveConfirmation(
|
|
606
|
+
options,
|
|
607
|
+
'Pass --reason "<text>" to record why the entity is being deleted.',
|
|
778
608
|
);
|
|
609
|
+
if (reason === null) return;
|
|
779
610
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
Result: RESULTS.Failure,
|
|
783
|
-
Message: "Error connecting to Data Fabric",
|
|
784
|
-
Instructions: await extractErrorMessage(clientError),
|
|
785
|
-
});
|
|
786
|
-
processContext.exit(1);
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
611
|
+
const sdk = await connectOrFail(options.tenant);
|
|
612
|
+
if (!sdk) return;
|
|
789
613
|
|
|
790
614
|
const entityService: EntityServiceModel = sdk.entities;
|
|
791
615
|
const [deleteError] = await catchError(
|
|
@@ -793,13 +617,10 @@ export const registerEntitiesCommand = (program: Command) => {
|
|
|
793
617
|
);
|
|
794
618
|
|
|
795
619
|
if (deleteError) {
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
});
|
|
801
|
-
processContext.exit(1);
|
|
802
|
-
return;
|
|
620
|
+
return fail(
|
|
621
|
+
`Error deleting entity '${id}'`,
|
|
622
|
+
await extractErrorMessage(deleteError),
|
|
623
|
+
);
|
|
803
624
|
}
|
|
804
625
|
|
|
805
626
|
OutputFormatter.success({
|