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.
pltr/commands/space.py ADDED
@@ -0,0 +1,662 @@
1
+ """
2
+ Space 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.space import SpaceService
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("create")
27
+ def create_space(
28
+ name: str = typer.Argument(..., help="Space display name"),
29
+ organization_rid: str = typer.Option(
30
+ ...,
31
+ "--organization-rid",
32
+ "-org",
33
+ help="Organization Resource Identifier",
34
+ autocompletion=complete_rid,
35
+ ),
36
+ description: Optional[str] = typer.Option(
37
+ None, "--description", "-d", help="Space description"
38
+ ),
39
+ profile: Optional[str] = typer.Option(
40
+ None, "--profile", help="Profile name", autocompletion=complete_profile
41
+ ),
42
+ format: str = typer.Option(
43
+ "table",
44
+ "--format",
45
+ "-f",
46
+ help="Output format (table, json, csv)",
47
+ autocompletion=complete_output_format,
48
+ ),
49
+ ):
50
+ """Create a new space in Foundry."""
51
+ try:
52
+ service = SpaceService(profile=profile)
53
+
54
+ with SpinnerProgressTracker().track_spinner(f"Creating space '{name}'..."):
55
+ space = service.create_space(
56
+ display_name=name,
57
+ organization_rid=organization_rid,
58
+ description=description,
59
+ )
60
+
61
+ # Cache the RID for future completions
62
+ if space.get("rid"):
63
+ cache_rid(space["rid"])
64
+
65
+ formatter.print_success(f"Successfully created space '{name}'")
66
+ formatter.print_info(f"Space RID: {space.get('rid', 'unknown')}")
67
+ formatter.print_info(
68
+ f"Root Folder RID: {space.get('root_folder_rid', 'unknown')}"
69
+ )
70
+
71
+ # Format output
72
+ if format == "json":
73
+ formatter.format_dict(space)
74
+ elif format == "csv":
75
+ formatter.format_list([space])
76
+ else:
77
+ _format_space_table(space)
78
+
79
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
80
+ formatter.print_error(f"Authentication error: {e}")
81
+ raise typer.Exit(1)
82
+ except Exception as e:
83
+ formatter.print_error(f"Failed to create space: {e}")
84
+ raise typer.Exit(1)
85
+
86
+
87
+ @app.command("get")
88
+ def get_space(
89
+ space_rid: str = typer.Argument(
90
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
91
+ ),
92
+ profile: Optional[str] = typer.Option(
93
+ None, "--profile", help="Profile name", autocompletion=complete_profile
94
+ ),
95
+ format: str = typer.Option(
96
+ "table",
97
+ "--format",
98
+ "-f",
99
+ help="Output format (table, json, csv)",
100
+ autocompletion=complete_output_format,
101
+ ),
102
+ output: Optional[str] = typer.Option(
103
+ None, "--output", "-o", help="Output file path"
104
+ ),
105
+ ):
106
+ """Get detailed information about a specific space."""
107
+ try:
108
+ # Cache the RID for future completions
109
+ cache_rid(space_rid)
110
+
111
+ service = SpaceService(profile=profile)
112
+
113
+ with SpinnerProgressTracker().track_spinner(f"Fetching space {space_rid}..."):
114
+ space = service.get_space(space_rid)
115
+
116
+ # Format output
117
+ if format == "json":
118
+ if output:
119
+ formatter.save_to_file(space, output, "json")
120
+ else:
121
+ formatter.format_dict(space)
122
+ elif format == "csv":
123
+ if output:
124
+ formatter.save_to_file([space], output, "csv")
125
+ else:
126
+ formatter.format_list([space])
127
+ else:
128
+ _format_space_table(space)
129
+
130
+ if output:
131
+ formatter.print_success(f"Space information saved to {output}")
132
+
133
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
134
+ formatter.print_error(f"Authentication error: {e}")
135
+ raise typer.Exit(1)
136
+ except Exception as e:
137
+ formatter.print_error(f"Failed to get space: {e}")
138
+ raise typer.Exit(1)
139
+
140
+
141
+ @app.command("list")
142
+ def list_spaces(
143
+ organization_rid: Optional[str] = typer.Option(
144
+ None,
145
+ "--organization-rid",
146
+ "-org",
147
+ help="Organization Resource Identifier to filter by",
148
+ autocompletion=complete_rid,
149
+ ),
150
+ profile: Optional[str] = typer.Option(
151
+ None, "--profile", help="Profile name", autocompletion=complete_profile
152
+ ),
153
+ format: str = typer.Option(
154
+ "table",
155
+ "--format",
156
+ "-f",
157
+ help="Output format (table, json, csv)",
158
+ autocompletion=complete_output_format,
159
+ ),
160
+ output: Optional[str] = typer.Option(
161
+ None, "--output", "-o", help="Output file path"
162
+ ),
163
+ page_size: Optional[int] = typer.Option(
164
+ None, "--page-size", help="Number of items per page"
165
+ ),
166
+ ):
167
+ """List spaces, optionally filtered by organization."""
168
+ try:
169
+ service = SpaceService(profile=profile)
170
+
171
+ filter_desc = f" in organization {organization_rid}" if organization_rid else ""
172
+ with SpinnerProgressTracker().track_spinner(f"Listing spaces{filter_desc}..."):
173
+ spaces = service.list_spaces(
174
+ organization_rid=organization_rid, page_size=page_size
175
+ )
176
+
177
+ if not spaces:
178
+ formatter.print_info("No spaces found.")
179
+ return
180
+
181
+ # Format output
182
+ if format == "json":
183
+ if output:
184
+ formatter.save_to_file(spaces, output, "json")
185
+ else:
186
+ formatter.format_list(spaces)
187
+ elif format == "csv":
188
+ if output:
189
+ formatter.save_to_file(spaces, output, "csv")
190
+ else:
191
+ formatter.format_list(spaces)
192
+ else:
193
+ _format_spaces_table(spaces)
194
+
195
+ if output:
196
+ formatter.print_success(f"Spaces list saved to {output}")
197
+
198
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
199
+ formatter.print_error(f"Authentication error: {e}")
200
+ raise typer.Exit(1)
201
+ except Exception as e:
202
+ formatter.print_error(f"Failed to list spaces: {e}")
203
+ raise typer.Exit(1)
204
+
205
+
206
+ @app.command("update")
207
+ def update_space(
208
+ space_rid: str = typer.Argument(
209
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
210
+ ),
211
+ name: Optional[str] = typer.Option(
212
+ None, "--name", "-n", help="New space display name"
213
+ ),
214
+ description: Optional[str] = typer.Option(
215
+ None, "--description", "-d", help="New space description"
216
+ ),
217
+ profile: Optional[str] = typer.Option(
218
+ None, "--profile", help="Profile name", autocompletion=complete_profile
219
+ ),
220
+ format: str = typer.Option(
221
+ "table",
222
+ "--format",
223
+ "-f",
224
+ help="Output format (table, json, csv)",
225
+ autocompletion=complete_output_format,
226
+ ),
227
+ ):
228
+ """Update space information."""
229
+ try:
230
+ if not name and not description:
231
+ formatter.print_error(
232
+ "At least one field (--name or --description) must be provided"
233
+ )
234
+ raise typer.Exit(1)
235
+
236
+ service = SpaceService(profile=profile)
237
+
238
+ with SpinnerProgressTracker().track_spinner(f"Updating space {space_rid}..."):
239
+ space = service.update_space(
240
+ space_rid=space_rid,
241
+ display_name=name,
242
+ description=description,
243
+ )
244
+
245
+ formatter.print_success(f"Successfully updated space {space_rid}")
246
+
247
+ # Format output
248
+ if format == "json":
249
+ formatter.format_dict(space)
250
+ elif format == "csv":
251
+ formatter.format_list([space])
252
+ else:
253
+ _format_space_table(space)
254
+
255
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
256
+ formatter.print_error(f"Authentication error: {e}")
257
+ raise typer.Exit(1)
258
+ except Exception as e:
259
+ formatter.print_error(f"Failed to update space: {e}")
260
+ raise typer.Exit(1)
261
+
262
+
263
+ @app.command("delete")
264
+ def delete_space(
265
+ space_rid: str = typer.Argument(
266
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
267
+ ),
268
+ profile: Optional[str] = typer.Option(
269
+ None, "--profile", help="Profile name", autocompletion=complete_profile
270
+ ),
271
+ confirm: bool = typer.Option(False, "--confirm", help="Skip confirmation prompt"),
272
+ ):
273
+ """Delete a space."""
274
+ try:
275
+ if not confirm:
276
+ confirm_delete = typer.confirm(
277
+ f"Are you sure you want to delete space {space_rid}?"
278
+ )
279
+ if not confirm_delete:
280
+ formatter.print_info("Space deletion cancelled.")
281
+ return
282
+
283
+ service = SpaceService(profile=profile)
284
+
285
+ with SpinnerProgressTracker().track_spinner(f"Deleting space {space_rid}..."):
286
+ service.delete_space(space_rid)
287
+
288
+ formatter.print_success(f"Successfully deleted space {space_rid}")
289
+
290
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
291
+ formatter.print_error(f"Authentication error: {e}")
292
+ raise typer.Exit(1)
293
+ except Exception as e:
294
+ formatter.print_error(f"Failed to delete space: {e}")
295
+ raise typer.Exit(1)
296
+
297
+
298
+ @app.command("batch-get")
299
+ def get_spaces_batch(
300
+ space_rids: List[str] = typer.Argument(
301
+ ..., help="Space Resource Identifiers (space-separated)"
302
+ ),
303
+ profile: Optional[str] = typer.Option(
304
+ None, "--profile", help="Profile name", autocompletion=complete_profile
305
+ ),
306
+ format: str = typer.Option(
307
+ "table",
308
+ "--format",
309
+ "-f",
310
+ help="Output format (table, json, csv)",
311
+ autocompletion=complete_output_format,
312
+ ),
313
+ output: Optional[str] = typer.Option(
314
+ None, "--output", "-o", help="Output file path"
315
+ ),
316
+ ):
317
+ """Get multiple spaces in a single request (max 1000)."""
318
+ try:
319
+ service = SpaceService(profile=profile)
320
+
321
+ with SpinnerProgressTracker().track_spinner(
322
+ f"Fetching {len(space_rids)} spaces..."
323
+ ):
324
+ spaces = service.get_spaces_batch(space_rids)
325
+
326
+ # Cache RIDs for future completions
327
+ for space in spaces:
328
+ if space.get("rid"):
329
+ cache_rid(space["rid"])
330
+
331
+ # Format output
332
+ if format == "json":
333
+ if output:
334
+ formatter.save_to_file(spaces, output, "json")
335
+ else:
336
+ formatter.format_list(spaces)
337
+ elif format == "csv":
338
+ if output:
339
+ formatter.save_to_file(spaces, output, "csv")
340
+ else:
341
+ formatter.format_list(spaces)
342
+ else:
343
+ _format_spaces_table(spaces)
344
+
345
+ if output:
346
+ formatter.print_success(f"Spaces information saved to {output}")
347
+
348
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
349
+ formatter.print_error(f"Authentication error: {e}")
350
+ raise typer.Exit(1)
351
+ except ValueError as e:
352
+ formatter.print_error(f"Invalid request: {e}")
353
+ raise typer.Exit(1)
354
+ except Exception as e:
355
+ formatter.print_error(f"Failed to get spaces batch: {e}")
356
+ raise typer.Exit(1)
357
+
358
+
359
+ @app.command("list-members")
360
+ def list_space_members(
361
+ space_rid: str = typer.Argument(
362
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
363
+ ),
364
+ principal_type: Optional[str] = typer.Option(
365
+ None,
366
+ "--principal-type",
367
+ "-t",
368
+ help="Filter by principal type (User or Group)",
369
+ ),
370
+ profile: Optional[str] = typer.Option(
371
+ None, "--profile", help="Profile name", autocompletion=complete_profile
372
+ ),
373
+ format: str = typer.Option(
374
+ "table",
375
+ "--format",
376
+ "-f",
377
+ help="Output format (table, json, csv)",
378
+ autocompletion=complete_output_format,
379
+ ),
380
+ output: Optional[str] = typer.Option(
381
+ None, "--output", "-o", help="Output file path"
382
+ ),
383
+ page_size: Optional[int] = typer.Option(
384
+ None, "--page-size", help="Number of items per page"
385
+ ),
386
+ ):
387
+ """Get all members (users/groups) of a space."""
388
+ try:
389
+ service = SpaceService(profile=profile)
390
+
391
+ filter_desc = f" ({principal_type}s only)" if principal_type else ""
392
+ with SpinnerProgressTracker().track_spinner(
393
+ f"Listing members of space {space_rid}{filter_desc}..."
394
+ ):
395
+ members = service.get_space_members(
396
+ space_rid=space_rid,
397
+ principal_type=principal_type.title() if principal_type else None,
398
+ page_size=page_size,
399
+ )
400
+
401
+ if not members:
402
+ formatter.print_info(f"No members found in space {space_rid}.")
403
+ return
404
+
405
+ # Format output
406
+ if format == "json":
407
+ if output:
408
+ formatter.save_to_file(members, output, "json")
409
+ else:
410
+ formatter.format_list(members)
411
+ elif format == "csv":
412
+ if output:
413
+ formatter.save_to_file(members, output, "csv")
414
+ else:
415
+ formatter.format_list(members)
416
+ else:
417
+ _format_space_members_table(members)
418
+
419
+ if output:
420
+ formatter.print_success(f"Space members saved to {output}")
421
+
422
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
423
+ formatter.print_error(f"Authentication error: {e}")
424
+ raise typer.Exit(1)
425
+ except Exception as e:
426
+ formatter.print_error(f"Failed to list space members: {e}")
427
+ raise typer.Exit(1)
428
+
429
+
430
+ @app.command("add-member")
431
+ def add_space_member(
432
+ space_rid: str = typer.Argument(
433
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
434
+ ),
435
+ principal_id: str = typer.Option(
436
+ ..., "--principal-id", "-p", help="Principal (user/group) identifier"
437
+ ),
438
+ principal_type: str = typer.Option(
439
+ ...,
440
+ "--principal-type",
441
+ "-t",
442
+ help="Principal type (User or Group)",
443
+ ),
444
+ role_name: str = typer.Option(..., "--role", "-r", help="Role name to grant"),
445
+ profile: Optional[str] = typer.Option(
446
+ None, "--profile", help="Profile name", autocompletion=complete_profile
447
+ ),
448
+ format: str = typer.Option(
449
+ "table",
450
+ "--format",
451
+ "-f",
452
+ help="Output format (table, json, csv)",
453
+ autocompletion=complete_output_format,
454
+ ),
455
+ ):
456
+ """Add a member to a space with a specific role."""
457
+ try:
458
+ service = SpaceService(profile=profile)
459
+
460
+ with SpinnerProgressTracker().track_spinner(
461
+ f"Adding {principal_type} '{principal_id}' to space {space_rid} with role '{role_name}'..."
462
+ ):
463
+ member = service.add_space_member(
464
+ space_rid=space_rid,
465
+ principal_id=principal_id,
466
+ principal_type=principal_type.title(),
467
+ role_name=role_name,
468
+ )
469
+
470
+ formatter.print_success(
471
+ f"Successfully added {principal_type} '{principal_id}' to space with role '{role_name}'"
472
+ )
473
+
474
+ # Format output
475
+ if format == "json":
476
+ formatter.format_dict(member)
477
+ elif format == "csv":
478
+ formatter.format_list([member])
479
+ else:
480
+ _format_space_member_table(member)
481
+
482
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
483
+ formatter.print_error(f"Authentication error: {e}")
484
+ raise typer.Exit(1)
485
+ except Exception as e:
486
+ formatter.print_error(f"Failed to add space member: {e}")
487
+ raise typer.Exit(1)
488
+
489
+
490
+ @app.command("remove-member")
491
+ def remove_space_member(
492
+ space_rid: str = typer.Argument(
493
+ ..., help="Space Resource Identifier", autocompletion=complete_rid
494
+ ),
495
+ principal_id: str = typer.Option(
496
+ ..., "--principal-id", "-p", help="Principal (user/group) identifier"
497
+ ),
498
+ principal_type: str = typer.Option(
499
+ ...,
500
+ "--principal-type",
501
+ "-t",
502
+ help="Principal type (User or Group)",
503
+ ),
504
+ profile: Optional[str] = typer.Option(
505
+ None, "--profile", help="Profile name", autocompletion=complete_profile
506
+ ),
507
+ confirm: bool = typer.Option(False, "--confirm", help="Skip confirmation prompt"),
508
+ ):
509
+ """Remove a member from a space."""
510
+ try:
511
+ if not confirm:
512
+ confirm_remove = typer.confirm(
513
+ f"Are you sure you want to remove {principal_type} '{principal_id}' from space {space_rid}?"
514
+ )
515
+ if not confirm_remove:
516
+ formatter.print_info("Member removal cancelled.")
517
+ return
518
+
519
+ service = SpaceService(profile=profile)
520
+
521
+ with SpinnerProgressTracker().track_spinner(
522
+ f"Removing {principal_type} '{principal_id}' from space {space_rid}..."
523
+ ):
524
+ service.remove_space_member(
525
+ space_rid=space_rid,
526
+ principal_id=principal_id,
527
+ principal_type=principal_type.title(),
528
+ )
529
+
530
+ formatter.print_success(
531
+ f"Successfully removed {principal_type} '{principal_id}' from space"
532
+ )
533
+
534
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
535
+ formatter.print_error(f"Authentication error: {e}")
536
+ raise typer.Exit(1)
537
+ except Exception as e:
538
+ formatter.print_error(f"Failed to remove space member: {e}")
539
+ raise typer.Exit(1)
540
+
541
+
542
+ def _format_space_table(space: dict):
543
+ """Format space information as a table."""
544
+ table = Table(title="Space Information", show_header=True, header_style="bold cyan")
545
+ table.add_column("Property", style="cyan")
546
+ table.add_column("Value")
547
+
548
+ table.add_row("RID", space.get("rid", "N/A"))
549
+ table.add_row("Display Name", space.get("display_name", "N/A"))
550
+ table.add_row("Description", space.get("description", "N/A"))
551
+ table.add_row("Organization RID", space.get("organization_rid", "N/A"))
552
+ table.add_row("Root Folder RID", space.get("root_folder_rid", "N/A"))
553
+ table.add_row("Created By", space.get("created_by", "N/A"))
554
+ table.add_row("Created Time", space.get("created_time", "N/A"))
555
+ table.add_row("Modified By", space.get("modified_by", "N/A"))
556
+ table.add_row("Modified Time", space.get("modified_time", "N/A"))
557
+ table.add_row("Trash Status", space.get("trash_status", "N/A"))
558
+
559
+ console.print(table)
560
+
561
+
562
+ def _format_spaces_table(spaces: List[dict]):
563
+ """Format multiple spaces as a table."""
564
+ table = Table(title="Spaces", show_header=True, header_style="bold cyan")
565
+ table.add_column("Display Name")
566
+ table.add_column("RID")
567
+ table.add_column("Organization RID")
568
+ table.add_column("Created By")
569
+ table.add_column("Created Time")
570
+
571
+ for space in spaces:
572
+ table.add_row(
573
+ space.get("display_name", "N/A"),
574
+ space.get("rid", "N/A"),
575
+ space.get("organization_rid", "N/A"),
576
+ space.get("created_by", "N/A"),
577
+ space.get("created_time", "N/A"),
578
+ )
579
+
580
+ console.print(table)
581
+ console.print(f"\nTotal: {len(spaces)} spaces")
582
+
583
+
584
+ def _format_space_member_table(member: dict):
585
+ """Format space member information as a table."""
586
+ table = Table(
587
+ title="Space Member Information", show_header=True, header_style="bold cyan"
588
+ )
589
+ table.add_column("Property", style="cyan")
590
+ table.add_column("Value")
591
+
592
+ table.add_row("Space RID", member.get("space_rid", "N/A"))
593
+ table.add_row("Principal ID", member.get("principal_id", "N/A"))
594
+ table.add_row("Principal Type", member.get("principal_type", "N/A"))
595
+ table.add_row("Role Name", member.get("role_name", "N/A"))
596
+ table.add_row("Added By", member.get("added_by", "N/A"))
597
+ table.add_row("Added Time", member.get("added_time", "N/A"))
598
+
599
+ console.print(table)
600
+
601
+
602
+ def _format_space_members_table(members: List[dict]):
603
+ """Format multiple space members as a table."""
604
+ table = Table(title="Space Members", show_header=True, header_style="bold cyan")
605
+ table.add_column("Principal Type")
606
+ table.add_column("Principal ID")
607
+ table.add_column("Role Name")
608
+ table.add_column("Added By")
609
+ table.add_column("Added Time")
610
+
611
+ for member in members:
612
+ table.add_row(
613
+ member.get("principal_type", "N/A"),
614
+ member.get("principal_id", "N/A"),
615
+ member.get("role_name", "N/A"),
616
+ member.get("added_by", "N/A"),
617
+ member.get("added_time", "N/A"),
618
+ )
619
+
620
+ console.print(table)
621
+ console.print(f"\nTotal: {len(members)} members")
622
+
623
+
624
+ @app.callback()
625
+ def main():
626
+ """
627
+ Space operations using foundry-platform-sdk.
628
+
629
+ Manage spaces in the Foundry filesystem. Create, retrieve, update, and delete
630
+ spaces, and manage space membership using Resource Identifiers (RIDs).
631
+
632
+ Examples:
633
+ # Create a space in an organization
634
+ pltr space create "My Space" --organization-rid ri.compass.main.organization.xyz123
635
+
636
+ # List all spaces
637
+ pltr space list
638
+
639
+ # List spaces in a specific organization
640
+ pltr space list --organization-rid ri.compass.main.organization.xyz123
641
+
642
+ # Get space information
643
+ pltr space get ri.compass.main.space.abc456
644
+
645
+ # Update space
646
+ pltr space update ri.compass.main.space.abc456 --name "Updated Name"
647
+
648
+ # List space members
649
+ pltr space list-members ri.compass.main.space.abc456
650
+
651
+ # Add a user to a space
652
+ pltr space add-member ri.compass.main.space.abc456 \\
653
+ --principal-id user123 --principal-type User --role viewer
654
+
655
+ # Remove a user from a space
656
+ pltr space remove-member ri.compass.main.space.abc456 \\
657
+ --principal-id user123 --principal-type User
658
+
659
+ # Delete space
660
+ pltr space delete ri.compass.main.space.abc456
661
+ """
662
+ pass