pltr-cli 0.10.0__py3-none-any.whl → 0.12.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/__init__.py +1 -1
- pltr/cli.py +16 -0
- pltr/commands/admin.py +553 -9
- pltr/commands/aip_agents.py +333 -0
- pltr/commands/connectivity.py +309 -1
- pltr/commands/cp.py +103 -0
- pltr/commands/dataset.py +104 -4
- pltr/commands/mediasets.py +176 -0
- pltr/commands/ontology.py +137 -13
- pltr/commands/orchestration.py +167 -11
- pltr/commands/project.py +249 -0
- pltr/commands/resource.py +452 -0
- pltr/commands/sql.py +54 -7
- pltr/commands/third_party_applications.py +82 -0
- pltr/services/admin.py +318 -1
- pltr/services/aip_agents.py +147 -0
- pltr/services/base.py +104 -1
- pltr/services/connectivity.py +139 -0
- pltr/services/copy.py +391 -0
- pltr/services/dataset.py +80 -9
- pltr/services/mediasets.py +144 -9
- pltr/services/ontology.py +119 -1
- pltr/services/orchestration.py +133 -1
- pltr/services/project.py +136 -0
- pltr/services/resource.py +227 -0
- pltr/services/sql.py +44 -20
- pltr/services/third_party_applications.py +53 -0
- pltr/utils/formatting.py +195 -1
- pltr/utils/pagination.py +325 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/METADATA +5 -3
- pltr_cli-0.12.0.dist-info/RECORD +62 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/WHEEL +1 -1
- pltr_cli-0.10.0.dist-info/RECORD +0 -55
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/entry_points.txt +0 -0
- {pltr_cli-0.10.0.dist-info → pltr_cli-0.12.0.dist-info}/licenses/LICENSE +0 -0
pltr/commands/cp.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copy resources between Compass folders.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
from ..auth.base import MissingCredentialsError, ProfileNotFoundError
|
|
13
|
+
from ..services.copy import CopyService
|
|
14
|
+
from ..utils.completion import complete_profile, complete_rid
|
|
15
|
+
from ..utils.formatting import OutputFormatter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def cp_command(
|
|
19
|
+
source_rid: str = typer.Argument(
|
|
20
|
+
...,
|
|
21
|
+
help="Resource Identifier to copy (dataset or folder)",
|
|
22
|
+
autocompletion=complete_rid,
|
|
23
|
+
),
|
|
24
|
+
target_folder_rid: str = typer.Argument(
|
|
25
|
+
...,
|
|
26
|
+
help="Destination Compass folder RID",
|
|
27
|
+
autocompletion=complete_rid,
|
|
28
|
+
),
|
|
29
|
+
profile: Optional[str] = typer.Option(
|
|
30
|
+
None,
|
|
31
|
+
"--profile",
|
|
32
|
+
help="Authentication profile",
|
|
33
|
+
autocompletion=complete_profile,
|
|
34
|
+
),
|
|
35
|
+
recursive: bool = typer.Option(
|
|
36
|
+
False,
|
|
37
|
+
"--recursive",
|
|
38
|
+
"-r",
|
|
39
|
+
help="Copy folder contents recursively (required when SOURCE_RID is a folder)",
|
|
40
|
+
),
|
|
41
|
+
branch: str = typer.Option(
|
|
42
|
+
"master",
|
|
43
|
+
"--branch",
|
|
44
|
+
"-b",
|
|
45
|
+
help="Dataset branch to copy file-backed datasets from",
|
|
46
|
+
),
|
|
47
|
+
name_suffix: str = typer.Option(
|
|
48
|
+
"-copy",
|
|
49
|
+
"--name-suffix",
|
|
50
|
+
help="Suffix appended to cloned folder/dataset names",
|
|
51
|
+
),
|
|
52
|
+
copy_schema: bool = typer.Option(
|
|
53
|
+
True,
|
|
54
|
+
"--schema/--no-schema",
|
|
55
|
+
help="Copy dataset schemas when available",
|
|
56
|
+
),
|
|
57
|
+
dry_run: bool = typer.Option(
|
|
58
|
+
False,
|
|
59
|
+
"--dry-run",
|
|
60
|
+
help="Log actions without creating any resources",
|
|
61
|
+
),
|
|
62
|
+
fail_fast: bool = typer.Option(
|
|
63
|
+
False,
|
|
64
|
+
"--fail-fast",
|
|
65
|
+
help="Stop immediately on first error when copying folders recursively",
|
|
66
|
+
),
|
|
67
|
+
debug: bool = typer.Option(
|
|
68
|
+
False,
|
|
69
|
+
"--debug",
|
|
70
|
+
help="Print stack traces when errors occur",
|
|
71
|
+
),
|
|
72
|
+
):
|
|
73
|
+
"""Copy a resource identified by RID into another Compass folder."""
|
|
74
|
+
console = Console()
|
|
75
|
+
formatter = OutputFormatter(console)
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
service = CopyService(
|
|
79
|
+
profile=profile,
|
|
80
|
+
branch=branch,
|
|
81
|
+
name_suffix=name_suffix,
|
|
82
|
+
copy_schema=copy_schema,
|
|
83
|
+
dry_run=dry_run,
|
|
84
|
+
fail_fast=fail_fast,
|
|
85
|
+
debug=debug,
|
|
86
|
+
console=console,
|
|
87
|
+
)
|
|
88
|
+
summary = service.copy_resource(
|
|
89
|
+
source_rid, target_folder_rid, recursive=recursive
|
|
90
|
+
)
|
|
91
|
+
formatter.print_success("Copy operation completed")
|
|
92
|
+
formatter.print_info(
|
|
93
|
+
"Folders copied: {folders_copied} | Datasets copied: {datasets_copied} | "
|
|
94
|
+
"Skipped: {skipped} | Errors: {errors}".format(**summary)
|
|
95
|
+
)
|
|
96
|
+
except (ProfileNotFoundError, MissingCredentialsError) as exc:
|
|
97
|
+
formatter.print_error(f"Authentication error: {exc}")
|
|
98
|
+
raise typer.Exit(1)
|
|
99
|
+
except Exception as exc:
|
|
100
|
+
if debug:
|
|
101
|
+
raise
|
|
102
|
+
formatter.print_error(f"Failed to copy resource: {exc}")
|
|
103
|
+
raise typer.Exit(1)
|
pltr/commands/dataset.py
CHANGED
|
@@ -8,6 +8,7 @@ from rich.console import Console
|
|
|
8
8
|
|
|
9
9
|
from ..services.dataset import DatasetService
|
|
10
10
|
from ..utils.formatting import OutputFormatter
|
|
11
|
+
from ..utils.pagination import PaginationConfig
|
|
11
12
|
from ..utils.progress import SpinnerProgressTracker
|
|
12
13
|
from ..auth.base import ProfileNotFoundError, MissingCredentialsError
|
|
13
14
|
from ..utils.completion import (
|
|
@@ -73,6 +74,55 @@ def get_dataset(
|
|
|
73
74
|
raise typer.Exit(1)
|
|
74
75
|
|
|
75
76
|
|
|
77
|
+
@app.command("preview")
|
|
78
|
+
def preview_dataset(
|
|
79
|
+
dataset_rid: str = typer.Argument(
|
|
80
|
+
..., help="Dataset Resource Identifier", autocompletion=complete_rid
|
|
81
|
+
),
|
|
82
|
+
limit: int = typer.Option(
|
|
83
|
+
10, "--limit", "-n", help="Number of rows to display", min=1
|
|
84
|
+
),
|
|
85
|
+
profile: Optional[str] = typer.Option(
|
|
86
|
+
None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
|
|
87
|
+
),
|
|
88
|
+
format: str = typer.Option(
|
|
89
|
+
"table",
|
|
90
|
+
"--format",
|
|
91
|
+
"-f",
|
|
92
|
+
help="Output format (table, json, csv)",
|
|
93
|
+
autocompletion=complete_output_format,
|
|
94
|
+
),
|
|
95
|
+
output: Optional[str] = typer.Option(
|
|
96
|
+
None, "--output", "-o", help="Output file path"
|
|
97
|
+
),
|
|
98
|
+
):
|
|
99
|
+
"""Preview dataset contents."""
|
|
100
|
+
try:
|
|
101
|
+
cache_rid(dataset_rid)
|
|
102
|
+
service = DatasetService(profile=profile)
|
|
103
|
+
|
|
104
|
+
with SpinnerProgressTracker().track_spinner(
|
|
105
|
+
f"Fetching preview of {dataset_rid} (limit: {limit})..."
|
|
106
|
+
):
|
|
107
|
+
data = service.preview_data(dataset_rid, limit=limit)
|
|
108
|
+
|
|
109
|
+
if not data:
|
|
110
|
+
formatter.print_warning("Dataset is empty or has no readable data")
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
formatter.format_output(data, format, output)
|
|
114
|
+
|
|
115
|
+
if output:
|
|
116
|
+
formatter.print_success(f"Preview saved to {output}")
|
|
117
|
+
|
|
118
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
119
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
120
|
+
raise typer.Exit(1)
|
|
121
|
+
except Exception as e:
|
|
122
|
+
formatter.print_error(f"Failed to preview dataset: {e}")
|
|
123
|
+
raise typer.Exit(1)
|
|
124
|
+
|
|
125
|
+
|
|
76
126
|
# Schema commands
|
|
77
127
|
@schema_app.command("get")
|
|
78
128
|
def get_schema(
|
|
@@ -566,21 +616,71 @@ def list_files(
|
|
|
566
616
|
output: Optional[str] = typer.Option(
|
|
567
617
|
None, "--output", "-o", help="Output file path"
|
|
568
618
|
),
|
|
619
|
+
page_size: Optional[int] = typer.Option(
|
|
620
|
+
None, "--page-size", help="Number of files per page (default: from settings)"
|
|
621
|
+
),
|
|
622
|
+
max_pages: Optional[int] = typer.Option(
|
|
623
|
+
1, "--max-pages", help="Maximum number of pages to fetch (default: 1)"
|
|
624
|
+
),
|
|
625
|
+
page_token: Optional[str] = typer.Option(
|
|
626
|
+
None, "--page-token", help="Page token to resume from previous response"
|
|
627
|
+
),
|
|
628
|
+
all: bool = typer.Option(
|
|
629
|
+
False, "--all", help="Fetch all available pages (overrides --max-pages)"
|
|
630
|
+
),
|
|
569
631
|
):
|
|
570
|
-
"""
|
|
632
|
+
"""
|
|
633
|
+
List files in a dataset with pagination support.
|
|
634
|
+
|
|
635
|
+
By default, fetches only the first page of results. Use --all to fetch all files,
|
|
636
|
+
or --max-pages to control how many pages to fetch. Critical for large datasets.
|
|
637
|
+
|
|
638
|
+
Examples:
|
|
639
|
+
# List first page of files (default)
|
|
640
|
+
pltr dataset files list DATASET_RID
|
|
641
|
+
|
|
642
|
+
# List all files
|
|
643
|
+
pltr dataset files list DATASET_RID --all
|
|
644
|
+
|
|
645
|
+
# List first 3 pages
|
|
646
|
+
pltr dataset files list DATASET_RID --max-pages 3
|
|
647
|
+
"""
|
|
571
648
|
try:
|
|
572
649
|
cache_rid(dataset_rid)
|
|
573
650
|
service = DatasetService(profile=profile)
|
|
574
651
|
|
|
652
|
+
# Create pagination config
|
|
653
|
+
config = PaginationConfig(
|
|
654
|
+
page_size=page_size,
|
|
655
|
+
max_pages=max_pages,
|
|
656
|
+
page_token=page_token,
|
|
657
|
+
fetch_all=all,
|
|
658
|
+
)
|
|
659
|
+
|
|
575
660
|
with SpinnerProgressTracker().track_spinner(
|
|
576
661
|
f"Fetching files from {dataset_rid} (branch: {branch})..."
|
|
577
662
|
):
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
formatter.format_files(files, format, output)
|
|
663
|
+
result = service.list_files_paginated(dataset_rid, branch, config)
|
|
581
664
|
|
|
665
|
+
# Format and display paginated results
|
|
582
666
|
if output:
|
|
667
|
+
formatter.format_paginated_output(
|
|
668
|
+
result,
|
|
669
|
+
format,
|
|
670
|
+
output,
|
|
671
|
+
formatter_fn=lambda data, fmt, out: formatter.format_files(
|
|
672
|
+
data, fmt, out
|
|
673
|
+
),
|
|
674
|
+
)
|
|
583
675
|
formatter.print_success(f"Files information saved to {output}")
|
|
676
|
+
else:
|
|
677
|
+
formatter.format_paginated_output(
|
|
678
|
+
result,
|
|
679
|
+
format,
|
|
680
|
+
formatter_fn=lambda data, fmt, out: formatter.format_files(
|
|
681
|
+
data, fmt, out
|
|
682
|
+
),
|
|
683
|
+
)
|
|
584
684
|
|
|
585
685
|
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
586
686
|
formatter.print_error(f"Authentication error: {e}")
|
pltr/commands/mediasets.py
CHANGED
|
@@ -405,6 +405,180 @@ def get_media_reference(
|
|
|
405
405
|
raise typer.Exit(1)
|
|
406
406
|
|
|
407
407
|
|
|
408
|
+
@app.command("thumbnail-calculate")
|
|
409
|
+
def thumbnail_calculate(
|
|
410
|
+
media_set_rid: str = typer.Argument(
|
|
411
|
+
..., help="Media Set Resource Identifier", autocompletion=complete_rid
|
|
412
|
+
),
|
|
413
|
+
media_item_rid: str = typer.Argument(
|
|
414
|
+
..., help="Media Item Resource Identifier", autocompletion=complete_rid
|
|
415
|
+
),
|
|
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
|
+
preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
|
|
430
|
+
):
|
|
431
|
+
"""Initiate thumbnail generation for an image."""
|
|
432
|
+
try:
|
|
433
|
+
cache_rid(media_set_rid)
|
|
434
|
+
cache_rid(media_item_rid)
|
|
435
|
+
service = MediaSetsService(profile=profile)
|
|
436
|
+
|
|
437
|
+
with SpinnerProgressTracker().track_spinner(
|
|
438
|
+
f"Initiating thumbnail calculation for {media_item_rid}..."
|
|
439
|
+
):
|
|
440
|
+
status = service.calculate_thumbnail(
|
|
441
|
+
media_set_rid, media_item_rid, preview=preview
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
formatter.format_thumbnail_status(status, format, output)
|
|
445
|
+
|
|
446
|
+
if output:
|
|
447
|
+
formatter.print_success(f"Thumbnail status saved to {output}")
|
|
448
|
+
else:
|
|
449
|
+
formatter.print_info(
|
|
450
|
+
"Use 'thumbnail-retrieve' to download once calculation completes"
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
454
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
455
|
+
raise typer.Exit(1)
|
|
456
|
+
except Exception as e:
|
|
457
|
+
formatter.print_error(f"Failed to calculate thumbnail: {e}")
|
|
458
|
+
raise typer.Exit(1)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
@app.command("thumbnail-retrieve")
|
|
462
|
+
def thumbnail_retrieve(
|
|
463
|
+
media_set_rid: str = typer.Argument(
|
|
464
|
+
..., help="Media Set Resource Identifier", autocompletion=complete_rid
|
|
465
|
+
),
|
|
466
|
+
media_item_rid: str = typer.Argument(
|
|
467
|
+
..., help="Media Item Resource Identifier", autocompletion=complete_rid
|
|
468
|
+
),
|
|
469
|
+
output_path: str = typer.Argument(
|
|
470
|
+
..., help="Local path where thumbnail should be saved"
|
|
471
|
+
),
|
|
472
|
+
profile: Optional[str] = typer.Option(
|
|
473
|
+
None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
|
|
474
|
+
),
|
|
475
|
+
preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
|
|
476
|
+
overwrite: bool = typer.Option(
|
|
477
|
+
False, "--overwrite", help="Overwrite existing file"
|
|
478
|
+
),
|
|
479
|
+
):
|
|
480
|
+
"""Download a calculated thumbnail from a media set (200px wide webp)."""
|
|
481
|
+
try:
|
|
482
|
+
# Check if output file already exists
|
|
483
|
+
output_path_obj = Path(output_path)
|
|
484
|
+
if output_path_obj.exists() and not overwrite:
|
|
485
|
+
formatter.print_error(f"File already exists: {output_path}")
|
|
486
|
+
formatter.print_info("Use --overwrite to replace existing file")
|
|
487
|
+
raise typer.Exit(1)
|
|
488
|
+
|
|
489
|
+
cache_rid(media_set_rid)
|
|
490
|
+
cache_rid(media_item_rid)
|
|
491
|
+
service = MediaSetsService(profile=profile)
|
|
492
|
+
|
|
493
|
+
with SpinnerProgressTracker().track_spinner("Downloading thumbnail..."):
|
|
494
|
+
result = service.retrieve_thumbnail(
|
|
495
|
+
media_set_rid,
|
|
496
|
+
media_item_rid,
|
|
497
|
+
output_path,
|
|
498
|
+
preview=preview,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
formatter.print_success("Successfully downloaded thumbnail")
|
|
502
|
+
formatter.print_info(f"Saved to: {result['output_path']}")
|
|
503
|
+
formatter.print_info(f"File size: {result['file_size']} bytes")
|
|
504
|
+
formatter.print_info(f"Format: {result['format']}")
|
|
505
|
+
|
|
506
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
507
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
508
|
+
raise typer.Exit(1)
|
|
509
|
+
except Exception as e:
|
|
510
|
+
formatter.print_error(f"Failed to retrieve thumbnail: {e}")
|
|
511
|
+
raise typer.Exit(1)
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
@app.command("upload-temp")
|
|
515
|
+
def upload_temp(
|
|
516
|
+
file_path: str = typer.Argument(..., help="Local path to the file to upload"),
|
|
517
|
+
profile: Optional[str] = typer.Option(
|
|
518
|
+
None, "--profile", "-p", help="Profile name", autocompletion=complete_profile
|
|
519
|
+
),
|
|
520
|
+
filename: Optional[str] = typer.Option(
|
|
521
|
+
None, "--filename", help="Override filename for the upload"
|
|
522
|
+
),
|
|
523
|
+
attribution: Optional[str] = typer.Option(
|
|
524
|
+
None, "--attribution", help="Attribution string for the media"
|
|
525
|
+
),
|
|
526
|
+
format: str = typer.Option(
|
|
527
|
+
"table",
|
|
528
|
+
"--format",
|
|
529
|
+
"-f",
|
|
530
|
+
help="Output format (table, json, csv)",
|
|
531
|
+
autocompletion=complete_output_format,
|
|
532
|
+
),
|
|
533
|
+
output: Optional[str] = typer.Option(
|
|
534
|
+
None, "--output", "-o", help="Output file path"
|
|
535
|
+
),
|
|
536
|
+
preview: bool = typer.Option(False, "--preview", help="Enable preview mode"),
|
|
537
|
+
):
|
|
538
|
+
"""Upload temporary media (auto-deleted after 1 hour if not persisted)."""
|
|
539
|
+
try:
|
|
540
|
+
# Validate file exists
|
|
541
|
+
file_path_obj = Path(file_path)
|
|
542
|
+
if not file_path_obj.exists():
|
|
543
|
+
formatter.print_error(f"File not found: {file_path}")
|
|
544
|
+
raise typer.Exit(1)
|
|
545
|
+
|
|
546
|
+
service = MediaSetsService(profile=profile)
|
|
547
|
+
|
|
548
|
+
file_size = file_path_obj.stat().st_size
|
|
549
|
+
formatter.print_info(f"Uploading {file_path} ({file_size} bytes)")
|
|
550
|
+
|
|
551
|
+
with SpinnerProgressTracker().track_spinner(
|
|
552
|
+
f"Uploading {file_path_obj.name}..."
|
|
553
|
+
):
|
|
554
|
+
reference = service.upload_temp_media(
|
|
555
|
+
file_path,
|
|
556
|
+
filename=filename,
|
|
557
|
+
attribution=attribution,
|
|
558
|
+
preview=preview,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
formatter.print_success("Successfully uploaded temporary media")
|
|
562
|
+
formatter.format_media_reference(reference, format, output)
|
|
563
|
+
|
|
564
|
+
if output:
|
|
565
|
+
formatter.print_success(f"Media reference saved to {output}")
|
|
566
|
+
else:
|
|
567
|
+
formatter.print_warning(
|
|
568
|
+
"This is a temporary upload. It will be deleted after 1 hour if not persisted."
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
572
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
573
|
+
raise typer.Exit(1)
|
|
574
|
+
except FileNotFoundError as e:
|
|
575
|
+
formatter.print_error(str(e))
|
|
576
|
+
raise typer.Exit(1)
|
|
577
|
+
except Exception as e:
|
|
578
|
+
formatter.print_error(f"Failed to upload temporary media: {e}")
|
|
579
|
+
raise typer.Exit(1)
|
|
580
|
+
|
|
581
|
+
|
|
408
582
|
@app.callback()
|
|
409
583
|
def main():
|
|
410
584
|
"""
|
|
@@ -415,6 +589,8 @@ def main():
|
|
|
415
589
|
- Create, commit, and abort upload transactions
|
|
416
590
|
- Upload media files to media sets
|
|
417
591
|
- Download media items from media sets
|
|
592
|
+
- Generate and retrieve image thumbnails
|
|
593
|
+
- Upload temporary media
|
|
418
594
|
|
|
419
595
|
All operations require Resource Identifiers (RIDs) like:
|
|
420
596
|
ri.mediasets.main.media-set.12345678-1234-1234-1234-123456789abc
|
pltr/commands/ontology.py
CHANGED
|
@@ -15,6 +15,7 @@ from ..services.ontology import (
|
|
|
15
15
|
QueryService,
|
|
16
16
|
)
|
|
17
17
|
from ..utils.formatting import OutputFormatter
|
|
18
|
+
from ..utils.pagination import PaginationConfig
|
|
18
19
|
from ..utils.progress import SpinnerProgressTracker
|
|
19
20
|
from ..auth.base import ProfileNotFoundError, MissingCredentialsError
|
|
20
21
|
|
|
@@ -176,36 +177,66 @@ def list_objects(
|
|
|
176
177
|
None, "--output", "-o", help="Output file path"
|
|
177
178
|
),
|
|
178
179
|
page_size: Optional[int] = typer.Option(
|
|
179
|
-
None, "--page-size", help="Number of
|
|
180
|
+
None, "--page-size", help="Number of objects per page (default: from settings)"
|
|
181
|
+
),
|
|
182
|
+
max_pages: Optional[int] = typer.Option(
|
|
183
|
+
1, "--max-pages", help="Maximum number of pages to fetch (default: 1)"
|
|
184
|
+
),
|
|
185
|
+
page_token: Optional[str] = typer.Option(
|
|
186
|
+
None, "--page-token", help="Page token to resume from previous response"
|
|
187
|
+
),
|
|
188
|
+
all: bool = typer.Option(
|
|
189
|
+
False, "--all", help="Fetch all available pages (overrides --max-pages)"
|
|
180
190
|
),
|
|
181
191
|
properties: Optional[str] = typer.Option(
|
|
182
192
|
None, "--properties", help="Comma-separated list of properties to include"
|
|
183
193
|
),
|
|
184
194
|
):
|
|
185
|
-
"""
|
|
195
|
+
"""
|
|
196
|
+
List objects of a specific type with pagination support.
|
|
197
|
+
|
|
198
|
+
By default, fetches only the first page of results. Use --all to fetch all objects,
|
|
199
|
+
or --max-pages to control how many pages to fetch.
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
# List first page of objects (default)
|
|
203
|
+
pltr ontology object-list ONTOLOGY_RID ObjectType
|
|
204
|
+
|
|
205
|
+
# List all objects
|
|
206
|
+
pltr ontology object-list ONTOLOGY_RID ObjectType --all
|
|
207
|
+
|
|
208
|
+
# List first 3 pages
|
|
209
|
+
pltr ontology object-list ONTOLOGY_RID ObjectType --max-pages 3
|
|
210
|
+
|
|
211
|
+
# Resume from a specific page
|
|
212
|
+
pltr ontology object-list ONTOLOGY_RID ObjectType --page-token abc123
|
|
213
|
+
"""
|
|
186
214
|
try:
|
|
187
215
|
service = OntologyObjectService(profile=profile)
|
|
188
216
|
|
|
189
217
|
prop_list = properties.split(",") if properties else None
|
|
190
218
|
|
|
219
|
+
# Create pagination config
|
|
220
|
+
config = PaginationConfig(
|
|
221
|
+
page_size=page_size,
|
|
222
|
+
max_pages=max_pages,
|
|
223
|
+
page_token=page_token,
|
|
224
|
+
fetch_all=all,
|
|
225
|
+
)
|
|
226
|
+
|
|
191
227
|
with SpinnerProgressTracker().track_spinner(
|
|
192
228
|
f"Fetching {object_type} objects..."
|
|
193
229
|
):
|
|
194
|
-
|
|
195
|
-
ontology_rid, object_type,
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
if format == "table" and objects:
|
|
199
|
-
# Use first object's keys as columns
|
|
200
|
-
columns = list(objects[0].keys()) if objects else []
|
|
201
|
-
formatter.format_table(
|
|
202
|
-
objects, columns=columns, format=format, output=output
|
|
230
|
+
result = service.list_objects_paginated(
|
|
231
|
+
ontology_rid, object_type, config, properties=prop_list
|
|
203
232
|
)
|
|
204
|
-
else:
|
|
205
|
-
formatter.format_list(objects, format=format, output=output)
|
|
206
233
|
|
|
234
|
+
# Format and display paginated results
|
|
207
235
|
if output:
|
|
236
|
+
formatter.format_paginated_output(result, format, output)
|
|
208
237
|
formatter.print_success(f"Objects saved to {output}")
|
|
238
|
+
else:
|
|
239
|
+
formatter.format_paginated_output(result, format)
|
|
209
240
|
|
|
210
241
|
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
211
242
|
formatter.print_error(f"Authentication error: {e}")
|
|
@@ -366,6 +397,99 @@ def list_linked_objects(
|
|
|
366
397
|
raise typer.Exit(1)
|
|
367
398
|
|
|
368
399
|
|
|
400
|
+
@app.command("object-count")
|
|
401
|
+
def count_objects(
|
|
402
|
+
ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
|
|
403
|
+
object_type: str = typer.Argument(..., help="Object type API name"),
|
|
404
|
+
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
|
|
405
|
+
format: str = typer.Option(
|
|
406
|
+
"table", "--format", "-f", help="Output format (table, json, csv)"
|
|
407
|
+
),
|
|
408
|
+
output: Optional[str] = typer.Option(
|
|
409
|
+
None, "--output", "-o", help="Output file path"
|
|
410
|
+
),
|
|
411
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
|
|
412
|
+
):
|
|
413
|
+
"""Count objects of a specific type."""
|
|
414
|
+
try:
|
|
415
|
+
service = OntologyObjectService(profile=profile)
|
|
416
|
+
|
|
417
|
+
with SpinnerProgressTracker().track_spinner(
|
|
418
|
+
f"Counting {object_type} objects..."
|
|
419
|
+
):
|
|
420
|
+
result = service.count_objects(ontology_rid, object_type, branch=branch)
|
|
421
|
+
|
|
422
|
+
formatter.format_dict(result, format=format, output=output)
|
|
423
|
+
|
|
424
|
+
if output:
|
|
425
|
+
formatter.print_success(f"Count result saved to {output}")
|
|
426
|
+
|
|
427
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
428
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
429
|
+
raise typer.Exit(1)
|
|
430
|
+
except Exception as e:
|
|
431
|
+
formatter.print_error(f"Failed to count objects: {e}")
|
|
432
|
+
raise typer.Exit(1)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
@app.command("object-search")
|
|
436
|
+
def search_objects(
|
|
437
|
+
ontology_rid: str = typer.Argument(..., help="Ontology Resource Identifier"),
|
|
438
|
+
object_type: str = typer.Argument(..., help="Object type API name"),
|
|
439
|
+
query: str = typer.Option(..., "--query", "-q", help="Search query string"),
|
|
440
|
+
profile: Optional[str] = typer.Option(None, "--profile", "-p", help="Profile name"),
|
|
441
|
+
format: str = typer.Option(
|
|
442
|
+
"table", "--format", "-f", help="Output format (table, json, csv)"
|
|
443
|
+
),
|
|
444
|
+
output: Optional[str] = typer.Option(
|
|
445
|
+
None, "--output", "-o", help="Output file path"
|
|
446
|
+
),
|
|
447
|
+
page_size: Optional[int] = typer.Option(
|
|
448
|
+
None, "--page-size", help="Number of results per page"
|
|
449
|
+
),
|
|
450
|
+
properties: Optional[str] = typer.Option(
|
|
451
|
+
None, "--properties", help="Comma-separated list of properties to include"
|
|
452
|
+
),
|
|
453
|
+
branch: Optional[str] = typer.Option(None, "--branch", "-b", help="Branch name"),
|
|
454
|
+
):
|
|
455
|
+
"""Search objects by query."""
|
|
456
|
+
try:
|
|
457
|
+
service = OntologyObjectService(profile=profile)
|
|
458
|
+
|
|
459
|
+
prop_list = properties.split(",") if properties else None
|
|
460
|
+
|
|
461
|
+
with SpinnerProgressTracker().track_spinner(
|
|
462
|
+
f"Searching {object_type} objects..."
|
|
463
|
+
):
|
|
464
|
+
objects = service.search_objects(
|
|
465
|
+
ontology_rid,
|
|
466
|
+
object_type,
|
|
467
|
+
query,
|
|
468
|
+
page_size=page_size,
|
|
469
|
+
properties=prop_list,
|
|
470
|
+
branch=branch,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if format == "table" and objects:
|
|
474
|
+
# Use first object's keys as columns
|
|
475
|
+
columns = list(objects[0].keys()) if objects else []
|
|
476
|
+
formatter.format_table(
|
|
477
|
+
objects, columns=columns, format=format, output=output
|
|
478
|
+
)
|
|
479
|
+
else:
|
|
480
|
+
formatter.format_list(objects, format=format, output=output)
|
|
481
|
+
|
|
482
|
+
if output:
|
|
483
|
+
formatter.print_success(f"Search results saved to {output}")
|
|
484
|
+
|
|
485
|
+
except (ProfileNotFoundError, MissingCredentialsError) as e:
|
|
486
|
+
formatter.print_error(f"Authentication error: {e}")
|
|
487
|
+
raise typer.Exit(1)
|
|
488
|
+
except Exception as e:
|
|
489
|
+
formatter.print_error(f"Failed to search objects: {e}")
|
|
490
|
+
raise typer.Exit(1)
|
|
491
|
+
|
|
492
|
+
|
|
369
493
|
# Action commands
|
|
370
494
|
@app.command("action-apply")
|
|
371
495
|
def apply_action(
|