pltr-cli 0.6.0__py3-none-any.whl → 0.7.0__py3-none-any.whl

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.
pltr/commands/dataset.py CHANGED
@@ -22,6 +22,9 @@ branches_app = typer.Typer()
22
22
  files_app = typer.Typer()
23
23
  transactions_app = typer.Typer()
24
24
  views_app = typer.Typer()
25
+ schema_app = typer.Typer()
26
+ schedules_app = typer.Typer()
27
+ jobs_app = typer.Typer()
25
28
  console = Console()
26
29
  formatter = OutputFormatter(console)
27
30
 
@@ -70,7 +73,175 @@ def get_dataset(
70
73
  raise typer.Exit(1)
71
74
 
72
75
 
73
- # schema command removed - uses preview-only API that returns INVALID_ARGUMENT
76
+ # Schema commands
77
+ @schema_app.command("get")
78
+ def get_schema(
79
+ dataset_rid: str = typer.Argument(
80
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
81
+ ),
82
+ profile: Optional[str] = typer.Option(
83
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
84
+ ),
85
+ format: str = typer.Option(
86
+ "table",
87
+ "--format",
88
+ "-f",
89
+ help="Output format (table, json, csv)",
90
+ autocompletion=complete_output_format,
91
+ ),
92
+ output: Optional[str] = typer.Option(
93
+ None, "--output", "-o", help="Output file path"
94
+ ),
95
+ ):
96
+ """Get the schema of a dataset."""
97
+ try:
98
+ cache_rid(dataset_rid)
99
+ service = DatasetService(profile=profile)
100
+
101
+ with SpinnerProgressTracker().track_spinner(
102
+ f"Fetching schema for {dataset_rid}..."
103
+ ):
104
+ schema = service.get_schema(dataset_rid)
105
+
106
+ # Format schema for display
107
+ if format == "json":
108
+ formatter._format_json(schema, output)
109
+ else:
110
+ formatter.print_info(f"Dataset: {dataset_rid}")
111
+ formatter.print_info(f"Status: {schema.get('status', 'Unknown')}")
112
+ if schema.get("schema"):
113
+ formatter.print_info("\nSchema:")
114
+ formatter._format_json(schema.get("schema"))
115
+
116
+ if output:
117
+ formatter.print_success(f"Schema saved to {output}")
118
+
119
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
120
+ formatter.print_error(f"Authentication error: {e}")
121
+ raise typer.Exit(1)
122
+ except Exception as e:
123
+ formatter.print_error(f"Failed to get schema: {e}")
124
+ raise typer.Exit(1)
125
+
126
+
127
+ @schema_app.command("set")
128
+ def set_schema(
129
+ dataset_rid: str = typer.Argument(
130
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
131
+ ),
132
+ from_csv: Optional[str] = typer.Option(
133
+ None, "--from-csv", help="Infer schema from CSV file"
134
+ ),
135
+ json_schema: Optional[str] = typer.Option(
136
+ None, "--json", help="JSON string defining the schema"
137
+ ),
138
+ json_file: Optional[str] = typer.Option(
139
+ None, "--json-file", help="Path to JSON file containing schema definition"
140
+ ),
141
+ branch: str = typer.Option("master", "--branch", help="Dataset branch"),
142
+ transaction_rid: Optional[str] = typer.Option(
143
+ None, "--transaction-rid", help="Transaction RID to use"
144
+ ),
145
+ profile: Optional[str] = typer.Option(
146
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
147
+ ),
148
+ ):
149
+ """Set or update the schema of a dataset."""
150
+ try:
151
+ cache_rid(dataset_rid)
152
+ service = DatasetService(profile=profile)
153
+
154
+ # Validate that exactly one input method is provided
155
+ input_methods = [from_csv, json_schema, json_file]
156
+ if sum(x is not None for x in input_methods) != 1:
157
+ formatter.print_error(
158
+ "Exactly one of --from-csv, --json, or --json-file must be provided"
159
+ )
160
+ raise typer.Exit(1)
161
+
162
+ schema = None
163
+
164
+ # Infer schema from CSV
165
+ if from_csv:
166
+ with SpinnerProgressTracker().track_spinner(
167
+ f"Inferring schema from {from_csv}..."
168
+ ):
169
+ schema = service.infer_schema_from_csv(from_csv)
170
+ formatter.print_info(
171
+ f"Inferred schema from CSV with {len(schema.field_schema_list)} fields"
172
+ )
173
+ for field in schema.field_schema_list:
174
+ formatter.print_info(
175
+ f" - {field.name}: {field.type} (nullable={field.nullable})"
176
+ )
177
+
178
+ # Parse schema from JSON string
179
+ elif json_schema:
180
+ import json
181
+ from foundry_sdk.v2.core.models import DatasetSchema, DatasetFieldSchema
182
+
183
+ try:
184
+ schema_data = json.loads(json_schema)
185
+ fields = []
186
+ for field_data in schema_data.get("fields", []):
187
+ fields.append(
188
+ DatasetFieldSchema(
189
+ name=field_data["name"],
190
+ type=field_data["type"],
191
+ nullable=field_data.get("nullable", True),
192
+ )
193
+ )
194
+ schema = DatasetSchema(field_schema_list=fields)
195
+ except (json.JSONDecodeError, KeyError) as e:
196
+ formatter.print_error(f"Invalid JSON schema: {e}")
197
+ raise typer.Exit(1)
198
+
199
+ # Load schema from JSON file
200
+ elif json_file:
201
+ import json
202
+ from foundry_sdk.v2.core.models import DatasetSchema, DatasetFieldSchema
203
+
204
+ try:
205
+ with open(json_file, "r") as f:
206
+ schema_data = json.load(f)
207
+ fields = []
208
+ for field_data in schema_data.get("fields", []):
209
+ fields.append(
210
+ DatasetFieldSchema(
211
+ name=field_data["name"],
212
+ type=field_data["type"],
213
+ nullable=field_data.get("nullable", True),
214
+ )
215
+ )
216
+ schema = DatasetSchema(field_schema_list=fields)
217
+ except (FileNotFoundError, json.JSONDecodeError, KeyError) as e:
218
+ formatter.print_error(f"Failed to load schema from file: {e}")
219
+ raise typer.Exit(1)
220
+
221
+ # Apply the schema
222
+ with SpinnerProgressTracker().track_spinner(
223
+ f"Setting schema on dataset {dataset_rid}..."
224
+ ):
225
+ service.put_schema(
226
+ dataset_rid=dataset_rid,
227
+ schema=schema,
228
+ branch=branch,
229
+ transaction_rid=transaction_rid,
230
+ )
231
+
232
+ formatter.print_success(f"Successfully set schema on dataset {dataset_rid}")
233
+ if transaction_rid:
234
+ formatter.print_info(f"Transaction RID: {transaction_rid}")
235
+
236
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
237
+ formatter.print_error(f"Authentication error: {e}")
238
+ raise typer.Exit(1)
239
+ except FileNotFoundError as e:
240
+ formatter.print_error(f"File not found: {e}")
241
+ raise typer.Exit(1)
242
+ except Exception as e:
243
+ formatter.print_error(f"Failed to set schema: {e}")
244
+ raise typer.Exit(1)
74
245
 
75
246
 
76
247
  @app.command("create")
@@ -189,6 +360,139 @@ def create_branch(
189
360
  raise typer.Exit(1)
190
361
 
191
362
 
363
+ @branches_app.command("delete")
364
+ def delete_branch(
365
+ dataset_rid: str = typer.Argument(
366
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
367
+ ),
368
+ branch_name: str = typer.Argument(..., help="Branch name to delete"),
369
+ confirm: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
370
+ profile: Optional[str] = typer.Option(
371
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
372
+ ),
373
+ ):
374
+ """Delete a branch from a dataset."""
375
+ try:
376
+ cache_rid(dataset_rid)
377
+ service = DatasetService(profile=profile)
378
+
379
+ # Prevent deleting master branch
380
+ if branch_name.lower() == "master":
381
+ formatter.print_error("Cannot delete the master branch")
382
+ raise typer.Exit(1)
383
+
384
+ # Confirmation prompt
385
+ if not confirm:
386
+ confirmed = typer.confirm(
387
+ f"Are you sure you want to delete branch '{branch_name}' from dataset {dataset_rid}? "
388
+ f"This action cannot be undone."
389
+ )
390
+ if not confirmed:
391
+ formatter.print_info("Branch deletion cancelled")
392
+ raise typer.Exit(0)
393
+
394
+ with SpinnerProgressTracker().track_spinner(
395
+ f"Deleting branch '{branch_name}' from {dataset_rid}..."
396
+ ):
397
+ service.delete_branch(dataset_rid, branch_name)
398
+
399
+ formatter.print_success(f"Branch '{branch_name}' deleted successfully")
400
+ formatter.print_info(f"Dataset: {dataset_rid}")
401
+
402
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
403
+ formatter.print_error(f"Authentication error: {e}")
404
+ raise typer.Exit(1)
405
+ except Exception as e:
406
+ formatter.print_error(f"Failed to delete branch: {e}")
407
+ raise typer.Exit(1)
408
+
409
+
410
+ @branches_app.command("get")
411
+ def get_branch(
412
+ dataset_rid: str = typer.Argument(
413
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
414
+ ),
415
+ branch_name: str = typer.Argument(..., help="Branch name"),
416
+ profile: Optional[str] = typer.Option(
417
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
418
+ ),
419
+ format: str = typer.Option(
420
+ "table",
421
+ "--format",
422
+ "-f",
423
+ help="Output format (table, json, csv)",
424
+ autocompletion=complete_output_format,
425
+ ),
426
+ output: Optional[str] = typer.Option(
427
+ None, "--output", "-o", help="Output file path"
428
+ ),
429
+ ):
430
+ """Get detailed information about a specific branch."""
431
+ try:
432
+ cache_rid(dataset_rid)
433
+ service = DatasetService(profile=profile)
434
+
435
+ with SpinnerProgressTracker().track_spinner(
436
+ f"Fetching branch '{branch_name}' from {dataset_rid}..."
437
+ ):
438
+ branch = service.get_branch(dataset_rid, branch_name)
439
+
440
+ formatter.format_branch_detail(branch, format, output)
441
+
442
+ if output:
443
+ formatter.print_success(f"Branch information saved to {output}")
444
+
445
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
446
+ formatter.print_error(f"Authentication error: {e}")
447
+ raise typer.Exit(1)
448
+ except Exception as e:
449
+ formatter.print_error(f"Failed to get branch: {e}")
450
+ raise typer.Exit(1)
451
+
452
+
453
+ @branches_app.command("transactions")
454
+ def list_branch_transactions(
455
+ dataset_rid: str = typer.Argument(
456
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
457
+ ),
458
+ branch_name: str = typer.Argument(..., help="Branch name"),
459
+ profile: Optional[str] = typer.Option(
460
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
461
+ ),
462
+ format: str = typer.Option(
463
+ "table",
464
+ "--format",
465
+ "-f",
466
+ help="Output format (table, json, csv)",
467
+ autocompletion=complete_output_format,
468
+ ),
469
+ output: Optional[str] = typer.Option(
470
+ None, "--output", "-o", help="Output file path"
471
+ ),
472
+ ):
473
+ """Get transaction history for a specific branch."""
474
+ try:
475
+ cache_rid(dataset_rid)
476
+ service = DatasetService(profile=profile)
477
+
478
+ with SpinnerProgressTracker().track_spinner(
479
+ f"Fetching transaction history for branch '{branch_name}' in {dataset_rid}..."
480
+ ):
481
+ transactions = service.get_branch_transactions(dataset_rid, branch_name)
482
+
483
+ formatter.format_transactions(transactions, format, output)
484
+
485
+ if output:
486
+ formatter.print_success(f"Branch transactions saved to {output}")
487
+
488
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
489
+ formatter.print_error(f"Authentication error: {e}")
490
+ raise typer.Exit(1)
491
+ except Exception as e:
492
+ formatter.print_error(f"Failed to get branch transactions: {e}")
493
+ raise typer.Exit(1)
494
+
495
+
192
496
  # Files commands
193
497
  @files_app.command("list")
194
498
  def list_files(
@@ -320,6 +624,93 @@ def get_file(
320
624
  raise typer.Exit(1)
321
625
 
322
626
 
627
+ @files_app.command("delete")
628
+ def delete_file(
629
+ dataset_rid: str = typer.Argument(
630
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
631
+ ),
632
+ file_path: str = typer.Argument(..., help="Path of file within dataset to delete"),
633
+ branch: str = typer.Option("master", "--branch", help="Dataset branch"),
634
+ confirm: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt"),
635
+ profile: Optional[str] = typer.Option(
636
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
637
+ ),
638
+ ):
639
+ """Delete a file from a dataset."""
640
+ try:
641
+ cache_rid(dataset_rid)
642
+ service = DatasetService(profile=profile)
643
+
644
+ # Confirmation prompt
645
+ if not confirm:
646
+ confirmed = typer.confirm(
647
+ f"Are you sure you want to delete '{file_path}' from dataset {dataset_rid}?"
648
+ )
649
+ if not confirmed:
650
+ formatter.print_info("File deletion cancelled")
651
+ raise typer.Exit(0)
652
+
653
+ with SpinnerProgressTracker().track_spinner(
654
+ f"Deleting {file_path} from {dataset_rid}..."
655
+ ):
656
+ service.delete_file(dataset_rid, file_path, branch)
657
+
658
+ formatter.print_success(f"File '{file_path}' deleted successfully")
659
+ formatter.print_info(f"Dataset: {dataset_rid}")
660
+ formatter.print_info(f"Branch: {branch}")
661
+
662
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
663
+ formatter.print_error(f"Authentication error: {e}")
664
+ raise typer.Exit(1)
665
+ except Exception as e:
666
+ formatter.print_error(f"Failed to delete file: {e}")
667
+ raise typer.Exit(1)
668
+
669
+
670
+ @files_app.command("info")
671
+ def get_file_info(
672
+ dataset_rid: str = typer.Argument(
673
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
674
+ ),
675
+ file_path: str = typer.Argument(..., help="Path of file within dataset"),
676
+ branch: str = typer.Option("master", "--branch", help="Dataset branch"),
677
+ profile: Optional[str] = typer.Option(
678
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
679
+ ),
680
+ format: str = typer.Option(
681
+ "table",
682
+ "--format",
683
+ "-f",
684
+ help="Output format (table, json, csv)",
685
+ autocompletion=complete_output_format,
686
+ ),
687
+ output: Optional[str] = typer.Option(
688
+ None, "--output", "-o", help="Output file path"
689
+ ),
690
+ ):
691
+ """Get metadata information about a file in a dataset."""
692
+ try:
693
+ cache_rid(dataset_rid)
694
+ service = DatasetService(profile=profile)
695
+
696
+ with SpinnerProgressTracker().track_spinner(
697
+ f"Getting file info for {file_path} in {dataset_rid}..."
698
+ ):
699
+ file_info = service.get_file_info(dataset_rid, file_path, branch)
700
+
701
+ formatter.format_file_info(file_info, format, output)
702
+
703
+ if output:
704
+ formatter.print_success(f"File information saved to {output}")
705
+
706
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
707
+ formatter.print_error(f"Authentication error: {e}")
708
+ raise typer.Exit(1)
709
+ except Exception as e:
710
+ formatter.print_error(f"Failed to get file info: {e}")
711
+ raise typer.Exit(1)
712
+
713
+
323
714
  # Transaction commands
324
715
  @transactions_app.command("start")
325
716
  def start_transaction(
@@ -581,6 +972,49 @@ def list_transactions(
581
972
  raise typer.Exit(1)
582
973
 
583
974
 
975
+ @transactions_app.command("build")
976
+ def get_transaction_build(
977
+ dataset_rid: str = typer.Argument(
978
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
979
+ ),
980
+ transaction_rid: str = typer.Argument(..., help="Transaction Resource Identifier"),
981
+ profile: Optional[str] = typer.Option(
982
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
983
+ ),
984
+ format: str = typer.Option(
985
+ "table",
986
+ "--format",
987
+ "-f",
988
+ help="Output format (table, json, csv)",
989
+ autocompletion=complete_output_format,
990
+ ),
991
+ output: Optional[str] = typer.Option(
992
+ None, "--output", "-o", help="Output file path"
993
+ ),
994
+ ):
995
+ """Get build information for a transaction."""
996
+ try:
997
+ cache_rid(dataset_rid)
998
+ service = DatasetService(profile=profile)
999
+
1000
+ with SpinnerProgressTracker().track_spinner(
1001
+ f"Fetching build information for transaction {transaction_rid}..."
1002
+ ):
1003
+ build_info = service.get_transaction_build(dataset_rid, transaction_rid)
1004
+
1005
+ formatter.format_transaction_build(build_info, format, output)
1006
+
1007
+ if output:
1008
+ formatter.print_success(f"Build information saved to {output}")
1009
+
1010
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1011
+ formatter.print_error(f"Authentication error: {e}")
1012
+ raise typer.Exit(1)
1013
+ except Exception as e:
1014
+ formatter.print_error(f"Failed to get transaction build: {e}")
1015
+ raise typer.Exit(1)
1016
+
1017
+
584
1018
  # Views commands
585
1019
  @views_app.command("list")
586
1020
  def list_views(
@@ -627,6 +1061,223 @@ def list_views(
627
1061
  raise typer.Exit(1)
628
1062
 
629
1063
 
1064
+ @views_app.command("get")
1065
+ def get_view(
1066
+ view_rid: str = typer.Argument(
1067
+ ..., help="View Resource Identifier", autocompletion=complete_rid
1068
+ ),
1069
+ branch: str = typer.Option("master", "--branch", help="Branch name"),
1070
+ profile: Optional[str] = typer.Option(
1071
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1072
+ ),
1073
+ format: str = typer.Option(
1074
+ "table",
1075
+ "--format",
1076
+ "-f",
1077
+ help="Output format (table, json, csv)",
1078
+ autocompletion=complete_output_format,
1079
+ ),
1080
+ output: Optional[str] = typer.Option(
1081
+ None, "--output", "-o", help="Output file path"
1082
+ ),
1083
+ ):
1084
+ """Get detailed information about a view."""
1085
+ try:
1086
+ cache_rid(view_rid)
1087
+ service = DatasetService(profile=profile)
1088
+
1089
+ with SpinnerProgressTracker().track_spinner(f"Fetching view {view_rid}..."):
1090
+ view = service.get_view(view_rid, branch)
1091
+
1092
+ formatter.format_view_detail(view, format, output)
1093
+
1094
+ if output:
1095
+ formatter.print_success(f"View information saved to {output}")
1096
+
1097
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1098
+ formatter.print_error(f"Authentication error: {e}")
1099
+ raise typer.Exit(1)
1100
+ except Exception as e:
1101
+ formatter.print_error(f"Failed to get view: {e}")
1102
+ raise typer.Exit(1)
1103
+
1104
+
1105
+ @views_app.command("add-datasets")
1106
+ def add_backing_datasets(
1107
+ view_rid: str = typer.Argument(
1108
+ ..., help="View Resource Identifier", autocompletion=complete_rid
1109
+ ),
1110
+ dataset_rids: list[str] = typer.Argument(
1111
+ ..., help="Dataset RIDs to add as backing datasets"
1112
+ ),
1113
+ profile: Optional[str] = typer.Option(
1114
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1115
+ ),
1116
+ format: str = typer.Option(
1117
+ "table",
1118
+ "--format",
1119
+ "-f",
1120
+ help="Output format (table, json, csv)",
1121
+ autocompletion=complete_output_format,
1122
+ ),
1123
+ ):
1124
+ """Add backing datasets to a view."""
1125
+ try:
1126
+ cache_rid(view_rid)
1127
+ service = DatasetService(profile=profile)
1128
+
1129
+ with SpinnerProgressTracker().track_spinner(
1130
+ f"Adding {len(dataset_rids)} backing datasets to view {view_rid}..."
1131
+ ):
1132
+ result = service.add_backing_datasets(view_rid, dataset_rids)
1133
+
1134
+ formatter.print_success("Successfully added backing datasets to view")
1135
+ formatter.print_info(f"View RID: {view_rid}")
1136
+ formatter.print_info(f"Added datasets: {', '.join(dataset_rids)}")
1137
+
1138
+ if format == "json":
1139
+ formatter._format_json(result)
1140
+
1141
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1142
+ formatter.print_error(f"Authentication error: {e}")
1143
+ raise typer.Exit(1)
1144
+ except Exception as e:
1145
+ formatter.print_error(f"Failed to add backing datasets: {e}")
1146
+ raise typer.Exit(1)
1147
+
1148
+
1149
+ @views_app.command("remove-datasets")
1150
+ def remove_backing_datasets(
1151
+ view_rid: str = typer.Argument(
1152
+ ..., help="View Resource Identifier", autocompletion=complete_rid
1153
+ ),
1154
+ dataset_rids: list[str] = typer.Argument(
1155
+ ..., help="Dataset RIDs to remove as backing datasets"
1156
+ ),
1157
+ profile: Optional[str] = typer.Option(
1158
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1159
+ ),
1160
+ format: str = typer.Option(
1161
+ "table",
1162
+ "--format",
1163
+ "-f",
1164
+ help="Output format (table, json, csv)",
1165
+ autocompletion=complete_output_format,
1166
+ ),
1167
+ ):
1168
+ """Remove backing datasets from a view."""
1169
+ try:
1170
+ cache_rid(view_rid)
1171
+ service = DatasetService(profile=profile)
1172
+
1173
+ with SpinnerProgressTracker().track_spinner(
1174
+ f"Removing {len(dataset_rids)} backing datasets from view {view_rid}..."
1175
+ ):
1176
+ result = service.remove_backing_datasets(view_rid, dataset_rids)
1177
+
1178
+ formatter.print_success("Successfully removed backing datasets from view")
1179
+ formatter.print_info(f"View RID: {view_rid}")
1180
+ formatter.print_info(f"Removed datasets: {', '.join(dataset_rids)}")
1181
+
1182
+ if format == "json":
1183
+ formatter._format_json(result)
1184
+
1185
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1186
+ formatter.print_error(f"Authentication error: {e}")
1187
+ raise typer.Exit(1)
1188
+ except Exception as e:
1189
+ formatter.print_error(f"Failed to remove backing datasets: {e}")
1190
+ raise typer.Exit(1)
1191
+
1192
+
1193
+ @views_app.command("replace-datasets")
1194
+ def replace_backing_datasets(
1195
+ view_rid: str = typer.Argument(
1196
+ ..., help="View Resource Identifier", autocompletion=complete_rid
1197
+ ),
1198
+ dataset_rids: list[str] = typer.Argument(
1199
+ ..., help="Dataset RIDs to set as backing datasets"
1200
+ ),
1201
+ profile: Optional[str] = typer.Option(
1202
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1203
+ ),
1204
+ format: str = typer.Option(
1205
+ "table",
1206
+ "--format",
1207
+ "-f",
1208
+ help="Output format (table, json, csv)",
1209
+ autocompletion=complete_output_format,
1210
+ ),
1211
+ ):
1212
+ """Replace all backing datasets in a view."""
1213
+ try:
1214
+ cache_rid(view_rid)
1215
+ service = DatasetService(profile=profile)
1216
+
1217
+ with SpinnerProgressTracker().track_spinner(
1218
+ f"Replacing backing datasets in view {view_rid} with {len(dataset_rids)} new datasets..."
1219
+ ):
1220
+ result = service.replace_backing_datasets(view_rid, dataset_rids)
1221
+
1222
+ formatter.print_success("Successfully replaced backing datasets in view")
1223
+ formatter.print_info(f"View RID: {view_rid}")
1224
+ formatter.print_info(f"New datasets: {', '.join(dataset_rids)}")
1225
+
1226
+ if format == "json":
1227
+ formatter._format_json(result)
1228
+
1229
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1230
+ formatter.print_error(f"Authentication error: {e}")
1231
+ raise typer.Exit(1)
1232
+ except Exception as e:
1233
+ formatter.print_error(f"Failed to replace backing datasets: {e}")
1234
+ raise typer.Exit(1)
1235
+
1236
+
1237
+ @views_app.command("add-primary-key")
1238
+ def add_primary_key(
1239
+ view_rid: str = typer.Argument(
1240
+ ..., help="View Resource Identifier", autocompletion=complete_rid
1241
+ ),
1242
+ key_fields: list[str] = typer.Argument(
1243
+ ..., help="Field names to use as primary key"
1244
+ ),
1245
+ profile: Optional[str] = typer.Option(
1246
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1247
+ ),
1248
+ format: str = typer.Option(
1249
+ "table",
1250
+ "--format",
1251
+ "-f",
1252
+ help="Output format (table, json, csv)",
1253
+ autocompletion=complete_output_format,
1254
+ ),
1255
+ ):
1256
+ """Add a primary key to a view."""
1257
+ try:
1258
+ cache_rid(view_rid)
1259
+ service = DatasetService(profile=profile)
1260
+
1261
+ with SpinnerProgressTracker().track_spinner(
1262
+ f"Adding primary key to view {view_rid}..."
1263
+ ):
1264
+ result = service.add_primary_key(view_rid, key_fields)
1265
+
1266
+ formatter.print_success("Successfully added primary key to view")
1267
+ formatter.print_info(f"View RID: {view_rid}")
1268
+ formatter.print_info(f"Primary key fields: {', '.join(key_fields)}")
1269
+
1270
+ if format == "json":
1271
+ formatter._format_json(result)
1272
+
1273
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1274
+ formatter.print_error(f"Authentication error: {e}")
1275
+ raise typer.Exit(1)
1276
+ except Exception as e:
1277
+ formatter.print_error(f"Failed to add primary key: {e}")
1278
+ raise typer.Exit(1)
1279
+
1280
+
630
1281
  @views_app.command("create")
631
1282
  def create_view(
632
1283
  dataset_rid: str = typer.Argument(
@@ -671,11 +1322,101 @@ def create_view(
671
1322
  raise typer.Exit(1)
672
1323
 
673
1324
 
1325
+ # Schedules commands
1326
+ @schedules_app.command("list")
1327
+ def list_schedules(
1328
+ dataset_rid: str = typer.Argument(
1329
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
1330
+ ),
1331
+ profile: Optional[str] = typer.Option(
1332
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1333
+ ),
1334
+ format: str = typer.Option(
1335
+ "table",
1336
+ "--format",
1337
+ "-f",
1338
+ help="Output format (table, json, csv)",
1339
+ autocompletion=complete_output_format,
1340
+ ),
1341
+ output: Optional[str] = typer.Option(
1342
+ None, "--output", "-o", help="Output file path"
1343
+ ),
1344
+ ):
1345
+ """List schedules that target a specific dataset."""
1346
+ try:
1347
+ cache_rid(dataset_rid)
1348
+ service = DatasetService(profile=profile)
1349
+
1350
+ with SpinnerProgressTracker().track_spinner(
1351
+ f"Fetching schedules for dataset {dataset_rid}..."
1352
+ ):
1353
+ schedules = service.get_schedules(dataset_rid)
1354
+
1355
+ formatter.format_schedules(schedules, format, output)
1356
+
1357
+ if output:
1358
+ formatter.print_success(f"Schedules information saved to {output}")
1359
+
1360
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1361
+ formatter.print_error(f"Authentication error: {e}")
1362
+ raise typer.Exit(1)
1363
+ except Exception as e:
1364
+ formatter.print_error(f"Failed to get schedules: {e}")
1365
+ raise typer.Exit(1)
1366
+
1367
+
1368
+ # Jobs commands
1369
+ @jobs_app.command("list")
1370
+ def list_jobs(
1371
+ dataset_rid: str = typer.Argument(
1372
+ ..., help="Dataset Resource Identifier", autocompletion=complete_rid
1373
+ ),
1374
+ branch: str = typer.Option("master", "--branch", help="Dataset branch"),
1375
+ profile: Optional[str] = typer.Option(
1376
+ None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
1377
+ ),
1378
+ format: str = typer.Option(
1379
+ "table",
1380
+ "--format",
1381
+ "-f",
1382
+ help="Output format (table, json, csv)",
1383
+ autocompletion=complete_output_format,
1384
+ ),
1385
+ output: Optional[str] = typer.Option(
1386
+ None, "--output", "-o", help="Output file path"
1387
+ ),
1388
+ ):
1389
+ """List jobs for a specific dataset."""
1390
+ try:
1391
+ cache_rid(dataset_rid)
1392
+ service = DatasetService(profile=profile)
1393
+
1394
+ with SpinnerProgressTracker().track_spinner(
1395
+ f"Fetching jobs for dataset {dataset_rid} (branch: {branch})..."
1396
+ ):
1397
+ jobs = service.get_jobs(dataset_rid, branch)
1398
+
1399
+ formatter.format_jobs(jobs, format, output)
1400
+
1401
+ if output:
1402
+ formatter.print_success(f"Jobs information saved to {output}")
1403
+
1404
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
1405
+ formatter.print_error(f"Authentication error: {e}")
1406
+ raise typer.Exit(1)
1407
+ except Exception as e:
1408
+ formatter.print_error(f"Failed to get jobs: {e}")
1409
+ raise typer.Exit(1)
1410
+
1411
+
674
1412
  # Add subcommands to main app
675
1413
  app.add_typer(branches_app, name="branches")
676
1414
  app.add_typer(files_app, name="files")
677
1415
  app.add_typer(transactions_app, name="transactions")
678
1416
  app.add_typer(views_app, name="views")
1417
+ app.add_typer(schema_app, name="schema")
1418
+ app.add_typer(schedules_app, name="schedules")
1419
+ app.add_typer(jobs_app, name="jobs")
679
1420
 
680
1421
 
681
1422
  @app.callback()