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.
- pltr/__init__.py +1 -1
- pltr/cli.py +40 -0
- pltr/commands/admin.py +565 -11
- 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/functions.py +503 -0
- pltr/commands/language_models.py +515 -0
- pltr/commands/mediasets.py +176 -0
- pltr/commands/models.py +362 -0
- pltr/commands/ontology.py +44 -13
- pltr/commands/orchestration.py +167 -11
- pltr/commands/project.py +231 -22
- pltr/commands/resource.py +416 -17
- pltr/commands/space.py +25 -303
- pltr/commands/sql.py +54 -7
- pltr/commands/streams.py +616 -0
- pltr/commands/third_party_applications.py +82 -0
- pltr/services/admin.py +331 -3
- 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 +77 -4
- pltr/services/folder.py +6 -1
- pltr/services/functions.py +223 -0
- pltr/services/language_models.py +281 -0
- pltr/services/mediasets.py +144 -9
- pltr/services/models.py +179 -0
- pltr/services/ontology.py +48 -1
- pltr/services/orchestration.py +133 -1
- pltr/services/project.py +213 -39
- pltr/services/resource.py +229 -60
- pltr/services/space.py +24 -175
- pltr/services/sql.py +44 -20
- pltr/services/streams.py +290 -0
- pltr/services/third_party_applications.py +53 -0
- pltr/utils/formatting.py +195 -1
- pltr/utils/pagination.py +325 -0
- {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/METADATA +55 -4
- pltr_cli-0.13.0.dist-info/RECORD +70 -0
- {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/WHEEL +1 -1
- pltr_cli-0.11.0.dist-info/RECORD +0 -55
- {pltr_cli-0.11.0.dist-info → pltr_cli-0.13.0.dist-info}/entry_points.txt +0 -0
- {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[
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
285
|
+
List of organization information dictionaries
|
|
175
286
|
"""
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
129
|
-
self,
|
|
130
|
-
|
|
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
|
-
|
|
146
|
+
Search for resources by query string.
|
|
133
147
|
|
|
134
148
|
Args:
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
156
|
+
List of matching resource information dictionaries
|
|
140
157
|
"""
|
|
141
158
|
try:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
preview
|
|
146
|
-
|
|
147
|
-
|
|
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
|
|
234
|
+
f"Failed to permanently delete resource {resource_rid}: {e}"
|
|
151
235
|
)
|
|
152
236
|
|
|
153
|
-
|
|
237
|
+
# ==================== Markings Operations ====================
|
|
238
|
+
|
|
239
|
+
def add_markings(self, resource_rid: str, marking_ids: List[str]) -> None:
|
|
154
240
|
"""
|
|
155
|
-
|
|
241
|
+
Add markings to a resource.
|
|
156
242
|
|
|
157
243
|
Args:
|
|
158
244
|
resource_rid: Resource Identifier
|
|
159
|
-
|
|
245
|
+
marking_ids: List of marking identifiers to add
|
|
160
246
|
|
|
161
247
|
Raises:
|
|
162
|
-
RuntimeError: If
|
|
248
|
+
RuntimeError: If adding markings fails
|
|
163
249
|
"""
|
|
164
250
|
try:
|
|
165
|
-
self.service.Resource.
|
|
166
|
-
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
|
|
256
|
+
f"Failed to add markings to resource {resource_rid}: {e}"
|
|
173
257
|
)
|
|
174
258
|
|
|
175
|
-
def
|
|
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
|
-
|
|
261
|
+
Remove markings from a resource.
|
|
180
262
|
|
|
181
263
|
Args:
|
|
182
264
|
resource_rid: Resource Identifier
|
|
183
|
-
|
|
265
|
+
marking_ids: List of marking identifiers to remove
|
|
184
266
|
|
|
185
|
-
|
|
186
|
-
|
|
267
|
+
Raises:
|
|
268
|
+
RuntimeError: If removing markings fails
|
|
187
269
|
"""
|
|
188
270
|
try:
|
|
189
|
-
|
|
190
|
-
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(
|
|
275
|
+
raise RuntimeError(
|
|
276
|
+
f"Failed to remove markings from resource {resource_rid}: {e}"
|
|
277
|
+
)
|
|
197
278
|
|
|
198
|
-
def
|
|
279
|
+
def list_markings(
|
|
199
280
|
self,
|
|
200
|
-
|
|
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
|
-
|
|
286
|
+
List markings directly applied to a resource.
|
|
208
287
|
|
|
209
288
|
Args:
|
|
210
|
-
|
|
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
|
|
294
|
+
List of marking information dictionaries
|
|
218
295
|
"""
|
|
219
296
|
try:
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
301
|
+
list_params["page_size"] = page_size
|
|
232
302
|
if page_token:
|
|
233
|
-
|
|
303
|
+
list_params["page_token"] = page_token
|
|
234
304
|
|
|
235
|
-
|
|
236
|
-
|
|
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
|
|
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
|
+
}
|