pltr-cli 0.11.0__py3-none-any.whl → 0.13.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.
Files changed (46) hide show
  1. pltr/__init__.py +1 -1
  2. pltr/cli.py +40 -0
  3. pltr/commands/admin.py +565 -11
  4. pltr/commands/aip_agents.py +333 -0
  5. pltr/commands/connectivity.py +309 -1
  6. pltr/commands/cp.py +103 -0
  7. pltr/commands/dataset.py +104 -4
  8. pltr/commands/functions.py +503 -0
  9. pltr/commands/language_models.py +515 -0
  10. pltr/commands/mediasets.py +176 -0
  11. pltr/commands/models.py +362 -0
  12. pltr/commands/ontology.py +44 -13
  13. pltr/commands/orchestration.py +167 -11
  14. pltr/commands/project.py +231 -22
  15. pltr/commands/resource.py +416 -17
  16. pltr/commands/space.py +25 -303
  17. pltr/commands/sql.py +54 -7
  18. pltr/commands/streams.py +616 -0
  19. pltr/commands/third_party_applications.py +82 -0
  20. pltr/services/admin.py +331 -3
  21. pltr/services/aip_agents.py +147 -0
  22. pltr/services/base.py +104 -1
  23. pltr/services/connectivity.py +139 -0
  24. pltr/services/copy.py +391 -0
  25. pltr/services/dataset.py +77 -4
  26. pltr/services/folder.py +6 -1
  27. pltr/services/functions.py +223 -0
  28. pltr/services/language_models.py +281 -0
  29. pltr/services/mediasets.py +144 -9
  30. pltr/services/models.py +179 -0
  31. pltr/services/ontology.py +48 -1
  32. pltr/services/orchestration.py +133 -1
  33. pltr/services/project.py +213 -39
  34. pltr/services/resource.py +229 -60
  35. pltr/services/space.py +24 -175
  36. pltr/services/sql.py +44 -20
  37. pltr/services/streams.py +290 -0
  38. pltr/services/third_party_applications.py +53 -0
  39. pltr/utils/formatting.py +195 -1
  40. pltr/utils/pagination.py +325 -0
  41. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/METADATA +55 -4
  42. pltr_cli-0.13.0.dist-info/RECORD +70 -0
  43. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/WHEEL +1 -1
  44. pltr_cli-0.11.0.dist-info/RECORD +0 -55
  45. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/entry_points.txt +0 -0
  46. {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/licenses/LICENSE +0 -0
pltr/services/project.py CHANGED
@@ -3,6 +3,7 @@ Project service wrapper for Foundry SDK filesystem API.
3
3
  """
4
4
 
5
5
  from typing import Any, Optional, Dict, List
6
+ import inspect
6
7
 
7
8
  from .base import BaseService
8
9
 
@@ -21,7 +22,7 @@ class ProjectService(BaseService):
21
22
  description: Optional[str] = None,
22
23
  organization_rids: Optional[List[str]] = None,
23
24
  default_roles: Optional[List[str]] = None,
24
- role_grants: Optional[List[Dict[str, Any]]] = None,
25
+ role_grants: Optional[Any] = None,
25
26
  ) -> Dict[str, Any]:
26
27
  """
27
28
  Create a new project.
@@ -38,25 +39,84 @@ class ProjectService(BaseService):
38
39
  Created project information
39
40
  """
40
41
  try:
41
- # Prepare the create request payload
42
- create_request: Dict[str, Any] = {
42
+ normalized_role_grants: Dict[str, List[Dict[str, Any]]] = {}
43
+ if role_grants is None:
44
+ from .admin import AdminService
45
+
46
+ current_user = AdminService(profile=self.profile).get_current_user()
47
+ user_id = (
48
+ current_user.get("id")
49
+ or current_user.get("user_id")
50
+ or current_user.get("userId")
51
+ )
52
+ if not user_id:
53
+ raise RuntimeError(
54
+ "Unable to determine current user id for owner role grant"
55
+ )
56
+ normalized_role_grants = {
57
+ "compass:manage": [
58
+ {
59
+ "principal_id": user_id,
60
+ "principal_type": "USER",
61
+ }
62
+ ]
63
+ }
64
+ elif role_grants:
65
+ if isinstance(role_grants, dict):
66
+ normalized_role_grants = role_grants
67
+ elif isinstance(role_grants, list):
68
+ for grant in role_grants:
69
+ if not isinstance(grant, dict):
70
+ raise ValueError(
71
+ "role_grants list entries must be dictionaries"
72
+ )
73
+ role_name = grant.get("role_name")
74
+ if not role_name:
75
+ raise ValueError(
76
+ "role_grants entries must include role_name"
77
+ )
78
+ principal = {
79
+ key: value
80
+ for key, value in grant.items()
81
+ if key != "role_name"
82
+ }
83
+ normalized_role_grants.setdefault(role_name, []).append(
84
+ principal
85
+ )
86
+ else:
87
+ raise ValueError("role_grants must be a dict or a list of dicts")
88
+
89
+ if normalized_role_grants:
90
+ for principals in normalized_role_grants.values():
91
+ for principal in principals:
92
+ principal_type = principal.get("principal_type")
93
+ if isinstance(principal_type, str):
94
+ principal["principal_type"] = principal_type.upper()
95
+
96
+ create_params: Dict[str, Any] = {
43
97
  "display_name": display_name,
44
98
  "space_rid": space_rid,
99
+ "description": description,
100
+ "organization_rids": organization_rids if organization_rids else [],
101
+ "default_roles": default_roles if default_roles else [],
102
+ "role_grants": normalized_role_grants,
45
103
  }
46
104
 
47
- if description:
48
- create_request["description"] = description
49
- if organization_rids:
50
- create_request["organization_rids"] = organization_rids
51
- if default_roles:
52
- create_request["default_roles"] = default_roles
53
- if role_grants:
54
- create_request["role_grants"] = role_grants
55
-
56
- project = self.service.Project.create(
57
- body=create_request,
58
- preview=True,
59
- )
105
+ create_fn = self.service.Project.create
106
+ try:
107
+ params = inspect.signature(create_fn).parameters
108
+ supports_kwargs = "display_name" in params or any(
109
+ param.kind == inspect.Parameter.VAR_KEYWORD
110
+ for param in params.values()
111
+ )
112
+ if supports_kwargs:
113
+ project = create_fn(**create_params, preview=True)
114
+ elif "body" in params:
115
+ project = create_fn(body=create_params, preview=True)
116
+ else:
117
+ project = create_fn(**create_params, preview=True)
118
+ except (TypeError, ValueError):
119
+ project = create_fn(**create_params, preview=True)
60
120
  return self._format_project_info(project)
61
121
  except Exception as e:
62
122
  raise RuntimeError(f"Failed to create project '{display_name}': {e}")
@@ -134,56 +194,154 @@ class ProjectService(BaseService):
134
194
  description: Optional[str] = None,
135
195
  ) -> Dict[str, Any]:
136
196
  """
137
- Update project information.
197
+ Update project information using replace().
138
198
 
139
199
  Args:
140
200
  project_rid: Project Resource Identifier
141
- display_name: New display name (optional)
201
+ display_name: New display name (optional, fetches current if not provided)
142
202
  description: New description (optional)
143
203
 
144
204
  Returns:
145
205
  Updated project information
146
206
  """
147
- update_request: Dict[str, Any] = {}
148
- if display_name:
149
- update_request["display_name"] = display_name
150
- if description:
151
- update_request["description"] = description
152
-
153
- if not update_request:
207
+ if not display_name and not description:
154
208
  raise ValueError("At least one field must be provided for update")
155
209
 
156
210
  try:
157
- project = self.service.Project.update(
211
+ # Fetch current project to get display_name if not provided (required for replace)
212
+ if not display_name:
213
+ current_project = self.service.Project.get(project_rid, preview=True)
214
+ display_name = current_project.display_name
215
+
216
+ project = self.service.Project.replace(
158
217
  project_rid=project_rid,
159
- body=update_request,
218
+ display_name=display_name,
219
+ description=description,
160
220
  preview=True,
161
221
  )
162
222
  return self._format_project_info(project)
163
223
  except Exception as e:
164
224
  raise RuntimeError(f"Failed to update project {project_rid}: {e}")
165
225
 
166
- def get_projects_batch(self, project_rids: List[str]) -> List[Dict[str, Any]]:
226
+ # ==================== Organization Operations ====================
227
+
228
+ def add_organizations(self, project_rid: str, organization_rids: List[str]) -> None:
229
+ """
230
+ Add organizations to a project.
231
+
232
+ Args:
233
+ project_rid: Project Resource Identifier
234
+ organization_rids: List of organization RIDs to add
235
+
236
+ Raises:
237
+ RuntimeError: If adding organizations fails
238
+ """
239
+ try:
240
+ self.service.Project.add_organizations(
241
+ project_rid, organization_rids=organization_rids, preview=True
242
+ )
243
+ except Exception as e:
244
+ raise RuntimeError(
245
+ f"Failed to add organizations to project {project_rid}: {e}"
246
+ )
247
+
248
+ def remove_organizations(
249
+ self, project_rid: str, organization_rids: List[str]
250
+ ) -> None:
251
+ """
252
+ Remove organizations from a project.
253
+
254
+ Args:
255
+ project_rid: Project Resource Identifier
256
+ organization_rids: List of organization RIDs to remove
257
+
258
+ Raises:
259
+ RuntimeError: If removing organizations fails
167
260
  """
168
- Get multiple projects in a single request.
261
+ try:
262
+ self.service.Project.remove_organizations(
263
+ project_rid, organization_rids=organization_rids, preview=True
264
+ )
265
+ except Exception as e:
266
+ raise RuntimeError(
267
+ f"Failed to remove organizations from project {project_rid}: {e}"
268
+ )
269
+
270
+ def list_organizations(
271
+ self,
272
+ project_rid: str,
273
+ page_size: Optional[int] = None,
274
+ page_token: Optional[str] = None,
275
+ ) -> List[Dict[str, Any]]:
276
+ """
277
+ List organizations directly applied to a project.
169
278
 
170
279
  Args:
171
- project_rids: List of project RIDs (max 1000)
280
+ project_rid: Project Resource Identifier
281
+ page_size: Number of items per page (optional)
282
+ page_token: Pagination token (optional)
172
283
 
173
284
  Returns:
174
- List of project information dictionaries
285
+ List of organization information dictionaries
175
286
  """
176
- if len(project_rids) > 1000:
177
- raise ValueError("Maximum batch size is 1000 projects")
287
+ try:
288
+ organizations = []
289
+ list_params: Dict[str, Any] = {"preview": True}
290
+
291
+ if page_size:
292
+ list_params["page_size"] = page_size
293
+ if page_token:
294
+ list_params["page_token"] = page_token
178
295
 
296
+ for org in self.service.Project.organizations(project_rid, **list_params):
297
+ organizations.append(self._format_organization_info(org))
298
+ return organizations
299
+ except Exception as e:
300
+ raise RuntimeError(
301
+ f"Failed to list organizations for project {project_rid}: {e}"
302
+ )
303
+
304
+ # ==================== Template Operations ====================
305
+
306
+ def create_project_from_template(
307
+ self,
308
+ template_rid: str,
309
+ variable_values: Dict[str, str],
310
+ default_roles: Optional[List[str]] = None,
311
+ organization_rids: Optional[List[str]] = None,
312
+ project_description: Optional[str] = None,
313
+ ) -> Dict[str, Any]:
314
+ """
315
+ Create a project from a template.
316
+
317
+ Args:
318
+ template_rid: Template Resource Identifier
319
+ variable_values: Dictionary mapping template variable names to values
320
+ default_roles: List of default role names (optional)
321
+ organization_rids: List of organization RIDs (optional)
322
+ project_description: Project description (optional)
323
+
324
+ Returns:
325
+ Created project information
326
+ """
179
327
  try:
180
- response = self.service.Project.get_batch(body=project_rids, preview=True)
181
- projects = []
182
- for project in response.projects:
183
- projects.append(self._format_project_info(project))
184
- return projects
328
+ create_params: Dict[str, Any] = {
329
+ "template_rid": template_rid,
330
+ "variable_values": variable_values,
331
+ "preview": True,
332
+ }
333
+
334
+ if default_roles:
335
+ create_params["default_roles"] = default_roles
336
+ if organization_rids:
337
+ create_params["organization_rids"] = organization_rids
338
+ if project_description:
339
+ create_params["project_description"] = project_description
340
+
341
+ project = self.service.Project.create_from_template(**create_params)
342
+ return self._format_project_info(project)
185
343
  except Exception as e:
186
- raise RuntimeError(f"Failed to get projects batch: {e}")
344
+ raise RuntimeError(f"Failed to create project from template: {e}")
187
345
 
188
346
  def _format_project_info(self, project: Any) -> Dict[str, Any]:
189
347
  """
@@ -213,6 +371,22 @@ class ProjectService(BaseService):
213
371
  "type": "project",
214
372
  }
