@uipath/data-fabric-tool 1.1.0 → 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.
@@ -17,7 +17,8 @@ import type {
17
17
  import { EntityFieldDataType } from "@uipath/uipath-typescript";
18
18
  import type { Command } from "commander";
19
19
  import { readJsonInput } from "../utils/input";
20
- import { createDataFabricClient } from "../utils/sdk-client";
20
+ import { fail, requireDestructiveConfirmation } from "../utils/output";
21
+ import { connectOrFail } from "../utils/sdk-client";
21
22
 
22
23
  interface ListOptions {
23
24
  tenant?: string;
@@ -50,28 +51,25 @@ interface DeleteOptions {
50
51
 
51
52
  const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
52
53
  {
53
- Description: "List Data Fabric entities",
54
+ Description: "List Data Fabric entities.",
54
55
  Command: "uip df entities list --native-only",
55
56
  Output: {
56
57
  Code: "EntityList",
57
58
  Data: [
58
59
  {
59
- Name: "Invoice",
60
- DisplayName: "Invoice",
61
- ID: "a1b2c3d4-0000-0000-0000-000000000001",
62
- Type: "Standard",
63
- Source: "Native",
64
- Description: "Invoice records",
65
- FieldCount: 8,
66
- },
67
- {
68
- Name: "Customer",
69
- DisplayName: "Customer",
70
- ID: "a1b2c3d4-0000-0000-0000-000000000002",
71
- Type: "Standard",
72
- Source: "Native",
73
- Description: "Customer records",
74
- 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: [],
75
73
  },
76
74
  ],
77
75
  },
@@ -80,34 +78,35 @@ const ENTITIES_LIST_EXAMPLES: CommandExample[] = [
80
78
 
81
79
  const ENTITIES_GET_EXAMPLES: CommandExample[] = [
82
80
  {
83
- Description: "Get entity schema by ID",
81
+ Description: "Get entity schema by ID.",
84
82
  Command: "uip df entities get a1b2c3d4-0000-0000-0000-000000000001",
85
83
  Output: {
86
84
  Code: "EntitySchema",
87
85
  Data: {
88
- Name: "Invoice",
89
- DisplayName: "Invoice",
90
- ID: "a1b2c3d4-0000-0000-0000-000000000001",
91
- Type: "Standard",
92
- Description: "Invoice records",
93
- Fields: [
94
- {
95
- ID: "f1000000-0000-0000-0000-000000000001",
96
- Name: "id",
97
- DisplayName: "ID",
98
- Type: "Guid",
99
- Required: true,
100
- PrimaryKey: true,
101
- System: true,
102
- },
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: [
103
93
  {
104
- ID: "f1000000-0000-0000-0000-000000000002",
105
- Name: "amount",
106
- DisplayName: "Amount",
107
- Type: "Decimal",
108
- Required: true,
109
- PrimaryKey: false,
110
- System: false,
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,
111
110
  },
112
111
  ],
113
112
  },
@@ -134,12 +133,12 @@ const ENTITIES_DELETE_EXAMPLES: CommandExample[] = [
134
133
  const ENTITIES_CREATE_EXAMPLES: CommandExample[] = [
135
134
  {
136
135
  Description:
137
- "Create an entity with a choice-set field (single-select and multi-select) and a relationship to another entity. " +
136
+ "Create an entity with choice-set, relationship, and file fields. " +
138
137
  "CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE require 'choiceSetId' (from 'df choice-sets list'). " +
139
- "RELATIONSHIP requires 'referenceEntityName' (target entity) and 'referenceFieldName' (the field used to resolve joins on read; e.g. 'Id' or a unique key like 'Email'). " +
140
- "Note: regardless of 'referenceFieldName', a relationship column on a record always stores the target record's UUID 'Id' — see 'df records insert' for how to write the value.",
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.",
141
140
  Command:
142
- '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","referenceEntityName":"Employee","referenceFieldName":"Id","isRequired":true}]}\'',
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"}]}\'',
143
142
  Output: {
144
143
  Code: "EntityCreated",
145
144
  Data: { ID: "a1b2c3d4-0000-0000-0000-000000000004" },
@@ -196,54 +195,25 @@ export const registerEntitiesCommand = (program: Command) => {
196
195
  )
197
196
  .examples(ENTITIES_LIST_EXAMPLES)
198
197
  .trackedAction(processContext, async (options: ListOptions) => {
199
- const [clientError, sdk] = await catchError(
200
- createDataFabricClient(options.tenant),
201
- );
202
-
203
- if (clientError) {
204
- OutputFormatter.error({
205
- Result: RESULTS.Failure,
206
- Message: "Error connecting to Data Fabric",
207
- Instructions: await extractErrorMessage(clientError),
208
- });
209
- processContext.exit(1);
210
- return;
211
- }
198
+ const sdk = await connectOrFail(options.tenant);
199
+ if (!sdk) return;
212
200
 
213
201
  const [listError, result] = await catchError(sdk.entities.getAll());
214
202
 
215
203
  if (listError) {
216
- OutputFormatter.error({
217
- Result: RESULTS.Failure,
218
- Message: "Error listing entities",
219
- Instructions: await extractErrorMessage(listError),
220
- });
221
- processContext.exit(1);
222
- return;
204
+ return fail(
205
+ "Error listing entities",
206
+ await extractErrorMessage(listError),
207
+ );
223
208
  }
224
209
 
225
- const entityList = (result ?? [])
226
- .map((e) => {
227
- const entity = e as unknown as RawEntityGetResponse;
228
- const externalFields: ExternalSourceFields[] =
229
- entity.externalFields ?? [];
230
- const isNative = externalFields.length === 0;
231
- const connectorName =
232
- externalFields[0]?.externalConnectionDetail
233
- ?.connectorName;
234
- return {
235
- Name: entity.name,
236
- DisplayName: entity.displayName || entity.name,
237
- ID: entity.id,
238
- Type: entity.entityType,
239
- Source: isNative
240
- ? "Native"
241
- : `Federated${connectorName ? ` (${connectorName})` : ""}`,
242
- Description: entity.description || "",
243
- FieldCount: entity.fields.length,
244
- };
245
- })
246
- .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
+ });
247
217
 
248
218
  OutputFormatter.success({
249
219
  Result: RESULTS.Success,
@@ -263,68 +233,33 @@ export const registerEntitiesCommand = (program: Command) => {
263
233
  .trackedAction(
264
234
  processContext,
265
235
  async (id: string, options: GetOptions) => {
266
- const [clientError, sdk] = await catchError(
267
- createDataFabricClient(options.tenant),
268
- );
269
-
270
- if (clientError) {
271
- OutputFormatter.error({
272
- Result: RESULTS.Failure,
273
- Message: "Error connecting to Data Fabric",
274
- Instructions: await extractErrorMessage(clientError),
275
- });
276
- processContext.exit(1);
277
- return;
278
- }
236
+ const sdk = await connectOrFail(options.tenant);
237
+ if (!sdk) return;
279
238
 
280
239
  const [getError, entity] = await catchError(
281
240
  sdk.entities.getById(id),
282
241
  );
283
242
 
284
243
  if (getError) {
285
- OutputFormatter.error({
286
- Result: RESULTS.Failure,
287
- Message: `Error getting entity schema '${id}'`,
288
- Instructions: await extractErrorMessage(getError),
289
- });
290
- processContext.exit(1);
291
- return;
244
+ return fail(
245
+ `Error getting entity schema '${id}'`,
246
+ await extractErrorMessage(getError),
247
+ );
292
248
  }
293
249
 
294
250
  const e = entity as unknown as RawEntityGetResponse;
295
251
 
296
252
  if (!e?.fields) {
297
- OutputFormatter.error({
298
- Result: RESULTS.Failure,
299
- Message: `Entity '${id}' not found`,
300
- Instructions:
301
- "Verify the entity ID exists. Use 'df entities list' to see available entities.",
302
- });
303
- processContext.exit(1);
304
- return;
253
+ return fail(
254
+ `Entity '${id}' not found`,
255
+ "Verify the entity ID exists. Use 'df entities list' to see available entities.",
256
+ );
305
257
  }
306
258
 
307
- const fields = e.fields.map((field) => ({
308
- ID: field.id,
309
- Name: field.name,
310
- DisplayName: field.displayName,
311
- Type: field.fieldDataType?.name,
312
- Required: field.isRequired,
313
- PrimaryKey: field.isPrimaryKey,
314
- System: field.isSystemField,
315
- }));
316
-
317
259
  OutputFormatter.success({
318
260
  Result: RESULTS.Success,
319
261
  Code: "EntitySchema",
320
- Data: {
321
- Name: e.name,
322
- DisplayName: e.displayName || e.name,
323
- ID: e.id,
324
- Type: e.entityType,
325
- Description: e.description || "",
326
- Fields: fields,
327
- },
262
+ Data: e,
328
263
  });
329
264
  },
330
265
  );
@@ -357,13 +292,10 @@ export const registerEntitiesCommand = (program: Command) => {
357
292
  );
358
293
 
359
294
  if (parseError) {
360
- OutputFormatter.error({
361
- Result: RESULTS.Failure,
362
- Message: "Error parsing entity definition",
363
- Instructions: parseError.message,
364
- });
365
- processContext.exit(1);
366
- return;
295
+ return fail(
296
+ "Error parsing entity definition",
297
+ parseError.message,
298
+ );
367
299
  }
368
300
 
369
301
  if (
@@ -371,27 +303,18 @@ export const registerEntitiesCommand = (program: Command) => {
371
303
  parsed === null ||
372
304
  Array.isArray(parsed)
373
305
  ) {
374
- OutputFormatter.error({
375
- Result: RESULTS.Failure,
376
- Message: "Entity definition must be a JSON object",
377
- Instructions:
378
- "Provide a JSON object with a 'fields' array and optional displayName, description, isRbacEnabled.",
379
- });
380
- processContext.exit(1);
381
- 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
+ );
382
310
  }
383
311
 
384
312
  const definition = parsed as Record<string, unknown>;
385
313
  if (!Array.isArray(definition.fields)) {
386
- OutputFormatter.error({
387
- Result: RESULTS.Failure,
388
- Message:
389
- "Entity definition must include a 'fields' array",
390
- Instructions:
391
- "Provide a JSON object with a 'fields' array containing field definitions.",
392
- });
393
- processContext.exit(1);
394
- 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
+ );
395
318
  }
396
319
 
397
320
  const hasInvalidField = (definition.fields as unknown[]).some(
@@ -402,39 +325,21 @@ export const registerEntitiesCommand = (program: Command) => {
402
325
  "string",
403
326
  );
404
327
  if (hasInvalidField) {
405
- OutputFormatter.error({
406
- Result: RESULTS.Failure,
407
- Message: "Each field must include a 'fieldName' string",
408
- Instructions:
409
- 'Example: {"fieldName":"title","type":"STRING"}',
410
- });
411
- processContext.exit(1);
412
- return;
328
+ return fail(
329
+ "Each field must include a 'fieldName' string",
330
+ 'Example: {"fieldName":"title","type":"STRING"}',
331
+ );
413
332
  }
414
333
 
415
334
  if (hasInvalidFieldType(definition.fields as unknown[])) {
416
- OutputFormatter.error({
417
- Result: RESULTS.Failure,
418
- Message: "Invalid field type in fields",
419
- Instructions: `Valid types: ${VALID_FIELD_TYPES_LIST}`,
420
- });
421
- processContext.exit(1);
422
- return;
335
+ return fail(
336
+ "Invalid field type in fields",
337
+ `Valid types: ${VALID_FIELD_TYPES_LIST}`,
338
+ );
423
339
  }
424
340
 
425
- const [clientError, sdk] = await catchError(
426
- createDataFabricClient(options.tenant),
427
- );
428
-
429
- if (clientError) {
430
- OutputFormatter.error({
431
- Result: RESULTS.Failure,
432
- Message: "Error connecting to Data Fabric",
433
- Instructions: await extractErrorMessage(clientError),
434
- });
435
- processContext.exit(1);
436
- return;
437
- }
341
+ const sdk = await connectOrFail(options.tenant);
342
+ if (!sdk) return;
438
343
 
439
344
  const createOpts = {
440
345
  ...(definition.displayName !== undefined && {
@@ -460,13 +365,10 @@ export const registerEntitiesCommand = (program: Command) => {
460
365
  );
461
366
 
462
367
  if (createError) {
463
- OutputFormatter.error({
464
- Result: RESULTS.Failure,
465
- Message: "Error creating entity",
466
- Instructions: await extractErrorMessage(createError),
467
- });
468
- processContext.exit(1);
469
- return;
368
+ return fail(
369
+ "Error creating entity",
370
+ await extractErrorMessage(createError),
371
+ );
470
372
  }
471
373
 
472
374
  OutputFormatter.success({
@@ -509,13 +411,10 @@ export const registerEntitiesCommand = (program: Command) => {
509
411
  );
510
412
 
511
413
  if (parseError) {
512
- OutputFormatter.error({
513
- Result: RESULTS.Failure,
514
- Message: "Error parsing update options",
515
- Instructions: parseError.message,
516
- });
517
- processContext.exit(1);
518
- return;
414
+ return fail(
415
+ "Error parsing update options",
416
+ parseError.message,
417
+ );
519
418
  }
520
419
 
521
420
  if (
@@ -523,14 +422,10 @@ export const registerEntitiesCommand = (program: Command) => {
523
422
  parsed === null ||
524
423
  Array.isArray(parsed)
525
424
  ) {
526
- OutputFormatter.error({
527
- Result: RESULTS.Failure,
528
- Message: "Update options must be a JSON object",
529
- Instructions:
530
- "Provide a JSON object with addFields, updateFields, removeFields, displayName, description, or isRbacEnabled.",
531
- });
532
- processContext.exit(1);
533
- 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
+ );
534
429
  }
535
430
 
536
431
  const input = parsed as Record<string, unknown>;
@@ -539,42 +434,30 @@ export const registerEntitiesCommand = (program: Command) => {
539
434
  input.addFields !== undefined &&
540
435
  !Array.isArray(input.addFields)
541
436
  ) {
542
- OutputFormatter.error({
543
- Result: RESULTS.Failure,
544
- Message: "'addFields' must be an array",
545
- Instructions:
546
- 'Example: {"addFields":[{"fieldName":"title","type":"STRING"}]}',
547
- });
548
- processContext.exit(1);
549
- return;
437
+ return fail(
438
+ "'addFields' must be an array",
439
+ 'Example: {"addFields":[{"fieldName":"title","type":"STRING"}]}',
440
+ );
550
441
  }
551
442
 
552
443
  if (
553
444
  input.updateFields !== undefined &&
554
445
  !Array.isArray(input.updateFields)
555
446
  ) {
556
- OutputFormatter.error({
557
- Result: RESULTS.Failure,
558
- Message: "'updateFields' must be an array",
559
- Instructions:
560
- 'Example: {"updateFields":[{"id":"<fieldId>","displayName":"New Name"}]}',
561
- });
562
- processContext.exit(1);
563
- return;
447
+ return fail(
448
+ "'updateFields' must be an array",
449
+ 'Example: {"updateFields":[{"id":"<fieldId>","displayName":"New Name"}]}',
450
+ );
564
451
  }
565
452
 
566
453
  if (
567
454
  input.removeFields !== undefined &&
568
455
  !Array.isArray(input.removeFields)
569
456
  ) {
570
- OutputFormatter.error({
571
- Result: RESULTS.Failure,
572
- Message: "'removeFields' must be an array",
573
- Instructions:
574
- 'Example: {"removeFields":[{"fieldName":"old_field"}]}',
575
- });
576
- processContext.exit(1);
577
- return;
457
+ return fail(
458
+ "'removeFields' must be an array",
459
+ 'Example: {"removeFields":[{"fieldName":"old_field"}]}',
460
+ );
578
461
  }
579
462
 
580
463
  let trimmedReason: string | undefined;
@@ -591,41 +474,18 @@ export const registerEntitiesCommand = (program: Command) => {
591
474
  return typeof fn !== "string" || fn.trim() === "";
592
475
  });
593
476
  if (hasInvalidRemove) {
594
- OutputFormatter.error({
595
- Result: RESULTS.Failure,
596
- Message:
597
- "Each field in removeFields must include a non-empty 'fieldName' string",
598
- Instructions:
599
- 'Example: {"removeFields":[{"fieldName":"old_field"}]}',
600
- });
601
- processContext.exit(1);
602
- return;
603
- }
604
-
605
- if (options.confirm !== true) {
606
- OutputFormatter.error({
607
- Result: RESULTS.Failure,
608
- Message:
609
- "Confirmation required for destructive operation",
610
- Instructions:
611
- "Pass --confirm to acknowledge field deletion is irreversible.",
612
- });
613
- processContext.exit(1);
614
- return;
477
+ return fail(
478
+ "Each field in removeFields must include a non-empty 'fieldName' string",
479
+ 'Example: {"removeFields":[{"fieldName":"old_field"}]}',
480
+ );
615
481
  }
616
482
 
617
- trimmedReason = options.reason?.trim();
618
- if (trimmedReason === undefined || trimmedReason === "") {
619
- OutputFormatter.error({
620
- Result: RESULTS.Failure,
621
- Message:
622
- "Reason required for destructive operation",
623
- Instructions:
624
- 'Pass --reason "<text>" to record why fields are being removed.',
625
- });
626
- processContext.exit(1);
627
- return;
628
- }
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;
629
489
 
630
490
  removedFieldNames = (
631
491
  removeFields as { fieldName: string }[]
@@ -642,25 +502,17 @@ export const registerEntitiesCommand = (program: Command) => {
642
502
  },
643
503
  );
644
504
  if (hasInvalidField) {
645
- OutputFormatter.error({
646
- Result: RESULTS.Failure,
647
- Message:
648
- "Each field in addFields must include a non-empty 'fieldName' string",
649
- Instructions:
650
- 'Example: {"fieldName":"title","type":"STRING"}',
651
- });
652
- processContext.exit(1);
653
- return;
505
+ return fail(
506
+ "Each field in addFields must include a non-empty 'fieldName' string",
507
+ 'Example: {"fieldName":"title","type":"STRING"}',
508
+ );
654
509
  }
655
510
 
656
511
  if (hasInvalidFieldType(input.addFields as unknown[])) {
657
- OutputFormatter.error({
658
- Result: RESULTS.Failure,
659
- Message: "Invalid field type in addFields",
660
- Instructions: `Valid types: ${VALID_FIELD_TYPES_LIST}`,
661
- });
662
- processContext.exit(1);
663
- return;
512
+ return fail(
513
+ "Invalid field type in addFields",
514
+ `Valid types: ${VALID_FIELD_TYPES_LIST}`,
515
+ );
664
516
  }
665
517
 
666
518
  const addNames = (
@@ -670,14 +522,10 @@ export const registerEntitiesCommand = (program: Command) => {
670
522
  (name, i) => addNames.indexOf(name) !== i,
671
523
  );
672
524
  if (duplicateAdd !== undefined) {
673
- OutputFormatter.error({
674
- Result: RESULTS.Failure,
675
- Message: `Duplicate fieldName '${duplicateAdd}' in addFields`,
676
- Instructions:
677
- "Each entry in addFields must have a unique fieldName.",
678
- });
679
- processContext.exit(1);
680
- return;
525
+ return fail(
526
+ `Duplicate fieldName '${duplicateAdd}' in addFields`,
527
+ "Each entry in addFields must have a unique fieldName.",
528
+ );
681
529
  }
682
530
 
683
531
  if (removedFieldNames.length > 0) {
@@ -685,14 +533,10 @@ export const registerEntitiesCommand = (program: Command) => {
685
533
  removedFieldNames.includes(name),
686
534
  );
687
535
  if (conflict !== undefined) {
688
- OutputFormatter.error({
689
- Result: RESULTS.Failure,
690
- Message: `Field '${conflict}' appears in both addFields and removeFields`,
691
- Instructions:
692
- "A single update cannot add and remove the same field. Split into two calls if you need to recreate it.",
693
- });
694
- processContext.exit(1);
695
- 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
+ );
696
540
  }
697
541
  }
698
542
  }
@@ -706,31 +550,15 @@ export const registerEntitiesCommand = (program: Command) => {
706
550
  return typeof fid !== "string" || fid.trim() === "";
707
551
  });
708
552
  if (hasInvalidField) {
709
- OutputFormatter.error({
710
- Result: RESULTS.Failure,
711
- Message:
712
- "Each field in updateFields must include a non-empty 'id' string",
713
- Instructions:
714
- 'Use \'df entities get <entityId>\' to find field IDs. Example: {"id":"<fieldId>","displayName":"Total Amount","isRequired":true}',
715
- });
716
- processContext.exit(1);
717
- 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
+ );
718
557
  }
719
558
  }
720
559
 
721
- const [clientError, sdk] = await catchError(
722
- createDataFabricClient(options.tenant),
723
- );
724
-
725
- if (clientError) {
726
- OutputFormatter.error({
727
- Result: RESULTS.Failure,
728
- Message: "Error connecting to Data Fabric",
729
- Instructions: await extractErrorMessage(clientError),
730
- });
731
- processContext.exit(1);
732
- return;
733
- }
560
+ const sdk = await connectOrFail(options.tenant);
561
+ if (!sdk) return;
734
562
 
735
563
  const entityService: EntityServiceModel = sdk.entities;
736
564
  const [updateError] = await catchError(
@@ -738,13 +566,10 @@ export const registerEntitiesCommand = (program: Command) => {
738
566
  );
739
567
 
740
568
  if (updateError) {
741
- OutputFormatter.error({
742
- Result: RESULTS.Failure,
743
- Message: "Error updating entity",
744
- Instructions: await extractErrorMessage(updateError),
745
- });
746
- processContext.exit(1);
747
- return;
569
+ return fail(
570
+ "Error updating entity",
571
+ await extractErrorMessage(updateError),
572
+ );
748
573
  }
749
574
 
750
575
  OutputFormatter.success({
@@ -777,43 +602,14 @@ export const registerEntitiesCommand = (program: Command) => {
777
602
  .trackedAction(
778
603
  processContext,
779
604
  async (id: string, options: DeleteOptions) => {
780
- if (options.confirm !== true) {
781
- OutputFormatter.error({
782
- Result: RESULTS.Failure,
783
- Message:
784
- "Confirmation required for destructive operation",
785
- Instructions:
786
- "Pass --confirm to acknowledge this is irreversible.",
787
- });
788
- processContext.exit(1);
789
- return;
790
- }
791
-
792
- const reason = options.reason?.trim();
793
- if (reason === undefined || reason === "") {
794
- OutputFormatter.error({
795
- Result: RESULTS.Failure,
796
- Message: "Reason required for destructive operation",
797
- Instructions:
798
- 'Pass --reason "<text>" to record why the entity is being deleted.',
799
- });
800
- processContext.exit(1);
801
- return;
802
- }
803
-
804
- const [clientError, sdk] = await catchError(
805
- createDataFabricClient(options.tenant),
605
+ const reason = requireDestructiveConfirmation(
606
+ options,
607
+ 'Pass --reason "<text>" to record why the entity is being deleted.',
806
608
  );
609
+ if (reason === null) return;
807
610
 
808
- if (clientError) {
809
- OutputFormatter.error({
810
- Result: RESULTS.Failure,
811
- Message: "Error connecting to Data Fabric",
812
- Instructions: await extractErrorMessage(clientError),
813
- });
814
- processContext.exit(1);
815
- return;
816
- }
611
+ const sdk = await connectOrFail(options.tenant);
612
+ if (!sdk) return;
817
613
 
818
614
  const entityService: EntityServiceModel = sdk.entities;
819
615
  const [deleteError] = await catchError(
@@ -821,13 +617,10 @@ export const registerEntitiesCommand = (program: Command) => {
821
617
  );
822
618
 
823
619
  if (deleteError) {
824
- OutputFormatter.error({
825
- Result: RESULTS.Failure,
826
- Message: `Error deleting entity '${id}'`,
827
- Instructions: await extractErrorMessage(deleteError),
828
- });
829
- processContext.exit(1);
830
- return;
620
+ return fail(
621
+ `Error deleting entity '${id}'`,
622
+ await extractErrorMessage(deleteError),
623
+ );
831
624
  }
832
625
 
833
626
  OutputFormatter.success({