@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.
@@ -1,14 +1,32 @@
1
1
  import {
2
2
  type CommandExample,
3
3
  catchError,
4
+ createHiddenDeprecatedTenantOption,
4
5
  extractErrorMessage,
5
6
  OutputFormatter,
6
7
  processContext,
7
8
  RESULTS,
8
9
  } from "@uipath/common";
9
- import type { Command } from "commander";
10
- import { extractCursorValue } from "../utils/pagination";
11
- import { createDataFabricClient } from "../utils/sdk-client";
10
+ import type {
11
+ ChoiceSetCreateOptions,
12
+ ChoiceSetServiceModel,
13
+ ChoiceSetUpdateOptions,
14
+ ChoiceSetValueInsertOptions,
15
+ } from "@uipath/uipath-typescript";
16
+ import { type Command, Option } from "commander";
17
+ import { fail, requireDestructiveConfirmation } from "../utils/output";
18
+ import { connectOrFail } from "../utils/sdk-client";
19
+
20
+ const OUTPUT_CODES = {
21
+ ChoiceSetList: "ChoiceSetList",
22
+ ChoiceSetValues: "ChoiceSetValues",
23
+ ChoiceSetCreated: "ChoiceSetCreated",
24
+ ChoiceSetUpdated: "ChoiceSetUpdated",
25
+ ChoiceSetDeleted: "ChoiceSetDeleted",
26
+ ChoiceSetValueCreated: "ChoiceSetValueCreated",
27
+ ChoiceSetValueUpdated: "ChoiceSetValueUpdated",
28
+ ChoiceSetValuesDeleted: "ChoiceSetValuesDeleted",
29
+ } as const;
12
30
 