215
373
 
374
+ def _format_organization_info(self, organization: Any) -> Dict[str, Any]:
375
+ """
376
+ Format organization information for consistent output.
377
+
378
+ Args:
379
+ organization: Organization object from Foundry SDK
380
+
381
+ Returns:
382
+ Formatted organization information dictionary
383
+ """
384
+ return {
385
+ "organization_rid": getattr(organization, "organization_rid", None),
386
+ "display_name": getattr(organization, "display_name", None),
387
+ "description": getattr(organization, "description", None),
388
+ }
389
+
216
390
  def _format_timestamp(self, timestamp: Any) -> Optional[str]:
217
391
  """
218
392
  Format timestamp for display.
pltr/services/resource.py CHANGED
@@ -4,6 +4,11 @@ Resource service wrapper for Foundry SDK filesystem API.
4
4
 
5
5
  from typing import Any, Optional, Dict, List
6
6
 
7
+ from foundry_sdk.v2.filesystem.models import (
8
+ GetResourcesBatchRequestElement,
9
+ GetByPathResourcesBatchRequestElement,
10
+ )
11
+
7
12
  from .base import BaseService
8
13
 
9
14
 
@@ -99,7 +104,11 @@ class ResourceService(BaseService):
99
104
  raise ValueError("Maximum batch size is 1000 resources")
100
105
 
101
106
  try:
102
- response = self.service.Resource.get_batch(body=resource_rids, preview=True)
107
+ elements = [
108
+ GetResourcesBatchRequestElement(resource_rid=rid)
109
+ for rid in resource_rids
110
+ ]
111
+ response = self.service.Resource.get_batch(body=elements, preview=True)
103
112
  resources = []
104
113
  for resource in response.resources:
105
114
  resources.append(self._format_resource_info(resource))
@@ -125,119 +134,230 @@ class ResourceService(BaseService):
125
134
  f"Failed to get metadata for resource {resource_rid}: {e}"
126
135
  )
127
136
 
128
- def set_resource_metadata(
129
- self, resource_rid: str, metadata: Dict[str, Any]
130
- ) -> Dict[str, Any]:
137
+ def search_resources(
138
+ self,
139
+ query: str,
140
+ resource_type: Optional[str] = None,
141
+ folder_rid: Optional[str] = None,
142
+ page_size: Optional[int] = None,
143
+ page_token: Optional[str] = None,
144
+ ) -> List[Dict[str, Any]]:
131
145
  """
