pltr-cli 0.5.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,440 @@
1
+ """
2
+ Project 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.project import ProjectService
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_project(
28
+ name: str = typer.Argument(..., help="Project display name"),
29
+ space_rid: str = typer.Option(
30
+ ...,
31
+ "--space-rid",
32
+ "-s",
33
+ help="Space Resource Identifier",
34
+ autocompletion=complete_rid,
35
+ ),
36
+ description: Optional[str] = typer.Option(
37
+ None, "--description", "-d", help="Project description"
38
+ ),
39
+ organization_rids: Optional[List[str]] = typer.Option(
40
+ None,
41
+ "--organization-rid",
42
+ "-org",
43
+ help="Organization RIDs (can be specified multiple times)",
44
+ ),
45
+ profile: Optional[str] = typer.Option(
46
+ None, "--profile", help="Profile name", autocompletion=complete_profile
47
+ ),
48
+ format: str = typer.Option(
49
+ "table",
50
+ "--format",
51
+ "-f",
52
+ help="Output format (table, json, csv)",
53
+ autocompletion=complete_output_format,
54
+ ),
55
+ ):
56
+ """Create a new project in Foundry."""
57
+ try:
58
+ service = ProjectService(profile=profile)
59
+
60
+ with SpinnerProgressTracker().track_spinner(f"Creating project '{name}'..."):
61
+ project = service.create_project(
62
+ display_name=name,
63
+ space_rid=space_rid,
64
+ description=description,
65
+ organization_rids=organization_rids,
66
+ )
67
+
68
+ # Cache the RID for future completions
69
+ if project.get("rid"):
70
+ cache_rid(project["rid"])
71
+
72
+ formatter.print_success(f"Successfully created project '{name}'")
73
+ formatter.print_info(f"Project RID: {project.get('rid', 'unknown')}")
74
+
75
+ # Format output
76
+ if format == "json":
77
+ formatter.format_dict(project)
78
+ elif format == "csv":
79
+ formatter.format_list([project])
80
+ else:
81
+ _format_project_table(project)
82
+
83
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
84
+ formatter.print_error(f"Authentication error: {e}")
85
+ raise typer.Exit(1)
86
+ except Exception as e:
87
+ formatter.print_error(f"Failed to create project: {e}")
88
+ raise typer.Exit(1)
89
+
90
+
91
+ @app.command("get")
92
+ def get_project(
93
+ project_rid: str = typer.Argument(
94
+ ..., help="Project Resource Identifier", autocompletion=complete_rid
95
+ ),
96
+ profile: Optional[str] = typer.Option(
97
+ None, "--profile", help="Profile name", autocompletion=complete_profile
98
+ ),
99
+ format: str = typer.Option(
100
+ "table",
101
+ "--format",
102
+ "-f",
103
+ help="Output format (table, json, csv)",
104
+ autocompletion=complete_output_format,
105
+ ),
106
+ output: Optional[str] = typer.Option(
107
+ None, "--output", "-o", help="Output file path"
108
+ ),
109
+ ):
110
+ """Get detailed information about a specific project."""
111
+ try:
112
+ # Cache the RID for future completions
113
+ cache_rid(project_rid)
114
+
115
+ service = ProjectService(profile=profile)
116
+
117
+ with SpinnerProgressTracker().track_spinner(
118
+ f"Fetching project {project_rid}..."
119
+ ):
120
+ project = service.get_project(project_rid)
121
+
122
+ # Format output
123
+ if format == "json":
124
+ if output:
125
+ formatter.save_to_file(project, output, "json")
126
+ else:
127
+ formatter.format_dict(project)
128
+ elif format == "csv":
129
+ if output:
130
+ formatter.save_to_file([project], output, "csv")
131
+ else:
132
+ formatter.format_list([project])
133
+ else:
134
+ _format_project_table(project)
135
+
136
+ if output:
137
+ formatter.print_success(f"Project information saved to {output}")
138
+
139
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
140
+ formatter.print_error(f"Authentication error: {e}")
141
+ raise typer.Exit(1)
142
+ except Exception as e:
143
+ formatter.print_error(f"Failed to get project: {e}")
144
+ raise typer.Exit(1)
145
+
146
+
147
+ @app.command("list")
148
+ def list_projects(
149
+ space_rid: Optional[str] = typer.Option(
150
+ None,
151
+ "--space-rid",
152
+ "-s",
153
+ help="Space Resource Identifier to filter by",
154
+ autocompletion=complete_rid,
155
+ ),
156
+ profile: Optional[str] = typer.Option(
157
+ None, "--profile", help="Profile name", autocompletion=complete_profile
158
+ ),
159
+ format: str = typer.Option(
160
+ "table",
161
+ "--format",
162
+ "-f",
163
+ help="Output format (table, json, csv)",
164
+ autocompletion=complete_output_format,
165
+ ),
166
+ output: Optional[str] = typer.Option(
167
+ None, "--output", "-o", help="Output file path"
168
+ ),
169
+ page_size: Optional[int] = typer.Option(
170
+ None, "--page-size", help="Number of items per page"
171
+ ),
172
+ ):
173
+ """List projects, optionally filtered by space."""
174
+ try:
175
+ service = ProjectService(profile=profile)
176
+
177
+ filter_desc = f" in space {space_rid}" if space_rid else ""
178
+ with SpinnerProgressTracker().track_spinner(
179
+ f"Listing projects{filter_desc}..."
180
+ ):
181
+ projects = service.list_projects(space_rid=space_rid, page_size=page_size)
182
+
183
+ if not projects:
184
+ formatter.print_info("No projects found.")
185
+ return
186
+
187
+ # Format output
188
+ if format == "json":
189
+ if output:
190
+ formatter.save_to_file(projects, output, "json")
191
+ else:
192
+ formatter.format_list(projects)
193
+ elif format == "csv":
194
+ if output:
195
+ formatter.save_to_file(projects, output, "csv")
196
+ else:
197
+ formatter.format_list(projects)
198
+ else:
199
+ _format_projects_table(projects)
200
+
201
+ if output:
202
+ formatter.print_success(f"Projects list saved to {output}")
203
+
204
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
205
+ formatter.print_error(f"Authentication error: {e}")
206
+ raise typer.Exit(1)
207
+ except Exception as e:
208
+ formatter.print_error(f"Failed to list projects: {e}")
209
+ raise typer.Exit(1)
210
+
211
+
212
+ @app.command("update")
213
+ def update_project(
214
+ project_rid: str = typer.Argument(
215
+ ..., help="Project Resource Identifier", autocompletion=complete_rid
216
+ ),
217
+ name: Optional[str] = typer.Option(
218
+ None, "--name", "-n", help="New project display name"
219
+ ),
220
+ description: Optional[str] = typer.Option(
221
+ None, "--description", "-d", help="New project description"
222
+ ),
223
+ profile: Optional[str] = typer.Option(
224
+ None, "--profile", help="Profile name", autocompletion=complete_profile
225
+ ),
226
+ format: str = typer.Option(
227
+ "table",
228
+ "--format",
229
+ "-f",
230
+ help="Output format (table, json, csv)",
231
+ autocompletion=complete_output_format,
232
+ ),
233
+ ):
234
+ """Update project information."""
235
+ try:
236
+ if not name and not description:
237
+ formatter.print_error(
238
+ "At least one field (--name or --description) must be provided"
239
+ )
240
+ raise typer.Exit(1)
241
+
242
+ service = ProjectService(profile=profile)
243
+
244
+ with SpinnerProgressTracker().track_spinner(
245
+ f"Updating project {project_rid}..."
246
+ ):
247
+ project = service.update_project(
248
+ project_rid=project_rid,
249
+ display_name=name,
250
+ description=description,
251
+ )
252
+
253
+ formatter.print_success(f"Successfully updated project {project_rid}")
254
+
255
+ # Format output
256
+ if format == "json":
257
+ formatter.format_dict(project)
258
+ elif format == "csv":
259
+ formatter.format_list([project])
260
+ else:
261
+ _format_project_table(project)
262
+
263
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
264
+ formatter.print_error(f"Authentication error: {e}")
265
+ raise typer.Exit(1)
266
+ except Exception as e:
267
+ formatter.print_error(f"Failed to update project: {e}")
268
+ raise typer.Exit(1)
269
+
270
+
271
+ @app.command("delete")
272
+ def delete_project(
273
+ project_rid: str = typer.Argument(
274
+ ..., help="Project Resource Identifier", autocompletion=complete_rid
275
+ ),
276
+ profile: Optional[str] = typer.Option(
277
+ None, "--profile", help="Profile name", autocompletion=complete_profile
278
+ ),
279
+ confirm: bool = typer.Option(False, "--confirm", help="Skip confirmation prompt"),
280
+ ):
281
+ """Delete a project."""
282
+ try:
283
+ if not confirm:
284
+ confirm_delete = typer.confirm(
285
+ f"Are you sure you want to delete project {project_rid}?"
286
+ )
287
+ if not confirm_delete:
288
+ formatter.print_info("Project deletion cancelled.")
289
+ return
290
+
291
+ service = ProjectService(profile=profile)
292
+
293
+ with SpinnerProgressTracker().track_spinner(
294
+ f"Deleting project {project_rid}..."
295
+ ):
296
+ service.delete_project(project_rid)
297
+
298
+ formatter.print_success(f"Successfully deleted project {project_rid}")
299
+
300
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
301
+ formatter.print_error(f"Authentication error: {e}")
302
+ raise typer.Exit(1)
303
+ except Exception as e:
304
+ formatter.print_error(f"Failed to delete project: {e}")
305
+ raise typer.Exit(1)
306
+
307
+
308
+ @app.command("batch-get")
309
+ def get_projects_batch(
310
+ project_rids: List[str] = typer.Argument(
311
+ ..., help="Project Resource Identifiers (space-separated)"
312
+ ),
313
+ profile: Optional[str] = typer.Option(
314
+ None, "--profile", help="Profile name", autocompletion=complete_profile
315
+ ),
316
+ format: str = typer.Option(
317
+ "table",
318
+ "--format",
319
+ "-f",
320
+ help="Output format (table, json, csv)",
321
+ autocompletion=complete_output_format,
322
+ ),
323
+ output: Optional[str] = typer.Option(
324
+ None, "--output", "-o", help="Output file path"
325
+ ),
326
+ ):
327
+ """Get multiple projects in a single request (max 1000)."""
328
+ try:
329
+ service = ProjectService(profile=profile)
330
+
331
+ with SpinnerProgressTracker().track_spinner(
332
+ f"Fetching {len(project_rids)} projects..."
333
+ ):
334
+ projects = service.get_projects_batch(project_rids)
335
+
336
+ # Cache RIDs for future completions
337
+ for project in projects:
338
+ if project.get("rid"):
339
+ cache_rid(project["rid"])
340
+
341
+ # Format output
342
+ if format == "json":
343
+ if output:
344
+ formatter.save_to_file(projects, output, "json")
345
+ else:
346
+ formatter.format_list(projects)
347
+ elif format == "csv":
348
+ if output:
349
+ formatter.save_to_file(projects, output, "csv")
350
+ else:
351
+ formatter.format_list(projects)
352
+ else:
353
+ _format_projects_table(projects)
354
+
355
+ if output:
356
+ formatter.print_success(f"Projects information saved to {output}")
357
+
358
+ except (ProfileNotFoundError, MissingCredentialsError) as e:
359
+ formatter.print_error(f"Authentication error: {e}")
360
+ raise typer.Exit(1)
361
+ except ValueError as e:
362
+ formatter.print_error(f"Invalid request: {e}")
363
+ raise typer.Exit(1)
364
+ except Exception as e:
365
+ formatter.print_error(f"Failed to get projects batch: {e}")
366
+ raise typer.Exit(1)
367
+
368
+
369
+ def _format_project_table(project: dict):
370
+ """Format project information as a table."""
371
+ table = Table(
372
+ title="Project Information", show_header=True, header_style="bold cyan"
373
+ )
374
+ table.add_column("Property", style="cyan")
375
+ table.add_column("Value")
376
+
377
+ table.add_row("RID", project.get("rid", "N/A"))
378
+ table.add_row("Display Name", project.get("display_name", "N/A"))
379
+ table.add_row("Description", project.get("description", "N/A"))
380
+ table.add_row("Path", project.get("path", "N/A"))
381
+ table.add_row("Space RID", project.get("space_rid", "N/A"))
382
+ table.add_row("Created By", project.get("created_by", "N/A"))
383
+ table.add_row("Created Time", project.get("created_time", "N/A"))
384
+ table.add_row("Modified By", project.get("modified_by", "N/A"))
385
+ table.add_row("Modified Time", project.get("modified_time", "N/A"))
386
+ table.add_row("Trash Status", project.get("trash_status", "N/A"))
387
+
388
+ console.print(table)
389
+
390
+
391
+ def _format_projects_table(projects: List[dict]):
392
+ """Format multiple projects as a table."""
393
+ table = Table(title="Projects", show_header=True, header_style="bold cyan")
394
+ table.add_column("Display Name")
395
+ table.add_column("RID")
396
+ table.add_column("Space RID")
397
+ table.add_column("Created By")
398
+ table.add_column("Created Time")
399
+
400
+ for project in projects:
401
+ table.add_row(
402
+ project.get("display_name", "N/A"),
403
+ project.get("rid", "N/A"),
404
+ project.get("space_rid", "N/A"),
405
+ project.get("created_by", "N/A"),
406
+ project.get("created_time", "N/A"),
407
+ )
408
+
409
+ console.print(table)
410
+ console.print(f"\nTotal: {len(projects)} projects")
411
+
412
+
413
+ @app.callback()
414
+ def main():
415
+ """
416
+ Project operations using foundry-platform-sdk.
417
+
418
+ Manage projects in the Foundry filesystem. Create, retrieve, update, and delete
419
+ projects using Resource Identifiers (RIDs).
420
+
421
+ Examples:
422
+ # Create a project in a space
423
+ pltr project create "My Project" --space-rid ri.compass.main.space.xyz123
424
+
425
+ # List all projects
426
+ pltr project list
427
+
428
+ # List projects in a specific space
429
+ pltr project list --space-rid ri.compass.main.space.xyz123
430
+
431
+ # Get project information
432
+ pltr project get ri.compass.main.project.abc456
433
+
434
+ # Update project
435
+ pltr project update ri.compass.main.project.abc456 --name "Updated Name"
436
+
437
+ # Delete project
438
+ pltr project delete ri.compass.main.project.abc456
439
+ """
440
+ pass