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/services/dataset.py
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Dataset service wrapper for Foundry SDK.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
import csv
|
|
8
8
|
|
|
9
|
+
from ..config.settings import Settings
|
|
10
|
+
from ..utils.pagination import PaginationConfig, PaginationResult
|
|
9
11
|
from .base import BaseService
|
|
10
12
|
|
|
11
13
|
|
|
@@ -312,6 +314,29 @@ class DatasetService(BaseService):
|
|
|
312
314
|
except Exception as e:
|
|
313
315
|
raise RuntimeError(f"Failed to read dataset {dataset_rid}: {e}")
|
|
314
316
|
|
|
317
|
+
def preview_data(
|
|
318
|
+
self,
|
|
319
|
+
dataset_rid: str,
|
|
320
|
+
limit: int = 10,
|
|
321
|
+
) -> List[Dict[str, Any]]:
|
|
322
|
+
"""
|
|
323
|
+
Preview dataset contents as a list of records.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
dataset_rid: Dataset Resource Identifier
|
|
327
|
+
limit: Maximum number of rows to return
|
|
328
|
+
|
|
329
|
+
Returns:
|
|
330
|
+
List of dictionaries representing rows
|
|
331
|
+
"""
|
|
332
|
+
try:
|
|
333
|
+
# Use read_table with pandas format for easy conversion
|
|
334
|
+
df = self.read_table(dataset_rid, format="pandas")
|
|
335
|
+
# Limit rows and convert to records
|
|
336
|
+
return df.head(limit).to_dict(orient="records")
|
|
337
|
+
except Exception as e:
|
|
338
|
+
raise RuntimeError(f"Failed to preview dataset {dataset_rid}: {e}")
|
|
339
|
+
|
|
315
340
|
def delete_dataset(self, dataset_rid: str) -> bool:
|
|
316
341
|
"""
|
|
317
342
|
Delete a dataset.
|
|
@@ -487,18 +512,15 @@ class DatasetService(BaseService):
|
|
|
487
512
|
# Ensure output directory exists
|
|
488
513
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
489
514
|
|
|
490
|
-
|
|
515
|
+
# Use Dataset.File.content() which returns bytes directly
|
|
516
|
+
# Note: In SDK v1.27.0, the method is 'content' not 'read'
|
|
517
|
+
file_content = self.service.Dataset.File.content(
|
|
491
518
|
dataset_rid=dataset_rid, file_path=file_path, branch_name=branch
|
|
492
519
|
)
|
|
493
520
|
|
|
494
|
-
# Write file content to disk
|
|
521
|
+
# Write file content to disk (file_content is bytes)
|
|
495
522
|
with open(output_path, "wb") as f:
|
|
496
|
-
|
|
497
|
-
# If it's a stream
|
|
498
|
-
f.write(file_content.read())
|
|
499
|
-
else:
|
|
500
|
-
# If it's bytes
|
|
501
|
-
f.write(file_content)
|
|
523
|
+
f.write(file_content)
|
|
502
524
|
|
|
503
525
|
return {
|
|
504
526
|
"dataset_rid": dataset_rid,
|
|
@@ -519,6 +541,8 @@ class DatasetService(BaseService):
|
|
|
519
541
|
"""
|
|
520
542
|
List files in a dataset.
|
|
521
543
|
|
|
544
|
+
DEPRECATED: Use list_files_paginated() instead for better pagination support.
|
|
545
|
+
|
|
522
546
|
Args:
|
|
523
547
|
dataset_rid: Dataset Resource Identifier
|
|
524
548
|
branch: Dataset branch name
|
|
@@ -543,6 +567,53 @@ class DatasetService(BaseService):
|
|
|
543
567
|
except Exception as e:
|
|
544
568
|
raise RuntimeError(f"Failed to list files in dataset {dataset_rid}: {e}")
|
|
545
569
|
|
|
570
|
+
def list_files_paginated(
|
|
571
|
+
self,
|
|
572
|
+
dataset_rid: str,
|
|
573
|
+
branch: str,
|
|
574
|
+
config: PaginationConfig,
|
|
575
|
+
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
576
|
+
) -> PaginationResult:
|
|
577
|
+
"""
|
|
578
|
+
List files with full pagination control.
|
|
579
|
+
|
|
580
|
+
Args:
|
|
581
|
+
dataset_rid: Dataset Resource Identifier
|
|
582
|
+
branch: Dataset branch name
|
|
583
|
+
config: Pagination configuration
|
|
584
|
+
progress_callback: Optional progress callback
|
|
585
|
+
|
|
586
|
+
Returns:
|
|
587
|
+
PaginationResult with file information and metadata
|
|
588
|
+
"""
|
|
589
|
+
try:
|
|
590
|
+
settings = Settings()
|
|
591
|
+
|
|
592
|
+
# Get iterator from SDK - ResourceIterator with next_page_token support
|
|
593
|
+
iterator = self.service.Dataset.File.list(
|
|
594
|
+
dataset_rid=dataset_rid,
|
|
595
|
+
branch_name=branch,
|
|
596
|
+
page_size=config.page_size or settings.get("page_size", 20),
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
# Use iterator pagination handler
|
|
600
|
+
result = self._paginate_iterator(iterator, config, progress_callback)
|
|
601
|
+
|
|
602
|
+
# Format file information
|
|
603
|
+
result.data = [
|
|
604
|
+
{
|
|
605
|
+
"path": file.path,
|
|
606
|
+
"size_bytes": getattr(file, "size_bytes", None),
|
|
607
|
+
"last_modified": getattr(file, "last_modified", None),
|
|
608
|
+
"transaction_rid": getattr(file, "transaction_rid", None),
|
|
609
|
+
}
|
|
610
|
+
for file in result.data
|
|
611
|
+
]
|
|
612
|
+
|
|
613
|
+
return result
|
|
614
|
+
except Exception as e:
|
|
615
|
+
raise RuntimeError(f"Failed to list files: {e}")
|
|
616
|
+
|
|
546
617
|
def get_branches(self, dataset_rid: str) -> List[Dict[str, Any]]:
|
|
547
618
|
"""
|
|
548
619
|
Get list of branches for a dataset.
|
pltr/services/mediasets.py
CHANGED
|
@@ -226,15 +226,7 @@ class MediaSetsService(BaseService):
|
|
|
226
226
|
preview=preview,
|
|
227
227
|
)
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
if hasattr(response, "content"):
|
|
231
|
-
file.write(response.content)
|
|
232
|
-
else:
|
|
233
|
-
# Handle streaming response
|
|
234
|
-
for chunk in response:
|
|
235
|
-
file.write(chunk)
|
|
236
|
-
|
|
237
|
-
file_size = output_path_obj.stat().st_size
|
|
229
|
+
file_size = self._write_response_to_file(response, output_path_obj)
|
|
238
230
|
return {
|
|
239
231
|
"media_set_rid": media_set_rid,
|
|
240
232
|
"media_item_rid": media_item_rid,
|
|
@@ -291,3 +283,146 @@ class MediaSetsService(BaseService):
|
|
|
291
283
|
"url": getattr(reference_response, "url", "unknown"),
|
|
292
284
|
"expires_at": getattr(reference_response, "expires_at", None),
|
|
293
285
|
}
|
|
286
|
+
|
|
287
|
+
def _write_response_to_file(self, response: Any, output_path: Path) -> int:
|
|
288
|
+
"""
|
|
289
|
+
Write response content to file and return file size.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
response: SDK response object (with .content attribute or iterable)
|
|
293
|
+
output_path: Path object for the output file
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
File size in bytes
|
|
297
|
+
"""
|
|
298
|
+
with open(output_path, "wb") as file:
|
|
299
|
+
if hasattr(response, "content"):
|
|
300
|
+
file.write(response.content)
|
|
301
|
+
else:
|
|
302
|
+
# Handle streaming response
|
|
303
|
+
for chunk in response:
|
|
304
|
+
file.write(chunk)
|
|
305
|
+
return output_path.stat().st_size
|
|
306
|
+
|
|
307
|
+
def calculate_thumbnail(
|
|
308
|
+
self,
|
|
309
|
+
media_set_rid: str,
|
|
310
|
+
media_item_rid: str,
|
|
311
|
+
preview: bool = False,
|
|
312
|
+
) -> Dict[str, Any]:
|
|
313
|
+
"""
|
|
314
|
+
Initiate thumbnail generation for an image.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
media_set_rid: Media Set Resource Identifier
|
|
318
|
+
media_item_rid: Media Item Resource Identifier
|
|
319
|
+
preview: Enable preview mode
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
Thumbnail calculation status information
|
|
323
|
+
"""
|
|
324
|
+
try:
|
|
325
|
+
response = self.service.MediaSet.calculate(
|
|
326
|
+
media_set_rid=media_set_rid,
|
|
327
|
+
media_item_rid=media_item_rid,
|
|
328
|
+
preview=preview,
|
|
329
|
+
)
|
|
330
|
+
return self._format_thumbnail_status(response)
|
|
331
|
+
except Exception as e:
|
|
332
|
+
raise RuntimeError(f"Failed to calculate thumbnail: {e}")
|
|
333
|
+
|
|
334
|
+
def retrieve_thumbnail(
|
|
335
|
+
self,
|
|
336
|
+
media_set_rid: str,
|
|
337
|
+
media_item_rid: str,
|
|
338
|
+
output_path: str,
|
|
339
|
+
preview: bool = False,
|
|
340
|
+
) -> Dict[str, Any]:
|
|
341
|
+
"""
|
|
342
|
+
Retrieve a calculated thumbnail (200px wide webp).
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
media_set_rid: Media Set Resource Identifier
|
|
346
|
+
media_item_rid: Media Item Resource Identifier
|
|
347
|
+
output_path: Local path where thumbnail should be saved
|
|
348
|
+
preview: Enable preview mode
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Download response information
|
|
352
|
+
"""
|
|
353
|
+
try:
|
|
354
|
+
output_path_obj = Path(output_path)
|
|
355
|
+
output_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
|
356
|
+
|
|
357
|
+
response = self.service.MediaSet.retrieve(
|
|
358
|
+
media_set_rid=media_set_rid,
|
|
359
|
+
media_item_rid=media_item_rid,
|
|
360
|
+
preview=preview,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
file_size = self._write_response_to_file(response, output_path_obj)
|
|
364
|
+
|
|
365
|
+
# Validate that we received actual content
|
|
366
|
+
if file_size == 0:
|
|
367
|
+
output_path_obj.unlink(missing_ok=True)
|
|
368
|
+
raise RuntimeError(
|
|
369
|
+
"Downloaded thumbnail is empty - thumbnail may not be ready yet"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
"media_set_rid": media_set_rid,
|
|
374
|
+
"media_item_rid": media_item_rid,
|
|
375
|
+
"output_path": str(output_path_obj),
|
|
376
|
+
"file_size": file_size,
|
|
377
|
+
"downloaded": True,
|
|
378
|
+
"format": "image/webp",
|
|
379
|
+
}
|
|
380
|
+
except Exception as e:
|
|
381
|
+
raise RuntimeError(f"Failed to retrieve thumbnail: {e}")
|
|
382
|
+
|
|
383
|
+
def upload_temp_media(
|
|
384
|
+
self,
|
|
385
|
+
file_path: str,
|
|
386
|
+
filename: Optional[str] = None,
|
|
387
|
+
attribution: Optional[str] = None,
|
|
388
|
+
preview: bool = False,
|
|
389
|
+
) -> Dict[str, Any]:
|
|
390
|
+
"""
|
|
391
|
+
Upload temporary media (auto-deleted after 1 hour if not persisted).
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
file_path: Local path to the file to upload
|
|
395
|
+
filename: Optional filename override
|
|
396
|
+
attribution: Optional attribution string
|
|
397
|
+
preview: Enable preview mode
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Media reference information
|
|
401
|
+
"""
|
|
402
|
+
try:
|
|
403
|
+
file_path_obj = Path(file_path)
|
|
404
|
+
if not file_path_obj.exists():
|
|
405
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
406
|
+
|
|
407
|
+
# Use provided filename or default to file name
|
|
408
|
+
upload_filename = filename or file_path_obj.name
|
|
409
|
+
|
|
410
|
+
with open(file_path_obj, "rb") as file:
|
|
411
|
+
response = self.service.MediaSet.upload_media(
|
|
412
|
+
body=file,
|
|
413
|
+
filename=upload_filename,
|
|
414
|
+
attribution=attribution,
|
|
415
|
+
preview=preview,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
return self._format_media_reference(response)
|
|
419
|
+
except Exception as e:
|
|
420
|
+
raise RuntimeError(f"Failed to upload temporary media: {e}")
|
|
421
|
+
|
|
422
|
+
def _format_thumbnail_status(self, status_response: Any) -> Dict[str, Any]:
|
|
423
|
+
"""Format thumbnail calculation status response for display."""
|
|
424
|
+
return {
|
|
425
|
+
"status": getattr(status_response, "status", "unknown"),
|
|
426
|
+
"transformation_id": getattr(status_response, "transformation_id", None),
|
|
427
|
+
"media_item_rid": getattr(status_response, "media_item_rid", None),
|
|
428
|
+
}
|
pltr/services/ontology.py
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
Ontology service wrappers for Foundry SDK.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Any,
|
|
5
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from ..config.settings import Settings
|
|
8
|
+
from ..utils.pagination import PaginationConfig, PaginationResult
|
|
6
9
|
from .base import BaseService
|
|
7
10
|
|
|
8
11
|
|
|
@@ -165,6 +168,8 @@ class OntologyObjectService(BaseService):
|
|
|
165
168
|
"""
|
|
166
169
|
List objects of a specific type.
|
|
167
170
|
|
|
171
|
+
DEPRECATED: Use list_objects_paginated() instead for better pagination support.
|
|
172
|
+
|
|
168
173
|
Args:
|
|
169
174
|
ontology_rid: Ontology Resource Identifier
|
|
170
175
|
object_type: Object type API name
|
|
@@ -188,6 +193,48 @@ class OntologyObjectService(BaseService):
|
|
|
188
193
|
except Exception as e:
|
|
189
194
|
raise RuntimeError(f"Failed to list objects: {e}")
|
|
190
195
|
|
|
196
|
+
def list_objects_paginated(
|
|
197
|
+
self,
|
|
198
|
+
ontology_rid: str,
|
|
199
|
+
object_type: str,
|
|
200
|
+
config: PaginationConfig,
|
|
201
|
+
properties: Optional[List[str]] = None,
|
|
202
|
+
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
203
|
+
) -> PaginationResult:
|
|
204
|
+
"""
|
|
205
|
+
List objects with full pagination control.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
ontology_rid: Ontology Resource Identifier
|
|
209
|
+
object_type: Object type API name
|
|
210
|
+
config: Pagination configuration
|
|
211
|
+
properties: List of properties to include
|
|
212
|
+
progress_callback: Optional progress callback
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
PaginationResult with objects and metadata
|
|
216
|
+
"""
|
|
217
|
+
try:
|
|
218
|
+
settings = Settings()
|
|
219
|
+
|
|
220
|
+
# Get iterator from SDK - ResourceIterator with next_page_token support
|
|
221
|
+
iterator = self.service.OntologyObject.list(
|
|
222
|
+
ontology_rid,
|
|
223
|
+
object_type,
|
|
224
|
+
page_size=config.page_size or settings.get("page_size", 20),
|
|
225
|
+
properties=properties,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Use iterator pagination handler
|
|
229
|
+
result = self._paginate_iterator(iterator, config, progress_callback)
|
|
230
|
+
|
|
231
|
+
# Format objects
|
|
232
|
+
result.data = [self._format_object(obj) for obj in result.data]
|
|
233
|
+
|
|
234
|
+
return result
|
|
235
|
+
except Exception as e:
|
|
236
|
+
raise RuntimeError(f"Failed to list objects: {e}")
|
|
237
|
+
|
|
191
238
|
def get_object(
|
|
192
239
|
self,
|
|
193
240
|
ontology_rid: str,
|
|
@@ -287,6 +334,77 @@ class OntologyObjectService(BaseService):
|
|
|
287
334
|
except Exception as e:
|
|
288
335
|
raise RuntimeError(f"Failed to list linked objects: {e}")
|
|
289
336
|
|
|
337
|
+
def count_objects(
|
|
338
|
+
self,
|
|
339
|
+
ontology_rid: str,
|
|
340
|
+
object_type: str,
|
|
341
|
+
branch: Optional[str] = None,
|
|
342
|
+
) -> Dict[str, Any]:
|
|
343
|
+
"""
|
|
344
|
+
Count objects of a specific type.
|
|
345
|
+
|
|
346
|
+
Args:
|
|
347
|
+
ontology_rid: Ontology Resource Identifier
|
|
348
|
+
object_type: Object type API name
|
|
349
|
+
branch: Branch name (optional)
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Dictionary containing count information
|
|
353
|
+
"""
|
|
354
|
+
try:
|
|
355
|
+
count = self.service.OntologyObject.count(
|
|
356
|
+
ontology_rid,
|
|
357
|
+
object_type,
|
|
358
|
+
branch_name=branch,
|
|
359
|
+
)
|
|
360
|
+
return {
|
|
361
|
+
"ontology_rid": ontology_rid,
|
|
362
|
+
"object_type": object_type,
|
|
363
|
+
"count": count,
|
|
364
|
+
"branch": branch,
|
|
365
|
+
}
|
|
366
|
+
except Exception as e:
|
|
367
|
+
raise RuntimeError(f"Failed to count objects: {e}")
|
|
368
|
+
|
|
369
|
+
def search_objects(
|
|
370
|
+
self,
|
|
371
|
+
ontology_rid: str,
|
|
372
|
+
object_type: str,
|
|
373
|
+
query: str,
|
|
374
|
+
page_size: Optional[int] = None,
|
|
375
|
+
properties: Optional[List[str]] = None,
|
|
376
|
+
branch: Optional[str] = None,
|
|
377
|
+
) -> List[Dict[str, Any]]:
|
|
378
|
+
"""
|
|
379
|
+
Search objects by query.
|
|
380
|
+
|
|
381
|
+
Args:
|
|
382
|
+
ontology_rid: Ontology Resource Identifier
|
|
383
|
+
object_type: Object type API name
|
|
384
|
+
query: Search query string
|
|
385
|
+
page_size: Number of results per page
|
|
386
|
+
properties: List of properties to include
|
|
387
|
+
branch: Branch name (optional)
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
List of matching object dictionaries
|
|
391
|
+
"""
|
|
392
|
+
try:
|
|
393
|
+
result = self.service.OntologyObject.search(
|
|
394
|
+
ontology_rid,
|
|
395
|
+
object_type,
|
|
396
|
+
query=query,
|
|
397
|
+
page_size=page_size,
|
|
398
|
+
properties=properties,
|
|
399
|
+
branch_name=branch,
|
|
400
|
+
)
|
|
401
|
+
objects = []
|
|
402
|
+
for obj in result:
|
|
403
|
+
objects.append(self._format_object(obj))
|
|
404
|
+
return objects
|
|
405
|
+
except Exception as e:
|
|
406
|
+
raise RuntimeError(f"Failed to search objects: {e}")
|
|
407
|
+
|
|
290
408
|
def _format_object(self, obj: Any) -> Dict[str, Any]:
|
|
291
409
|
"""Format object for consistent output."""
|
|
292
410
|
# Objects may have various properties - extract them dynamically
|
pltr/services/orchestration.py
CHANGED
|
@@ -3,8 +3,10 @@ Orchestration service wrapper for Foundry SDK v2 API.
|
|
|
3
3
|
Provides operations for managing builds, jobs, and schedules.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
from typing import Any,
|
|
6
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
7
7
|
|
|
8
|
+
from ..config.settings import Settings
|
|
9
|
+
from ..utils.pagination import PaginationConfig, PaginationResult
|
|
8
10
|
from .base import BaseService
|
|
9
11
|
|
|
10
12
|
|
|
@@ -134,6 +136,8 @@ class OrchestrationService(BaseService):
|
|
|
134
136
|
"""
|
|
135
137
|
Search for builds.
|
|
136
138
|
|
|
139
|
+
DEPRECATED: Use search_builds_paginated() instead for better pagination support.
|
|
140
|
+
|
|
137
141
|
Args:
|
|
138
142
|
page_size: Number of results per page
|
|
139
143
|
page_token: Token for pagination
|
|
@@ -155,6 +159,63 @@ class OrchestrationService(BaseService):
|
|
|
155
159
|
except Exception as e:
|
|
156
160
|
raise RuntimeError(f"Failed to search builds: {e}")
|
|
157
161
|
|
|
162
|
+
def search_builds_paginated(
|
|
163
|
+
self,
|
|
164
|
+
config: PaginationConfig,
|
|
165
|
+
progress_callback: Optional[Callable[[int, int], None]] = None,
|
|
166
|
+
**search_params,
|
|
167
|
+
) -> PaginationResult:
|
|
168
|
+
"""
|
|
169
|
+
Search for builds with full pagination control.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
config: Pagination configuration
|
|
173
|
+
progress_callback: Optional progress callback
|
|
174
|
+
**search_params: Additional search parameters
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
PaginationResult with builds and metadata
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
settings = Settings()
|
|
181
|
+
|
|
182
|
+
def fetch_page(page_token: Optional[str]) -> Dict[str, Any]:
|
|
183
|
+
"""Fetch a single page of builds."""
|
|
184
|
+
kwargs: Dict[str, Any] = {
|
|
185
|
+
"page_size": config.page_size or settings.get("page_size", 20),
|
|
186
|
+
}
|
|
187
|
+
if page_token:
|
|
188
|
+
kwargs["page_token"] = page_token
|
|
189
|
+
kwargs.update(search_params)
|
|
190
|
+
|
|
191
|
+
response = self.service.Build.search(**kwargs)
|
|
192
|
+
return self._format_builds_search_response(response)
|
|
193
|
+
|
|
194
|
+
return self._paginate_response(fetch_page, config, progress_callback)
|
|
195
|
+
except Exception as e:
|
|
196
|
+
raise RuntimeError(f"Failed to search builds: {e}")
|
|
197
|
+
|
|
198
|
+
def get_builds_batch(self, build_rids: List[str]) -> Dict[str, Any]:
|
|
199
|
+
"""
|
|
200
|
+
Get multiple builds in batch.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
build_rids: List of Build Resource Identifiers (max 100)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Batch response with build information
|
|
207
|
+
"""
|
|
208
|
+
if len(build_rids) > 100:
|
|
209
|
+
raise ValueError("Maximum batch size is 100 builds")
|
|
210
|
+
|
|
211
|
+
try:
|
|
212
|
+
# SDK expects list of {"rid": ...} objects for batch operations
|
|
213
|
+
body = [{"rid": rid} for rid in build_rids]
|
|
214
|
+
response = self.service.Build.get_batch(body)
|
|
215
|
+
return self._format_builds_batch_response(response)
|
|
216
|
+
except Exception as e:
|
|
217
|
+
raise RuntimeError(f"Failed to get builds batch: {e}")
|
|
218
|
+
|
|
158
219
|
# Job operations
|
|
159
220
|
def get_job(self, job_rid: str) -> Dict[str, Any]:
|
|
160
221
|
"""
|
|
@@ -353,6 +414,35 @@ class OrchestrationService(BaseService):
|
|
|
353
414
|
except Exception as e:
|
|
354
415
|
raise RuntimeError(f"Failed to replace schedule {schedule_rid}: {e}")
|
|
355
416
|
|
|
417
|
+
def get_schedule_runs(
|
|
418
|
+
self,
|
|
419
|
+
schedule_rid: str,
|
|
420
|
+
page_size: Optional[int] = None,
|
|
421
|
+
page_token: Optional[str] = None,
|
|
422
|
+
) -> Dict[str, Any]:
|
|
423
|
+
"""
|
|
424
|
+
Get recent execution runs for a schedule.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
schedule_rid: Schedule Resource Identifier
|
|
428
|
+
page_size: Number of results per page
|
|
429
|
+
page_token: Token for pagination
|
|
430
|
+
|
|
431
|
+
Returns:
|
|
432
|
+
Runs list with pagination info
|
|
433
|
+
"""
|
|
434
|
+
try:
|
|
435
|
+
kwargs: Dict[str, Any] = {"schedule_rid": schedule_rid}
|
|
436
|
+
if page_size is not None:
|
|
437
|
+
kwargs["page_size"] = page_size
|
|
438
|
+
if page_token is not None:
|
|
439
|
+
kwargs["page_token"] = page_token
|
|
440
|
+
|
|
441
|
+
response = self.service.Schedule.runs(**kwargs)
|
|
442
|
+
return self._format_schedule_runs_response(response)
|
|
443
|
+
except Exception as e:
|
|
444
|
+
raise RuntimeError(f"Failed to get runs for schedule {schedule_rid}: {e}")
|
|
445
|
+
|
|
356
446
|
# Formatting methods
|
|
357
447
|
def _format_build_info(self, build: Any) -> Dict[str, Any]:
|
|
358
448
|
"""Format build information for consistent output."""
|
|
@@ -455,3 +545,45 @@ class OrchestrationService(BaseService):
|
|
|
455
545
|
result["jobs"].append(self._format_job_info(item.data))
|
|
456
546
|
|
|
457
547
|
return result
|
|
548
|
+
|
|
549
|
+
def _format_builds_batch_response(self, response: Any) -> Dict[str, Any]:
|
|
550
|
+
"""Format builds batch response."""
|
|
551
|
+
result: Dict[str, Any] = {"builds": []}
|
|
552
|
+
|
|
553
|
+
if hasattr(response, "data"):
|
|
554
|
+
for item in response.data:
|
|
555
|
+
if hasattr(item, "data"):
|
|
556
|
+
result["builds"].append(self._format_build_info(item.data))
|
|
557
|
+
|
|
558
|
+
return result
|
|
559
|
+
|
|
560
|
+
def _format_run_info(self, run: Any) -> Dict[str, Any]:
|
|
561
|
+
"""Format schedule run information for consistent output."""
|
|
562
|
+
info = {}
|
|
563
|
+
|
|
564
|
+
for attr in [
|
|
565
|
+
"rid",
|
|
566
|
+
"schedule_rid",
|
|
567
|
+
"status",
|
|
568
|
+
"created_time",
|
|
569
|
+
"started_time",
|
|
570
|
+
"finished_time",
|
|
571
|
+
"build_rid",
|
|
572
|
+
"result",
|
|
573
|
+
]:
|
|
574
|
+
if hasattr(run, attr):
|
|
575
|
+
info[attr] = getattr(run, attr)
|
|
576
|
+
|
|
577
|
+
return info
|
|
578
|
+
|
|
579
|
+
def _format_schedule_runs_response(self, response: Any) -> Dict[str, Any]:
|
|
580
|
+
"""Format schedule runs response."""
|
|
581
|
+
result: Dict[str, Any] = {"runs": []}
|
|
582
|
+
|
|
583
|
+
if hasattr(response, "data"):
|
|
584
|
+
result["runs"] = [self._format_run_info(run) for run in response.data]
|
|
585
|
+
|
|
586
|
+
if hasattr(response, "next_page_token"):
|
|
587
|
+
result["next_page_token"] = response.next_page_token
|
|
588
|
+
|
|
589
|
+
return result
|