132
- Set metadata for a specific resource.
146
+ Search for resources by query string.
133
147
 
134
148
  Args:
135
- resource_rid: Resource Identifier
136
- metadata: Metadata dictionary to set
149
+ query: Search query string
150
+ resource_type: Resource type to filter by (optional)
151
+ folder_rid: Folder to search within (optional)
152
+ page_size: Number of items per page (optional)
153
+ page_token: Pagination token (optional)
137
154
 
138
155
  Returns:
139
- Updated resource metadata
156
+ List of matching resource information dictionaries
140
157
  """
141
158
  try:
142
- updated_metadata = self.service.Resource.set_metadata(
143
- resource_rid=resource_rid,
144
- body=metadata,
145
- preview=True,
146
- )
147
- return self._format_metadata(updated_metadata)
159
+ resources = []
160
+ search_params: Dict[str, Any] = {
161
+ "query": query,
162
+ "preview": True,
163
+ }
164
+
165
+ if resource_type:
166
+ search_params["resource_type"] = resource_type
167
+ if folder_rid:
168
+ search_params["folder_rid"] = folder_rid
169
+ if page_size:
170
+ search_params["page_size"] = page_size
171
+ if page_token:
172
+ search_params["page_token"] = page_token
173
+
174
+ # The search method returns an iterator
175
+ for resource in self.service.Resource.search(**search_params):
176
+ resources.append(self._format_resource_info(resource))
177
+ return resources
178
+ except Exception as e:
179
+ raise RuntimeError(f"Failed to search resources: {e}")
180
+
181
+ # ==================== Trash Operations ====================
182
+
183
+ def delete_resource(self, resource_rid: str) -> None:
184
+ """
185
+ Move a resource to trash.
186
+
187
+ The resource can be restored later or permanently deleted.
188
+
189
+ Args:
190
+ resource_rid: Resource Identifier
191
+
192
+ Raises:
193
+ RuntimeError: If deletion fails
194
+ """
195
+ try:
196
+ self.service.Resource.delete(resource_rid, preview=True)
197
+ except Exception as e:
198
+ raise RuntimeError(f"Failed to delete resource {resource_rid}: {e}")
199
+
200
+ def restore_resource(self, resource_rid: str) -> None:
201
+ """
202
+ Restore a resource from trash.
203
+
204
+ This also restores any directly trashed ancestors.
205
+ Operation is ignored if the resource is not trashed.
206
+
207
+ Args:
208
+ resource_rid: Resource Identifier
209
+
210
+ Raises:
211
+ RuntimeError: If restoration fails
212
+ """
213
+ try:
214
+ self.service.Resource.restore(resource_rid, preview=True)
215
+ except Exception as e:
216
+ raise RuntimeError(f"Failed to restore resource {resource_rid}: {e}")
217
+
218
+ def permanently_delete_resource(self, resource_rid: str) -> None:
219
+ """
220
+ Permanently delete a resource from trash.
221
+
222
+ The resource must already be in the trash. This operation is irreversible.
223
+
224
+ Args:
225
+ resource_rid: Resource Identifier
226
+
227
+ Raises:
228
+ RuntimeError: If permanent deletion fails
229
+ """
230
+ try:
231
+ self.service.Resource.permanently_delete(resource_rid, preview=True)
148
232
  except Exception as e:
149
233
  raise RuntimeError(
150
- f"Failed to set metadata for resource {resource_rid}: {e}"
234
+ f"Failed to permanently delete resource {resource_rid}: {e}"
151
235
  )
152
236
 
153
- def delete_resource_metadata(self, resource_rid: str, keys: List[str]) -> None:
237
+ # ==================== Markings Operations ====================
238
+
239
+ def add_markings(self, resource_rid: str, marking_ids: List[str]) -> None:
154
240
  """