13
31
  interface ListOptions {
14
32
  tenant?: string;
@@ -21,24 +39,61 @@ interface GetOptions {
21
39
  cursor?: string;
22
40
  }
23
41
 
42
+ interface CreateOptions {
43
+ tenant?: string;
44
+ displayName?: string;
45
+ description?: string;
46
+ folderKey?: string;
47
+ }
48
+
49
+ interface UpdateOptions {
50
+ tenant?: string;
51
+ displayName?: string;
52
+ description?: string;
53
+ }
54
+
55
+ interface DeleteOptions {
56
+ tenant?: string;
57
+ yes?: boolean;
58
+ confirm?: boolean;
59
+ reason?: string;
60
+ }
61
+
62
+ interface ValueCreateOptions {
63
+ tenant?: string;
64
+ displayName?: string;
65
+ }
66
+
67
+ interface ValueUpdateOptions {
68
+ tenant?: string;
69
+ }
70
+
71
+ interface ValueDeleteOptions {
72
+ tenant?: string;
73
+ ids?: string;
74
+ yes?: boolean;
75
+ confirm?: boolean;
76
+ reason?: string;
77
+ }
78
+
24
79
  const CHOICE_SETS_LIST_EXAMPLES: CommandExample[] = [
25
80
  {
26
81
  Description:
27
- "List all Data Fabric choice sets. The returned 'ID' is the value to pass as 'choiceSetId' on a CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE entity field, or to 'df choice-sets get <id>'.",
82
+ "List all Data Fabric choice sets. The returned 'id' is the value to pass as 'choiceSetId' on a CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE entity field, or to 'df choice-sets list-values <id>'.",
28
83
  Command: "uip df choice-sets list",
29
84
  Output: {
30
- Code: "ChoiceSetList",
85
+ Code: OUTPUT_CODES.ChoiceSetList,
31
86
  Data: [
32
87
  {
33
- ID: "c1d2e3f4-0000-0000-0000-000000000001",
34
- Name: "ExpenseTypes",
35
- DisplayName: "Expense Types",
36
- Description: "Categories of expenses",
37
- FolderId: "f1000000-0000-0000-0000-000000000001",
38
- CreatedBy: "u1000000-0000-0000-0000-000000000001",
39
- UpdatedBy: "u1000000-0000-0000-0000-000000000001",
40
- CreatedTime: "2026-01-01T00:00:00Z",
41
- UpdatedTime: "2026-01-02T00:00:00Z",
88
+ id: "c1d2e3f4-0000-0000-0000-000000000001",
89
+ name: "ExpenseTypes",
90
+ displayName: "Expense Types",
91
+ description: "Categories of expenses",
92
+ folderId: "f1000000-0000-0000-0000-000000000001",
93
+ createdBy: "u1000000-0000-0000-0000-000000000001",
94
+ updatedBy: "u1000000-0000-0000-0000-000000000001",
95
+ createdTime: "2026-01-01T00:00:00Z",
96
+ updatedTime: "2026-01-02T00:00:00Z",
42
97
  },
43
98
  ],
44
99
  },
@@ -48,28 +103,70 @@ const CHOICE_SETS_LIST_EXAMPLES: CommandExample[] = [
48
103
  const CHOICE_SETS_GET_EXAMPLES: CommandExample[] = [
49
104
  {
50
105
  Description:
51
- "Get values for a choice set. The 'NumberId' on each value is the integer to pass when inserting or updating a record into a CHOICE_SET_SINGLE field (or in an array for CHOICE_SET_MULTIPLE).",
106
+ "List the values of a choice set. The 'numberId' on each value is the integer to pass when inserting or updating a record into a CHOICE_SET_SINGLE field (or in an array for CHOICE_SET_MULTIPLE).",
52
107
  Command:
53
- "uip df choice-sets get c1d2e3f4-0000-0000-0000-000000000001 --limit 2",
108
+ "uip df choice-sets list-values c1d2e3f4-0000-0000-0000-000000000001 --limit 2",
54
109
  Output: {
55
- Code: "ChoiceSetValues",
110
+ Code: OUTPUT_CODES.ChoiceSetValues,
56
111
  Data: {
57
- TotalCount: 2,
58
- Values: [
112
+ items: [
59
113
  {
60
- Id: "v1000000-0000-0000-0000-000000000001",
61
- Name: "travel",
62
- DisplayName: "Travel",
63
- NumberId: 1,
114
+ id: "v1000000-0000-0000-0000-000000000001",
115
+ name: "travel",
116
+ displayName: "Travel",
117
+ numberId: 0,
64
118
  },
65
119
  {
66
- Id: "v1000000-0000-0000-0000-000000000002",
67
- Name: "meals",
68
- DisplayName: "Meals",
69
- NumberId: 2,
120
+ id: "v1000000-0000-0000-0000-000000000002",
121
+ name: "meals",
122
+ displayName: "Meals",
123
+ numberId: 1,
70
124
  },
71
125
  ],
72
- HasNextPage: false,
126
+ totalCount: 2,
127
+ hasNextPage: false,
128
+ },
129
+ },
130
+ },
131
+ ];
132
+
133
+ const CHOICE_SETS_CREATE_EXAMPLES: CommandExample[] = [
134
+ {
135
+ Description:
136
+ "Create a choice set. The returned 'ID' is used as 'choiceSetId' on CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE entity fields. Add values with 'df choice-set-values create'.",
137
+ Command:
138
+ 'uip df choice-sets create ExpenseTypes --display-name "Expense Types" --description "Categories of expenses"',
139
+ Output: {
140
+ Code: OUTPUT_CODES.ChoiceSetCreated,
141
+ Data: { ID: "c1d2e3f4-0000-0000-0000-000000000001" },
142
+ },
143
+ },
144
+ ];
145
+
146
+ const CHOICE_SETS_UPDATE_EXAMPLES: CommandExample[] = [
147
+ {
148
+ Description:
149
+ "Update a choice set's display name and/or description. At least one of --display-name or --description is required.",
150
+ Command:
151
+ 'uip df choice-sets update c1d2e3f4-0000-0000-0000-000000000001 --display-name "Expense Categories"',
152
+ Output: {
153
+ Code: OUTPUT_CODES.ChoiceSetUpdated,
154
+ Data: { ID: "c1d2e3f4-0000-0000-0000-000000000001" },
155
+ },
156
+ },
157
+ ];
158
+
159
+ const CHOICE_SETS_DELETE_EXAMPLES: CommandExample[] = [
160
+ {
161
+ Description:
162
+ "Delete a choice set (irreversible — requires --confirm and --reason).",
163
+ Command:
164
+ 'uip df choice-sets delete c1d2e3f4-0000-0000-0000-000000000001 --confirm --reason "no longer used"',
165
+ Output: {
166
+ Code: OUTPUT_CODES.ChoiceSetDeleted,
167
+ Data: {
168
+ ID: "c1d2e3f4-0000-0000-0000-000000000001",
169
+ Reason: "no longer used",
73
170
  },
74
171
  },
75
172
  },
@@ -79,68 +176,46 @@ export const registerChoiceSetsCommand = (program: Command) => {
79
176
  const choiceSets = program
80
177
  .command("choice-sets")
81
178
  .description(
82
- "Browse Data Fabric choice sets. Read-only: choice sets are authored in the Data Fabric web UI and cannot be created, updated, or deleted from the CLI. " +
83
- "Use 'list' to find a choice set's ID (used as 'choiceSetId' on CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE entity fields), and 'get' to inspect its values.",
179
+ "Manage Data Fabric choice sets the enumerated value lists referenced by CHOICE_SET_SINGLE/CHOICE_SET_MULTIPLE entity fields. " +
180
+ "Use 'list'/'list-values' to inspect, and 'create'/'update'/'delete' to manage them. Manage individual values with 'df choice-set-values'.",
84
181
  );
85
182
 
86
183
  choiceSets
87
184
  .command("list")
88
185
  .description("List all Data Fabric choice sets")
89
- .option("-t, --tenant <tenant-name>", "Tenant name")
186
+ .addOption(
187
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
188
+ )
90
189
  .examples(CHOICE_SETS_LIST_EXAMPLES)
91
190
  .trackedAction(processContext, async (options: ListOptions) => {
92
- const [clientError, sdk] = await catchError(
93
- createDataFabricClient(options.tenant),
94
- );
95
-
96
- if (clientError) {
97
- OutputFormatter.error({
98
- Result: RESULTS.Failure,
99
- Message: "Error connecting to Data Fabric",
100
- Instructions: await extractErrorMessage(clientError),
101
- });
102
- processContext.exit(1);
103
- return;
104
- }
191
+ const sdk = await connectOrFail(options.tenant);
192
+ if (!sdk) return;
105
193
 
106
194
  const [listError, result] = await catchError(
107
195
  sdk.entities.choicesets.getAll(),
108
196
  );
109
197
 
110
198
  if (listError) {
111
- OutputFormatter.error({
112
- Result: RESULTS.Failure,
113
- Message: "Error listing choice sets",
114
- Instructions: await extractErrorMessage(listError),
115
- });
116
- processContext.exit(1);
117
- return;
199
+ return fail(
200
+ "Error listing choice sets",
201
+ await extractErrorMessage(listError),
202
+ );
118
203
  }
119
204
 
120
- const items = (result ?? []).map((cs) => ({
121
- ID: (cs as { id?: string }).id,
122
- Name: cs.name,
123
- DisplayName: cs.displayName || cs.name,
124
- Description: cs.description || "",
125
- FolderId: cs.folderId,
126
- CreatedBy: cs.createdBy,
127
- UpdatedBy: cs.updatedBy,
128
- CreatedTime: cs.createdTime,
129
- UpdatedTime: cs.updatedTime,
130
- }));
131
-
132
205
  OutputFormatter.success({
133
206
  Result: RESULTS.Success,
134
- Code: "ChoiceSetList",
135
- Data: items,
207
+ Code: OUTPUT_CODES.ChoiceSetList,
208
+ Data: result ?? [],
136
209
  });
137
210
  });
138
211
 
139
212
  choiceSets
140
- .command("get")
141
- .description("Get values for a Data Fabric choice set")
213
+ .command("list-values")
214
+ .description("List the values of a Data Fabric choice set")
142
215
  .argument("<choice-set-id>", "Choice set ID")
143
- .option("-t, --tenant <tenant-name>", "Tenant name")
216
+ .addOption(
217
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
218
+ )
144
219
  .option(
145
220
  "-l, --limit <number>",
146
221
  "Number of values to return per page",
@@ -160,58 +235,36 @@ export const registerChoiceSetsCommand = (program: Command) => {
160
235
  async (choiceSetId: string, options: GetOptions) => {
161
236
  const pageSize = Number(options.limit ?? "50");
162
237
  if (Number.isNaN(pageSize) || pageSize < 1) {
163
- OutputFormatter.error({
164
- Result: RESULTS.Failure,
165
- Message: "Invalid --limit value",
166
- Instructions: "Provide a positive integer for --limit.",
167
- });
168
- processContext.exit(1);
169
- return;
238
+ return fail(
239
+ "Invalid --limit value",
240
+ "Provide a positive integer for --limit.",
241
+ );
170
242
  }
171
243
 
172
244
  if (
173
245
  options.cursor !== undefined &&
174
246
  options.offset !== undefined
175
247
  ) {
176
- OutputFormatter.error({
177
- Result: RESULTS.Failure,
178
- Message: "--offset and --cursor are mutually exclusive",
179
- Instructions:
180
- "Use --offset to jump to a position by record count, or --cursor to continue from a previous response.",
181
- });
182
- processContext.exit(1);
183
- return;
248
+ return fail(
249
+ "--offset and --cursor are mutually exclusive",
250
+ "Use --offset to jump to a position by record count, or --cursor to continue from a previous response.",
251
+ );
184
252
  }
185
253
 
186
254
  let jumpToPage: number | undefined;
187
255
  if (options.offset !== undefined) {
188
256
  const offsetValue = Number(options.offset);
189
257
  if (Number.isNaN(offsetValue) || offsetValue < 0) {
190
- OutputFormatter.error({
191
- Result: RESULTS.Failure,
192
- Message: "Invalid --offset value",
193
- Instructions:
194
- "Provide a non-negative integer for --offset.",
195
- });
196
- processContext.exit(1);
197
- return;
258
+ return fail(
259
+ "Invalid --offset value",
260
+ "Provide a non-negative integer for --offset.",
261
+ );
198
262
  }
199
263
  jumpToPage = Math.floor(offsetValue / pageSize) + 1;
200
264
  }
201
265
 
202
- const [clientError, sdk] = await catchError(
203
- createDataFabricClient(options.tenant),
204
- );
205
-
206
- if (clientError) {
207
- OutputFormatter.error({
208
- Result: RESULTS.Failure,
209
- Message: "Error connecting to Data Fabric",
210
- Instructions: await extractErrorMessage(clientError),
211
- });
212
- processContext.exit(1);
213
- return;
214
- }
266
+ const sdk = await connectOrFail(options.tenant);
267
+ if (!sdk) return;
215
268
 
216
269
  const paginationOptions =
217
270
  options.cursor !== undefined
@@ -228,57 +281,418 @@ export const registerChoiceSetsCommand = (program: Command) => {
228
281
  );
229
282
 
230
283
  if (getError) {
231
- OutputFormatter.error({
232
- Result: RESULTS.Failure,
233
- Message: `Error getting choice set '${choiceSetId}'`,
234
- Instructions: await extractErrorMessage(getError),
235
- });
236
- processContext.exit(1);
237
- return;
284
+ return fail(
285
+ `Error getting choice set '${choiceSetId}'`,
286
+ await extractErrorMessage(getError),
287
+ );
238
288
  }
239
289
 
240
- type ChoiceSetValue = {
241
- id?: string;
242
- name?: string;
243
- displayName?: string;
244
- numberId?: number;
245
- createdTime?: string;
246
- updatedTime?: string;
290
+ OutputFormatter.success({
291
+ Result: RESULTS.Success,
292
+ Code: OUTPUT_CODES.ChoiceSetValues,
293
+ Data: result,
294
+ });
295
+ },
296
+ );
297
+
298
+ choiceSets
299
+ .command("create")
300
+ .description("Create a new Data Fabric choice set")
301
+ .argument(
302
+ "<name>",
303
+ "Choice set name (must start with a letter; letters, numbers, and underscores only)",
304
+ )
305
+ .addOption(
306
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
307
+ )
308
+ .option("--display-name <name>", "Human-readable display name")
309
+ .option("--description <text>", "Choice set description")
310
+ .option(
311
+ "--folder-key <uuid>",
312
+ "UUID of the folder to place the choice set in (defaults to the tenant-level folder)",
313
+ )
314
+ .examples(CHOICE_SETS_CREATE_EXAMPLES)
315
+ .trackedAction(
316
+ processContext,
317
+ async (name: string, options: CreateOptions) => {
318
+ const sdk = await connectOrFail(options.tenant);
319
+ if (!sdk) return;
320
+
321
+ const createOpts: ChoiceSetCreateOptions = {
322
+ ...(options.displayName !== undefined && {
323
+ displayName: options.displayName,
324
+ }),
325
+ ...(options.description !== undefined && {
326
+ description: options.description,
327
+ }),
328
+ ...(options.folderKey !== undefined && {
329
+ folderKey: options.folderKey,
330
+ }),
247
331
  };
248
- const response = result as unknown as {
249
- items?: ChoiceSetValue[];
250
- totalCount?: number;
251
- hasNextPage?: boolean;
252
- nextCursor?: unknown;
253
- currentPage?: number;
254
- totalPages?: number;
332
+
333
+ const choiceSetService: ChoiceSetServiceModel =
334
+ sdk.entities.choicesets;
335
+ const [createError, choiceSetId] = await catchError(
336
+ choiceSetService.create(
337
+ name,
338
+ Object.keys(createOpts).length > 0
339
+ ? createOpts
340
+ : undefined,
341
+ ),
342
+ );
343
+
344
+ if (createError) {
345
+ return fail(
346
+ "Error creating choice set",
347
+ await extractErrorMessage(createError),
348
+ );
349
+ }
350
+
351
+ OutputFormatter.success({
352
+ Result: RESULTS.Success,
353
+ Code: OUTPUT_CODES.ChoiceSetCreated,
354
+ Data: { ID: choiceSetId },
355
+ });
356
+ },
357
+ );
358
+
359
+ choiceSets
360
+ .command("update")
361
+ .description("Update the metadata of a Data Fabric choice set")
362
+ .argument("<choice-set-id>", "Choice set ID")
363
+ .addOption(
364
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
365
+ )
366
+ .option("--display-name <name>", "New display name")
367
+ .option("--description <text>", "New description")
368
+ .examples(CHOICE_SETS_UPDATE_EXAMPLES)
369
+ .trackedAction(
370
+ processContext,
371
+ async (choiceSetId: string, options: UpdateOptions) => {
372
+ if (
373
+ options.displayName === undefined &&
374
+ options.description === undefined
375
+ ) {
376
+ return fail(
377
+ "No update fields provided",
378
+ "Provide at least one of --display-name or --description.",
379
+ );
380
+ }
381
+
382
+ const sdk = await connectOrFail(options.tenant);
383
+ if (!sdk) return;
384
+
385
+ const updateOpts: ChoiceSetUpdateOptions = {
386
+ ...(options.displayName !== undefined && {
387
+ displayName: options.displayName,
388
+ }),
389
+ ...(options.description !== undefined && {
390
+ description: options.description,
391
+ }),
255
392
  };
256
- const values = (response.items ?? []).map((v) => ({
257
- Id: v.id,
258
- Name: v.name,
259
- DisplayName: v.displayName || v.name,
260
- NumberId: v.numberId,
261
- CreatedTime: v.createdTime,
262
- UpdatedTime: v.updatedTime,
263
- }));
264
- const nextCursor = extractCursorValue(response.nextCursor);
393
+
394
+ const choiceSetService: ChoiceSetServiceModel =
395
+ sdk.entities.choicesets;
396
+ const [updateError] = await catchError(
397
+ choiceSetService.updateById(choiceSetId, updateOpts),
398
+ );
399
+
400
+ if (updateError) {
401
+ return fail(
402
+ `Error updating choice set '${choiceSetId}'`,
403
+ await extractErrorMessage(updateError),
404
+ );
405
+ }
406
+
407
+ OutputFormatter.success({
408
+ Result: RESULTS.Success,
409
+ Code: OUTPUT_CODES.ChoiceSetUpdated,
410
+ Data: { ID: choiceSetId },
411
+ });
412
+ },
413
+ );
414
+
415
+ choiceSets
416
+ .command("delete")
417
+ .description("Delete a Data Fabric choice set (irreversible)")
418
+ .argument("<choice-set-id>", "Choice set ID")
419
+ .addOption(
420
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
421
+ )
422
+ .option("-y, --yes", "Acknowledge this is an irreversible operation")
423
+ .addOption(
424
+ new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
425
+ )
426
+ .option(
427
+ "--reason <reason>",
428
+ "Reason for the deletion — echoed back in the response so the caller can log it",
429
+ )
430
+ .examples(CHOICE_SETS_DELETE_EXAMPLES)
431
+ .trackedAction(
432
+ processContext,
433
+ async (choiceSetId: string, options: DeleteOptions) => {
434
+ const reason = requireDestructiveConfirmation(
435
+ options,
436
+ `delete choice set '${choiceSetId}'`,
437
+ 'Pass --reason "<text>" to record why the choice set is being deleted.',
438
+ );
439
+ if (reason === null) return;
440
+
441
+ const sdk = await connectOrFail(options.tenant);
442
+ if (!sdk) return;
443
+
444
+ const choiceSetService: ChoiceSetServiceModel =
445
+ sdk.entities.choicesets;
446
+ const [deleteError] = await catchError(
447
+ choiceSetService.deleteById(choiceSetId),
448
+ );
449
+
450
+ if (deleteError) {
451
+ return fail(
452
+ `Error deleting choice set '${choiceSetId}'`,
453
+ await extractErrorMessage(deleteError),
454
+ );
455
+ }
456
+
457
+ OutputFormatter.success({
458
+ Result: RESULTS.Success,
459
+ Code: OUTPUT_CODES.ChoiceSetDeleted,
460
+ Data: { ID: choiceSetId, Reason: reason },
461
+ });
462
+ },
463
+ );
464
+ };
465
+
466
+ const CHOICE_SET_VALUES_CREATE_EXAMPLES: CommandExample[] = [
467
+ {
468
+ Description:
469
+ "Add a value to a choice set. The returned 'numberId' is the integer to pass when inserting or updating records into a CHOICE_SET_SINGLE field (or in an array for CHOICE_SET_MULTIPLE).",
470
+ Command:
471
+ 'uip df choice-set-values create c1d2e3f4-0000-0000-0000-000000000001 travel --display-name "Travel"',
472
+ Output: {
473
+ Code: OUTPUT_CODES.ChoiceSetValueCreated,
474
+ Data: {
475
+ id: "v1000000-0000-0000-0000-000000000001",
476
+ name: "travel",
477
+ displayName: "Travel",
478
+ numberId: 0,
479
+ },
480
+ },
481
+ },
482
+ ];
483
+
484
+ const CHOICE_SET_VALUES_UPDATE_EXAMPLES: CommandExample[] = [
485
+ {
486
+ Description:
487
+ "Update the display name of an existing choice set value. Find value IDs with 'df choice-sets list-values <choice-set-id>'.",
488
+ Command:
489
+ 'uip df choice-set-values update c1d2e3f4-0000-0000-0000-000000000001 v1000000-0000-0000-0000-000000000001 "Business Travel"',
490
+ Output: {
491
+ Code: OUTPUT_CODES.ChoiceSetValueUpdated,
492
+ Data: {
493
+ id: "v1000000-0000-0000-0000-000000000001",
494
+ name: "travel",
495
+ displayName: "Business Travel",
496
+ numberId: 0,
497
+ },
498
+ },
499
+ },
500
+ ];
501
+
502
+ const CHOICE_SET_VALUES_DELETE_EXAMPLES: CommandExample[] = [
503
+ {
504
+ Description:
505
+ "Delete one or more values from a choice set (irreversible — requires --confirm and --reason). Pass value IDs as a comma-separated list.",
506
+ Command:
507
+ 'uip df choice-set-values delete c1d2e3f4-0000-0000-0000-000000000001 --ids v1000000-0000-0000-0000-000000000001,v1000000-0000-0000-0000-000000000002 --confirm --reason "deprecated categories"',
508
+ Output: {
509
+ Code: OUTPUT_CODES.ChoiceSetValuesDeleted,
510
+ Data: {
511
+ ChoiceSetId: "c1d2e3f4-0000-0000-0000-000000000001",
512
+ DeletedIds: [
513
+ "v1000000-0000-0000-0000-000000000001",
514
+ "v1000000-0000-0000-0000-000000000002",
515
+ ],
516
+ Reason: "deprecated categories",
517
+ },
518
+ },
519
+ },
520
+ ];
521
+
522
+ export const registerChoiceSetValuesCommand = (program: Command) => {
523
+ const values = program
524
+ .command("choice-set-values")
525
+ .description(
526
+ "Manage the individual values of a Data Fabric choice set. " +
527
+ "Use 'df choice-sets list-values <choice-set-id>' to list existing values and their IDs.",
528
+ );
529
+
530
+ values
531
+ .command("create")
532
+ .description("Add a value to a Data Fabric choice set")
533
+ .argument("<choice-set-id>", "Choice set ID")
534
+ .argument(
535
+ "<name>",
536
+ "Value name (must start with a letter; letters, numbers, and underscores only)",
537
+ )
538
+ .addOption(
539
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
540
+ )
541
+ .option("--display-name <name>", "Human-readable display name")
542
+ .examples(CHOICE_SET_VALUES_CREATE_EXAMPLES)
543
+ .trackedAction(
544
+ processContext,
545
+ async (
546
+ choiceSetId: string,
547
+ name: string,
548
+ options: ValueCreateOptions,
549
+ ) => {
550
+ const sdk = await connectOrFail(options.tenant);
551
+ if (!sdk) return;
552
+
553
+ const insertOpts: ChoiceSetValueInsertOptions = {
554
+ ...(options.displayName !== undefined && {
555
+ displayName: options.displayName,
556
+ }),
557
+ };
558
+
559
+ const choiceSetService: ChoiceSetServiceModel =
560
+ sdk.entities.choicesets;
561
+ const [createError, result] = await catchError(
562
+ choiceSetService.insertValueById(
563
+ choiceSetId,
564
+ name,
565
+ Object.keys(insertOpts).length > 0
566
+ ? insertOpts
567
+ : undefined,
568
+ ),
569
+ );
570
+
571
+ if (createError) {
572
+ return fail(
573
+ `Error creating value in choice set '${choiceSetId}'`,
574
+ await extractErrorMessage(createError),
575
+ );
576
+ }
577
+
578
+ OutputFormatter.success({
579
+ Result: RESULTS.Success,
580
+ Code: OUTPUT_CODES.ChoiceSetValueCreated,
581
+ Data: result,
582
+ });
583
+ },
584
+ );
585
+
586
+ values
587
+ .command("update")
588
+ .description("Update the display name of a choice set value")
589
+ .argument("<choice-set-id>", "Choice set ID")
590
+ .argument("<value-id>", "Choice set value ID")
591
+ .argument("<display-name>", "New display name for the value")
592
+ .addOption(
593
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
594
+ )
595
+ .examples(CHOICE_SET_VALUES_UPDATE_EXAMPLES)
596
+ .trackedAction(
597
+ processContext,
598
+ async (
599
+ choiceSetId: string,
600
+ valueId: string,
601
+ displayName: string,
602
+ options: ValueUpdateOptions,
603
+ ) => {
604
+ const sdk = await connectOrFail(options.tenant);
605
+ if (!sdk) return;
606
+
607
+ const choiceSetService: ChoiceSetServiceModel =
608
+ sdk.entities.choicesets;
609
+ const [updateError, result] = await catchError(
610
+ choiceSetService.updateValueById(
611
+ choiceSetId,
612
+ valueId,
613
+ displayName,
614
+ ),
615
+ );
616
+
617
+ if (updateError) {
618
+ return fail(
619
+ `Error updating value '${valueId}' in choice set '${choiceSetId}'`,
620
+ await extractErrorMessage(updateError),
621
+ );
622
+ }
623
+
624
+ OutputFormatter.success({
625
+ Result: RESULTS.Success,
626
+ Code: OUTPUT_CODES.ChoiceSetValueUpdated,
627
+ Data: result,
628
+ });
629
+ },
630
+ );
631
+
632
+ values
633
+ .command("delete")
634
+ .description(
635
+ "Delete values from a Data Fabric choice set (irreversible)",
636
+ )
637
+ .argument("<choice-set-id>", "Choice set ID")
638
+ .addOption(
639
+ createHiddenDeprecatedTenantOption("-t, --tenant <tenant-name>"),
640
+ )
641
+ .option("--ids <ids>", "Comma-separated list of value IDs to delete")
642
+ .option("-y, --yes", "Acknowledge this is an irreversible operation")
643
+ .addOption(
644
+ new Option("--confirm", "Deprecated alias for --yes").hideHelp(),
645
+ )
646
+ .option(
647
+ "--reason <reason>",
648
+ "Reason for the deletion — echoed back in the response so the caller can log it",
649
+ )
650
+ .examples(CHOICE_SET_VALUES_DELETE_EXAMPLES)
651
+ .trackedAction(
652
+ processContext,
653
+ async (choiceSetId: string, options: ValueDeleteOptions) => {
654
+ const valueIds = (options.ids ?? "")
655
+ .split(",")
656
+ .map((id) => id.trim())
657
+ .filter((id) => id.length > 0);
658
+
659
+ if (valueIds.length === 0) {
660
+ return fail(
661
+ "No value IDs provided",
662
+ "Pass --ids <id,id,...> with at least one value ID to delete.",
663
+ );
664
+ }
665
+
666
+ const reason = requireDestructiveConfirmation(
667
+ options,
668
+ `delete ${valueIds.length} value(s) from choice set '${choiceSetId}'`,
669
+ 'Pass --reason "<text>" to record why the values are being deleted.',
670
+ );
671
+ if (reason === null) return;
672
+
673
+ const sdk = await connectOrFail(options.tenant);
674
+ if (!sdk) return;
675
+
676
+ const choiceSetService: ChoiceSetServiceModel =
677
+ sdk.entities.choicesets;
678
+ const [deleteError] = await catchError(
679
+ choiceSetService.deleteValuesById(choiceSetId, valueIds),
680
+ );
681
+
682
+ if (deleteError) {
683
+ return fail(
684
+ `Error deleting values from choice set '${choiceSetId}'`,
685
+ await extractErrorMessage(deleteError),
686
+ );
687
+ }
265
688
 
266
689
  OutputFormatter.success({
267
690
  Result: RESULTS.Success,
268
- Code: "ChoiceSetValues",
691
+ Code: OUTPUT_CODES.ChoiceSetValuesDeleted,
269
692
  Data: {
270
- TotalCount: response.totalCount ?? values.length,
271
- Values: values,
272
- HasNextPage: response.hasNextPage ?? false,
273
- ...(nextCursor !== undefined && {
274
- NextCursor: nextCursor,
275
- }),
276
- ...(response.currentPage !== undefined && {
277
- CurrentPage: response.currentPage,
278
- }),
279
- ...(response.totalPages !== undefined && {
280
- TotalPages: response.totalPages,
281
- }),
693
+ ChoiceSetId: choiceSetId,
694
+ DeletedIds: valueIds,
695
+ Reason: reason,
282
696
  },
283
697
  });
284
698
  },