pltr-cli 0.4.0__py3-none-any.whl → 0.5.1__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.
@@ -0,0 +1,499 @@
1
+ """
2
+ Resource management commands for Foundry filesystem.
3
+ """
4
+
5
+ import typer
6
+ from typing import Optional, List
7
+ from rich.console import Console
8
+ from rich.table import Table
9
+
10
+ from ..services.resource import ResourceService
11
+ from ..utils.formatting import OutputFormatter
12
+ from ..utils.progress import SpinnerProgressTracker
13
+ from ..auth.base import ProfileNotFoundError, MissingCredentialsError
14
+ from ..utils.completion import (
15
+ complete_rid,
16
+ complete_profile,
17
+ complete_output_format,
18
+ cache_rid,
19
+ )
20
+
21
+ app = typer.Typer()
22
+ console = Console()
23
+ formatter = OutputFormatter(console)
24
+
25
+
26
+ @app.command("get")
27
+ def get_resource(
28
+ resource_rid: str = typer.Argument(
29
+ ..., help="Resource Identifier", autocompletion=complete_rid
30
+ ),
31
+ profile: Optional[str] = typer.Option(
32
+ None, "--profile", help="Profile name", autocompletion=complete_profile
33
+ ),
34
+ format: str = typer.Option(
35
+ "table",
36
+ "--format",
37
+ "-f",
38
+ help="Output format (table, json, csv)",
39
+ autocompletion=complete_output_format,
40
+ ),
41
+ output: Optional[str] = typer.Option(
42
+ None, "--output", "-o", help="Output file path"
43
+ ),
44
+ ):
45
+ """Get detailed information about a specific resource."""
46
+ try:
47
+ # Cache the RID for future completions
48
+ cache_rid(resource_rid)
49
+
50
+ service = ResourceService(profile=profile)
51
+
52
+ with SpinnerProgressTracker().track_spinner(
53
+ f"Fetching resource {resource_rid}..."
54
+ ):
55
+ resource = service.get_resource(resource_rid)
56
+
57
+ # Format output
58
+ if format == "json":
59
+ if output:
60
+ formatter.save_to_file(resource, output, "json")
61
+ else:
62
+ formatter.format_dict(resource)
63
+ elif format == "csv":
64
+ if output:
65
+ formatter.save_to_file([resource], output, "csv")
66
+ else:
67
+ formatter.format_list([resource])
68
+ else:
69
+ _format_resource_table(resource)
70
+
71
+ if output:
72
+ formatter.print_success(f"Resource information saved to {output}")
73
+
74
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
75
+ formatter.print_error(f"Authentication error: {e}")
76
+ raise typer.Exit(1)
77
+ except Exception as e:
78
+ formatter.print_error(f"Failed to get resource: {e}")
79
+ raise typer.Exit(1)
80
+
81
+
82
+ @app.command("list")
83
+ def list_resources(
84
+ folder_rid: Optional[str] = typer.Option(
85
+ None,
86
+ "--folder-rid",
87
+ "-f",
88
+ help="Folder Resource Identifier to filter by",
89
+ autocompletion=complete_rid,
90
+ ),
91
+ resource_type: Optional[str] = typer.Option(
92
+ None, "--type", "-t", help="Resource type to filter by (e.g., dataset, folder)"
93
+ ),
94
+ profile: Optional[str] = typer.Option(
95
+ None, "--profile", help="Profile name", autocompletion=complete_profile
96
+ ),
97
+ format: str = typer.Option(
98
+ "table",
99
+ "--format",
100
+ "-o",
101
+ help="Output format (table, json, csv)",
102
+ autocompletion=complete_output_format,
103
+ ),
104
+ output: Optional[str] = typer.Option(None, "--output", help="Output file path"),
105
+ page_size: Optional[int] = typer.Option(
106
+ None, "--page-size", help="Number of items per page"
107
+ ),
108
+ ):
109
+ """List resources, optionally filtered by folder and type."""
110
+ try:
111
+ service = ResourceService(profile=profile)
112
+
113
+ filter_parts = []
114
+ if folder_rid:
115
+ filter_parts.append(f"in folder {folder_rid}")
116
+ if resource_type:
117
+ filter_parts.append(f"of type {resource_type}")
118
+
119
+ filter_desc = f" ({', '.join(filter_parts)})" if filter_parts else ""
120
+
121
+ with SpinnerProgressTracker().track_spinner(
122
+ f"Listing resources{filter_desc}..."
123
+ ):
124
+ resources = service.list_resources(
125
+ folder_rid=folder_rid,
126
+ resource_type=resource_type,
127
+ page_size=page_size,
128
+ )
129
+
130
+ if not resources:
131
+ formatter.print_info("No resources found.")
132
+ return
133
+
134
+ # Format output
135
+ if format == "json":
136
+ if output:
137
+ formatter.save_to_file(resources, output, "json")
138
+ else:
139
+ formatter.format_list(resources)
140
+ elif format == "csv":
141
+ if output:
142
+ formatter.save_to_file(resources, output, "csv")
143
+ else:
144
+ formatter.format_list(resources)
145
+ else:
146
+ _format_resources_table(resources)
147
+
148
+ if output:
149
+ formatter.print_success(f"Resources list saved to {output}")
150
+
151
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
152
+ formatter.print_error(f"Authentication error: {e}")
153
+ raise typer.Exit(1)
154
+ except Exception as e:
155
+ formatter.print_error(f"Failed to list resources: {e}")
156
+ raise typer.Exit(1)
157
+
158
+
159
+ @app.command("search")
160
+ def search_resources(
161
+ query: str = typer.Argument(..., help="Search query string"),
162
+ resource_type: Optional[str] = typer.Option(
163
+ None, "--type", "-t", help="Resource type to filter by (e.g., dataset, folder)"
164
+ ),
165
+ folder_rid: Optional[str] = typer.Option(
166
+ None,
167
+ "--folder-rid",
168
+ "-f",
169
+ help="Folder to search within",
170
+ autocompletion=complete_rid,
171
+ ),
172
+ profile: Optional[str] = typer.Option(
173
+ None, "--profile", help="Profile name", autocompletion=complete_profile
174
+ ),
175
+ format: str = typer.Option(
176
+ "table",
177
+ "--format",
178
+ "-o",
179
+ help="Output format (table, json, csv)",
180
+ autocompletion=complete_output_format,
181
+ ),
182
+ output: Optional[str] = typer.Option(None, "--output", help="Output file path"),
183
+ page_size: Optional[int] = typer.Option(
184
+ None, "--page-size", help="Number of items per page"
185
+ ),
186
+ ):
187
+ """Search for resources by query string."""
188
+ try:
189
+ service = ResourceService(profile=profile)
190
+
191
+ filter_parts = []
192
+ if resource_type:
193
+ filter_parts.append(f"type={resource_type}")
194
+ if folder_rid:
195
+ filter_parts.append(f"folder={folder_rid}")
196
+
197
+ filter_desc = f" ({', '.join(filter_parts)})" if filter_parts else ""
198
+
199
+ with SpinnerProgressTracker().track_spinner(
200
+ f"Searching resources for '{query}'{filter_desc}..."
201
+ ):
202
+ resources = service.search_resources(
203
+ query=query,
204
+ resource_type=resource_type,
205
+ folder_rid=folder_rid,
206
+ page_size=page_size,
207
+ )
208
+
209
+ if not resources:
210
+ formatter.print_info(f"No resources found matching '{query}'.")
211
+ return
212
+
213
+ # Format output
214
+ if format == "json":
215
+ if output:
216
+ formatter.save_to_file(resources, output, "json")
217
+ else:
218
+ formatter.format_list(resources)
219
+ elif format == "csv":
220
+ if output:
221
+ formatter.save_to_file(resources, output, "csv")
222
+ else:
223
+ formatter.format_list(resources)
224
+ else:
225
+ _format_resources_table(resources)
226
+
227
+ if output:
228
+ formatter.print_success(f"Search results saved to {output}")
229
+
230
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
231
+ formatter.print_error(f"Authentication error: {e}")
232
+ raise typer.Exit(1)
233
+ except Exception as e:
234
+ formatter.print_error(f"Failed to search resources: {e}")
235
+ raise typer.Exit(1)
236
+
237
+
238
+ @app.command("batch-get")
239
+ def get_resources_batch(
240
+ resource_rids: List[str] = typer.Argument(
241
+ ..., help="Resource Identifiers (space-separated)"
242
+ ),
243
+ profile: Optional[str] = typer.Option(
244
+ None, "--profile", help="Profile name", autocompletion=complete_profile
245
+ ),
246
+ format: str = typer.Option(
247
+ "table",
248
+ "--format",
249
+ "-f",
250
+ help="Output format (table, json, csv)",
251
+ autocompletion=complete_output_format,
252
+ ),
253
+ output: Optional[str] = typer.Option(
254
+ None, "--output", "-o", help="Output file path"
255
+ ),
256
+ ):
257
+ """Get multiple resources in a single request (max 1000)."""
258
+ try:
259
+ service = ResourceService(profile=profile)
260
+
261
+ with SpinnerProgressTracker().track_spinner(
262
+ f"Fetching {len(resource_rids)} resources..."
263
+ ):
264
+ resources = service.get_resources_batch(resource_rids)
265
+
266
+ # Cache RIDs for future completions
267
+ for resource in resources:
268
+ if resource.get("rid"):
269
+ cache_rid(resource["rid"])
270
+
271
+ # Format output
272
+ if format == "json":
273
+ if output:
274
+ formatter.save_to_file(resources, output, "json")
275
+ else:
276
+ formatter.format_list(resources)
277
+ elif format == "csv":
278
+ if output:
279
+ formatter.save_to_file(resources, output, "csv")
280
+ else:
281
+ formatter.format_list(resources)
282
+ else:
283
+ _format_resources_table(resources)
284
+
285
+ if output:
286
+ formatter.print_success(f"Resources information saved to {output}")
287
+
288
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
289
+ formatter.print_error(f"Authentication error: {e}")
290
+ raise typer.Exit(1)
291
+ except ValueError as e:
292
+ formatter.print_error(f"Invalid request: {e}")
293
+ raise typer.Exit(1)
294
+ except Exception as e:
295
+ formatter.print_error(f"Failed to get resources batch: {e}")
296
+ raise typer.Exit(1)
297
+
298
+
299
+ @app.command("get-metadata")
300
+ def get_resource_metadata(
301
+ resource_rid: str = typer.Argument(
302
+ ..., help="Resource Identifier", autocompletion=complete_rid
303
+ ),
304
+ profile: Optional[str] = typer.Option(
305
+ None, "--profile", help="Profile name", autocompletion=complete_profile
306
+ ),
307
+ format: str = typer.Option(
308
+ "table",
309
+ "--format",
310
+ "-f",
311
+ help="Output format (table, json, csv)",
312
+ autocompletion=complete_output_format,
313
+ ),
314
+ output: Optional[str] = typer.Option(
315
+ None, "--output", "-o", help="Output file path"
316
+ ),
317
+ ):
318
+ """Get metadata for a specific resource."""
319
+ try:
320
+ service = ResourceService(profile=profile)
321
+
322
+ with SpinnerProgressTracker().track_spinner(
323
+ f"Fetching metadata for {resource_rid}..."
324
+ ):
325
+ metadata = service.get_resource_metadata(resource_rid)
326
+
327
+ # Format output
328
+ if format == "json":
329
+ if output:
330
+ formatter.save_to_file(metadata, output, "json")
331
+ else:
332
+ formatter.format_dict(metadata)
333
+ elif format == "csv":
334
+ # Convert metadata dict to list for CSV output
335
+ metadata_list = [{"key": k, "value": v} for k, v in metadata.items()]
336
+ if output:
337
+ formatter.save_to_file(metadata_list, output, "csv")
338
+ else:
339
+ formatter.format_list(metadata_list)
340
+ else:
341
+ _format_metadata_table(metadata)
342
+
343
+ if output:
344
+ formatter.print_success(f"Resource metadata saved to {output}")
345
+
346
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
347
+ formatter.print_error(f"Authentication error: {e}")
348
+ raise typer.Exit(1)
349
+ except Exception as e:
350
+ formatter.print_error(f"Failed to get resource metadata: {e}")
351
+ raise typer.Exit(1)
352
+
353
+
354
+ @app.command("move")
355
+ def move_resource(
356
+ resource_rid: str = typer.Argument(
357
+ ..., help="Resource Identifier", autocompletion=complete_rid
358
+ ),
359
+ target_folder_rid: str = typer.Option(
360
+ ...,
361
+ "--target-folder",
362
+ "-t",
363
+ help="Target folder Resource Identifier",
364
+ autocompletion=complete_rid,
365
+ ),
366
+ profile: Optional[str] = typer.Option(
367
+ None, "--profile", help="Profile name", autocompletion=complete_profile
368
+ ),
369
+ format: str = typer.Option(
370
+ "table",
371
+ "--format",
372
+ "-f",
373
+ help="Output format (table, json, csv)",
374
+ autocompletion=complete_output_format,
375
+ ),
376
+ ):
377
+ """Move a resource to a different folder."""
378
+ try:
379
+ service = ResourceService(profile=profile)
380
+
381
+ with SpinnerProgressTracker().track_spinner(
382
+ f"Moving resource {resource_rid} to {target_folder_rid}..."
383
+ ):
384
+ resource = service.move_resource(resource_rid, target_folder_rid)
385
+
386
+ formatter.print_success(f"Successfully moved resource to {target_folder_rid}")
387
+
388
+ # Format output
389
+ if format == "json":
390
+ formatter.format_dict(resource)
391
+ elif format == "csv":
392
+ formatter.format_list([resource])
393
+ else:
394
+ _format_resource_table(resource)
395
+
396
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
397
+ formatter.print_error(f"Authentication error: {e}")
398
+ raise typer.Exit(1)
399
+ except Exception as e:
400
+ formatter.print_error(f"Failed to move resource: {e}")
401
+ raise typer.Exit(1)
402
+
403
+
404
+ def _format_resource_table(resource: dict):
405
+ """Format resource information as a table."""
406
+ table = Table(
407
+ title="Resource Information", show_header=True, header_style="bold cyan"
408
+ )
409
+ table.add_column("Property", style="cyan")
410
+ table.add_column("Value")
411
+
412
+ table.add_row("RID", resource.get("rid", "N/A"))
413
+ table.add_row("Display Name", resource.get("display_name", "N/A"))
414
+ table.add_row("Name", resource.get("name", "N/A"))
415
+ table.add_row("Description", resource.get("description", "N/A"))
416
+ table.add_row("Type", resource.get("type", "N/A"))
417
+ table.add_row("Path", resource.get("path", "N/A"))
418
+ table.add_row("Folder RID", resource.get("folder_rid", "N/A"))
419
+ table.add_row("Created By", resource.get("created_by", "N/A"))
420
+ table.add_row("Created Time", resource.get("created_time", "N/A"))
421
+ table.add_row("Modified By", resource.get("modified_by", "N/A"))
422
+ table.add_row("Modified Time", resource.get("modified_time", "N/A"))
423
+ table.add_row("Size (bytes)", resource.get("size_bytes", "N/A"))
424
+ table.add_row("Trash Status", resource.get("trash_status", "N/A"))
425
+
426
+ console.print(table)
427
+
428
+
429
+ def _format_resources_table(resources: List[dict]):
430
+ """Format multiple resources as a table."""
431
+ table = Table(title="Resources", show_header=True, header_style="bold cyan")
432
+ table.add_column("Type")
433
+ table.add_column("Display Name")
434
+ table.add_column("RID")
435
+ table.add_column("Folder RID")
436
+ table.add_column("Created By")
437
+
438
+ for resource in resources:
439
+ table.add_row(
440
+ resource.get("type", "N/A"),
441
+ resource.get("display_name") or resource.get("name", "N/A"),
442
+ resource.get("rid", "N/A"),
443
+ resource.get("folder_rid", "N/A"),
444
+ resource.get("created_by", "N/A"),
445
+ )
446
+
447
+ console.print(table)
448
+ console.print(f"\nTotal: {len(resources)} resources")
449
+
450
+
451
+ def _format_metadata_table(metadata: dict):
452
+ """Format metadata as a table."""
453
+ table = Table(title="Resource Metadata", show_header=True, header_style="bold cyan")
454
+ table.add_column("Key", style="cyan")
455
+ table.add_column("Value")
456
+
457
+ for key, value in metadata.items():
458
+ # Convert complex values to strings
459
+ value_str = str(value) if value is not None else "N/A"
460
+ table.add_row(key, value_str)
461
+
462
+ console.print(table)
463
+
464
+
465
+ @app.callback()
466
+ def main():
467
+ """
468
+ Resource operations using foundry-platform-sdk.
469
+
470
+ Manage resources in the Foundry filesystem. Get resource information,
471
+ search resources, manage metadata, and perform operations using Resource
472
+ Identifiers (RIDs).
473
+
474
+ Examples:
475
+ # Get resource information
476
+ pltr resource get ri.compass.main.dataset.xyz123
477
+
478
+ # List all resources
479
+ pltr resource list
480
+
481
+ # List resources in a specific folder
482
+ pltr resource list --folder-rid ri.compass.main.folder.abc456
483
+
484
+ # List only datasets
485
+ pltr resource list --type dataset
486
+
487
+ # Search for resources
488
+ pltr resource search "sales data"
489
+
490
+ # Search for datasets containing "user"
491
+ pltr resource search "user" --type dataset
492
+
493
+ # Get resource metadata
494
+ pltr resource get-metadata ri.compass.main.dataset.xyz123
495
+
496
+ # Move resource to different folder
497
+ pltr resource move ri.compass.main.dataset.xyz123 --target-folder ri.compass.main.folder.new456
498
+ """
499
+ pass