155
- Delete specific metadata keys for a resource.
241
+ Add markings to a resource.
156
242
 
157
243
  Args:
158
244
  resource_rid: Resource Identifier
159
- keys: List of metadata keys to delete
245
+ marking_ids: List of marking identifiers to add
160
246
 
161
247
  Raises:
162
- RuntimeError: If deletion fails
248
+ RuntimeError: If adding markings fails
163
249
  """
164
250
  try:
165
- self.service.Resource.delete_metadata(
166
- resource_rid=resource_rid,
167
- body={"keys": keys},
168
- preview=True,
251
+ self.service.Resource.add_markings(
252
+ resource_rid, marking_ids=marking_ids, preview=True
169
253
  )
170
254
  except Exception as e:
171
255
  raise RuntimeError(
172
- f"Failed to delete metadata for resource {resource_rid}: {e}"
256
+ f"Failed to add markings to resource {resource_rid}: {e}"
173
257
  )
174
258
 
175
- def move_resource(
176
- self, resource_rid: str, target_folder_rid: str
177
- ) -> Dict[str, Any]:
259
+ def remove_markings(self, resource_rid: str, marking_ids: List[str]) -> None:
178
260
  """
179
- Move a resource to a different folder.
261
+ Remove markings from a resource.
180
262
 
181
263
  Args:
182
264
  resource_rid: Resource Identifier
183
- target_folder_rid: Target folder Resource Identifier
265
+ marking_ids: List of marking identifiers to remove
184
266
 
185
- Returns:
186
- Updated resource information
267
+ Raises:
268
+ RuntimeError: If removing markings fails
187
269
  """
188
270
  try:
189
- resource = self.service.Resource.move(
190
- resource_rid=resource_rid,
191
- body={"target_folder_rid": target_folder_rid},
192
- preview=True,
271
+ self.service.Resource.remove_markings(
272
+ resource_rid, marking_ids=marking_ids, preview=True
193
273
  )
194
- return self._format_resource_info(resource)
195
274
  except Exception as e:
196
- raise RuntimeError(f"Failed to move resource {resource_rid}: {e}")
275
+ raise RuntimeError(
276
+ f"Failed to remove markings from resource {resource_rid}: {e}"
277
+ )
197
278
 
198
- def search_resources(
279
+ def list_markings(
199
280
  self,
200
- query: str,
201
- resource_type: Optional[str] = None,
202
- folder_rid: Optional[str] = None,
281
+ resource_rid: str,
203
282
  page_size: Optional[int] = None,
204
283
  page_token: Optional[str] = None,
205
284
  ) -> List[Dict[str, Any]]:
206
285
  """
207
- Search for resources by query string.
286
+ List markings directly applied to a resource.
208
287
 
209
288
  Args:
210
- query: Search query string
211
- resource_type: Resource type to filter by (optional)
212
- folder_rid: Folder to search within (optional)
289
+ resource_rid: Resource Identifier
213
290
  page_size: Number of items per page (optional)
214
291
  page_token: Pagination token (optional)
215
292
 
216
293
  Returns:
217
- List of matching resource information dictionaries
294
+ List of marking information dictionaries
218
295
  """
219
296
  try:
220
- resources = []
221
- search_params: Dict[str, Any] = {
222
- "query": query,
223
- "preview": True,
224
- }
297
+ markings = []
298
+ list_params: Dict[str, Any] = {"preview": True}
225
299
 
226
- if resource_type:
227
- search_params["resource_type"] = resource_type
228
- if folder_rid:
229
- search_params["folder_rid"] = folder_rid
230
300
  if page_size:
231
- search_params["page_size"] = page_size
301
+ list_params["page_size"] = page_size
232
302
  if page_token:
233
- search_params["page_token"] = page_token
303
+ list_params["page_token"] = page_token
234
304
 
235
- # The search method returns an iterator
236
- for resource in self.service.Resource.search(**search_params):
305
+ for marking in self.service.Resource.markings(resource_rid, **list_params):
306
+ markings.append(self._format_marking_info(marking))
307
+ return markings
308
+ except Exception as e:
309
+ raise RuntimeError(
310
+ f"Failed to list markings for resource {resource_rid}: {e}"
311
+ )
312
+
313
+ # ==================== Access & Batch Operations ====================
314
+
315
+ def get_access_requirements(self, resource_rid: str) -> Dict[str, Any]:
316
+ """
317
+ Get access requirements for a resource.
318
+
319
+ Returns the Organizations and Markings required to view the resource.
320
+
321
+ Args:
322
+ resource_rid: Resource Identifier
323
+
324
+ Returns:
325
+ Access requirements dictionary with organizations and markings
326
+ """
327
+ try:
328
+ requirements = self.service.Resource.get_access_requirements(
329
+ resource_rid, preview=True
330
+ )
331
+ return self._format_access_requirements(requirements)
332
+ except Exception as e:
333
+ raise RuntimeError(
334
+ f"Failed to get access requirements for resource {resource_rid}: {e}"
335
+ )
336
+
337
+ def get_resources_by_path_batch(self, paths: List[str]) -> List[Dict[str, Any]]:
338
+ """
339
+ Get multiple resources by their absolute paths in a single request.
340
+
341
+ Args:
342
+ paths: List of absolute paths (max 1000)
343
+
344
+ Returns:
345
+ List of resource information dictionaries
346
+ """
347
+ if len(paths) > 1000:
348
+ raise ValueError("Maximum batch size is 1000 paths")
349
+
350
+ try:
351
+ elements = [GetByPathResourcesBatchRequestElement(path=p) for p in paths]
352
+ response = self.service.Resource.get_by_path_batch(
353
+ body=elements, preview=True
354
+ )
355
+ resources = []
356
+ for resource in response.resources:
237
357
  resources.append(self._format_resource_info(resource))
238
358
  return resources
239
359
  except Exception as e:
240
- raise RuntimeError(f"Failed to search resources: {e}")
360
+ raise RuntimeError(f"Failed to get resources by path batch: {e}")
241
361
 
242
362
  def _format_resource_info(self, resource: Any) -> Dict[str, Any]:
243
363
  """
@@ -303,3 +423,52 @@ class ResourceService(BaseService):
303
423
  if hasattr(timestamp, "time"):
304
424
  return str(timestamp.time)
305
425
  return str(timestamp)
426
+
427
+ def _format_marking_info(self, marking: Any) -> Dict[str, Any]:
428
+ """
429
+ Format marking information for consistent output.
430
+
431
+ Args:
432
+ marking: Marking object from Foundry SDK
433
+
434
+ Returns:
435
+ Formatted marking information dictionary
436
+ """
437
+ return {
438
+ "marking_id": getattr(marking, "marking_id", None),
439
+ "display_name": getattr(marking, "display_name", None),
440
+ "description": getattr(marking, "description", None),
441
+ "category_id": getattr(marking, "category_id", None),
442
+ "category_display_name": getattr(marking, "category_display_name", None),
443
+ }
444
+
445
+ def _format_access_requirements(self, requirements: Any) -> Dict[str, Any]:
446
+ """
447
+ Format access requirements for consistent output.
448
+
449
+ Args:
450
+ requirements: AccessRequirements object from Foundry SDK
451
+
452
+ Returns:
453
+ Formatted access requirements dictionary
454
+ """
455
+ organizations = []
456
+ markings = []
457
+
458
+ if hasattr(requirements, "organizations"):
459
+ for org in getattr(requirements, "organizations", []) or []:
460
+ organizations.append(
461
+ {
462
+ "organization_rid": getattr(org, "organization_rid", None),
463
+ "display_name": getattr(org, "display_name", None),
464
+ }
465
+ )
466
+
467
+ if hasattr(requirements, "markings"):
468
+ for marking in getattr(requirements, "markings", []) or []:
469
+ markings.append(self._format_marking_info(marking))
470
+
471
+ return {
472
+ "organizations": organizations,
473
+ "markings": markings,
474
+ }