pltr-cli 0.5.0__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pltr/__init__.py +1 -1
- pltr/cli.py +14 -0
- pltr/commands/connectivity.py +432 -0
- pltr/commands/dataset.py +268 -0
- pltr/commands/project.py +440 -0
- pltr/commands/resource.py +499 -0
- pltr/commands/resource_role.py +454 -0
- pltr/commands/space.py +662 -0
- pltr/services/connectivity.py +305 -0
- pltr/services/dataset.py +243 -8
- pltr/services/project.py +232 -0
- pltr/services/resource.py +289 -0
- pltr/services/resource_role.py +321 -0
- pltr/services/space.py +354 -0
- pltr/utils/formatting.py +108 -1
- {pltr_cli-0.5.0.dist-info → pltr_cli-0.5.1.dist-info}/METADATA +101 -2
- {pltr_cli-0.5.0.dist-info → pltr_cli-0.5.1.dist-info}/RECORD +20 -10
- {pltr_cli-0.5.0.dist-info → pltr_cli-0.5.1.dist-info}/WHEEL +0 -0
- {pltr_cli-0.5.0.dist-info → pltr_cli-0.5.1.dist-info}/entry_points.txt +0 -0
- {pltr_cli-0.5.0.dist-info → pltr_cli-0.5.1.dist-info}/licenses/LICENSE +0 -0
pltr/services/project.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project service wrapper for Foundry SDK filesystem API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional, Dict, List
|
|
6
|
+
|
|
7
|
+
from .base import BaseService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProjectService(BaseService):
|
|
11
|
+
"""Service wrapper for Foundry project operations using filesystem API."""
|
|
12
|
+
|
|
13
|
+
def _get_service(self) -> Any:
|
|
14
|
+
"""Get the Foundry filesystem service."""
|
|
15
|
+
return self.client.filesystem
|
|
16
|
+
|
|
17
|
+
def create_project(
|
|
18
|
+
self,
|
|
19
|
+
display_name: str,
|
|
20
|
+
space_rid: str,
|
|
21
|
+
description: Optional[str] = None,
|
|
22
|
+
organization_rids: Optional[List[str]] = None,
|
|
23
|
+
default_roles: Optional[List[str]] = None,
|
|
24
|
+
role_grants: Optional[List[Dict[str, Any]]] = None,
|
|
25
|
+
) -> Dict[str, Any]:
|
|
26
|
+
"""
|
|
27
|
+
Create a new project.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
display_name: Project display name (cannot contain '/')
|
|
31
|
+
space_rid: Space Resource Identifier where project will be created
|
|
32
|
+
description: Project description (optional)
|
|
33
|
+
organization_rids: List of organization RIDs (optional)
|
|
34
|
+
default_roles: List of default role names (optional)
|
|
35
|
+
role_grants: List of role grant specifications (optional)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Created project information
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
# Prepare the create request payload
|
|
42
|
+
create_request: Dict[str, Any] = {
|
|
43
|
+
"display_name": display_name,
|
|
44
|
+
"space_rid": space_rid,
|
|
45
|
+
}
|
|
46
|
+
|
|
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
|
+
)
|
|
60
|
+
return self._format_project_info(project)
|
|
61
|
+
except Exception as e:
|
|
62
|
+
raise RuntimeError(f"Failed to create project '{display_name}': {e}")
|
|
63
|
+
|
|
64
|
+
def get_project(self, project_rid: str) -> Dict[str, Any]:
|
|
65
|
+
"""
|
|
66
|
+
Get information about a specific project.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
project_rid: Project Resource Identifier
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Project information dictionary
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
project = self.service.Project.get(project_rid, preview=True)
|
|
76
|
+
return self._format_project_info(project)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
raise RuntimeError(f"Failed to get project {project_rid}: {e}")
|
|
79
|
+
|
|
80
|
+
def list_projects(
|
|
81
|
+
self,
|
|
82
|
+
space_rid: Optional[str] = None,
|
|
83
|
+
page_size: Optional[int] = None,
|
|
84
|
+
page_token: Optional[str] = None,
|
|
85
|
+
) -> List[Dict[str, Any]]:
|
|
86
|
+
"""
|
|
87
|
+
List projects, optionally filtered by space.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
space_rid: Space Resource Identifier to filter by (optional)
|
|
91
|
+
page_size: Number of items per page (optional)
|
|
92
|
+
page_token: Pagination token (optional)
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
List of project information dictionaries
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
projects = []
|
|
99
|
+
list_params: Dict[str, Any] = {"preview": True}
|
|
100
|
+
|
|
101
|
+
if space_rid:
|
|
102
|
+
list_params["space_rid"] = space_rid
|
|
103
|
+
if page_size:
|
|
104
|
+
list_params["page_size"] = page_size
|
|
105
|
+
if page_token:
|
|
106
|
+
list_params["page_token"] = page_token
|
|
107
|
+
|
|
108
|
+
# The list method returns an iterator
|
|
109
|
+
for project in self.service.Project.list(**list_params):
|
|
110
|
+
projects.append(self._format_project_info(project))
|
|
111
|
+
return projects
|
|
112
|
+
except Exception as e:
|
|
113
|
+
raise RuntimeError(f"Failed to list projects: {e}")
|
|
114
|
+
|
|
115
|
+
def delete_project(self, project_rid: str) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Delete a project.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
project_rid: Project Resource Identifier
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
RuntimeError: If deletion fails
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
self.service.Project.delete(project_rid, preview=True)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise RuntimeError(f"Failed to delete project {project_rid}: {e}")
|
|
129
|
+
|
|
130
|
+
def update_project(
|
|
131
|
+
self,
|
|
132
|
+
project_rid: str,
|
|
133
|
+
display_name: Optional[str] = None,
|
|
134
|
+
description: Optional[str] = None,
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
"""
|
|
137
|
+
Update project information.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
project_rid: Project Resource Identifier
|
|
141
|
+
display_name: New display name (optional)
|
|
142
|
+
description: New description (optional)
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Updated project information
|
|
146
|
+
"""
|
|
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:
|
|
154
|
+
raise ValueError("At least one field must be provided for update")
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
project = self.service.Project.update(
|
|
158
|
+
project_rid=project_rid,
|
|
159
|
+
body=update_request,
|
|
160
|
+
preview=True,
|
|
161
|
+
)
|
|
162
|
+
return self._format_project_info(project)
|
|
163
|
+
except Exception as e:
|
|
164
|
+
raise RuntimeError(f"Failed to update project {project_rid}: {e}")
|
|
165
|
+
|
|
166
|
+
def get_projects_batch(self, project_rids: List[str]) -> List[Dict[str, Any]]:
|
|
167
|
+
"""
|
|
168
|
+
Get multiple projects in a single request.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
project_rids: List of project RIDs (max 1000)
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
List of project information dictionaries
|
|
175
|
+
"""
|
|
176
|
+
if len(project_rids) > 1000:
|
|
177
|
+
raise ValueError("Maximum batch size is 1000 projects")
|
|
178
|
+
|
|
179
|
+
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
|
|
185
|
+
except Exception as e:
|
|
186
|
+
raise RuntimeError(f"Failed to get projects batch: {e}")
|
|
187
|
+
|
|
188
|
+
def _format_project_info(self, project: Any) -> Dict[str, Any]:
|
|
189
|
+
"""
|
|
190
|
+
Format project information for consistent output.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
project: Project object from Foundry SDK
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Formatted project information dictionary
|
|
197
|
+
"""
|
|
198
|
+
return {
|
|
199
|
+
"rid": getattr(project, "rid", None),
|
|
200
|
+
"display_name": getattr(project, "display_name", None),
|
|
201
|
+
"description": getattr(project, "description", None),
|
|
202
|
+
"path": getattr(project, "path", None),
|
|
203
|
+
"space_rid": getattr(project, "space_rid", None),
|
|
204
|
+
"created_by": getattr(project, "created_by", None),
|
|
205
|
+
"created_time": self._format_timestamp(
|
|
206
|
+
getattr(project, "created_time", None)
|
|
207
|
+
),
|
|
208
|
+
"modified_by": getattr(project, "modified_by", None),
|
|
209
|
+
"modified_time": self._format_timestamp(
|
|
210
|
+
getattr(project, "modified_time", None)
|
|
211
|
+
),
|
|
212
|
+
"trash_status": getattr(project, "trash_status", None),
|
|
213
|
+
"type": "project",
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
def _format_timestamp(self, timestamp: Any) -> Optional[str]:
|
|
217
|
+
"""
|
|
218
|
+
Format timestamp for display.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
timestamp: Timestamp object from SDK
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Formatted timestamp string or None
|
|
225
|
+
"""
|
|
226
|
+
if timestamp is None:
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
# Handle different timestamp formats from the SDK
|
|
230
|
+
if hasattr(timestamp, "time"):
|
|
231
|
+
return str(timestamp.time)
|
|
232
|
+
return str(timestamp)
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Resource service wrapper for Foundry SDK filesystem API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional, Dict, List
|
|
6
|
+
|
|
7
|
+
from .base import BaseService
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ResourceService(BaseService):
|
|
11
|
+
"""Service wrapper for Foundry resource operations using filesystem API."""
|
|
12
|
+
|
|
13
|
+
def _get_service(self) -> Any:
|
|
14
|
+
"""Get the Foundry filesystem service."""
|
|
15
|
+
return self.client.filesystem
|
|
16
|
+
|
|
17
|
+
def get_resource(self, resource_rid: str) -> Dict[str, Any]:
|
|
18
|
+
"""
|
|
19
|
+
Get information about a specific resource.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
resource_rid: Resource Identifier
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Resource information dictionary
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
resource = self.service.Resource.get(resource_rid, preview=True)
|
|
29
|
+
return self._format_resource_info(resource)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
raise RuntimeError(f"Failed to get resource {resource_rid}: {e}")
|
|
32
|
+
|
|
33
|
+
def list_resources(
|
|
34
|
+
self,
|
|
35
|
+
folder_rid: Optional[str] = None,
|
|
36
|
+
resource_type: Optional[str] = None,
|
|
37
|
+
page_size: Optional[int] = None,
|
|
38
|
+
page_token: Optional[str] = None,
|
|
39
|
+
) -> List[Dict[str, Any]]:
|
|
40
|
+
"""
|
|
41
|
+
List resources, optionally filtered by folder and type.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
folder_rid: Folder Resource Identifier to filter by (optional)
|
|
45
|
+
resource_type: Resource type to filter by (optional)
|
|
46
|
+
page_size: Number of items per page (optional)
|
|
47
|
+
page_token: Pagination token (optional)
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
List of resource information dictionaries
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
resources = []
|
|
54
|
+
list_params: Dict[str, Any] = {"preview": True}
|
|
55
|
+
|
|
56
|
+
if folder_rid:
|
|
57
|
+
list_params["folder_rid"] = folder_rid
|
|
58
|
+
if resource_type:
|
|
59
|
+
list_params["resource_type"] = resource_type
|
|
60
|
+
if page_size:
|
|
61
|
+
list_params["page_size"] = page_size
|
|
62
|
+
if page_token:
|
|
63
|
+
list_params["page_token"] = page_token
|
|
64
|
+
|
|
65
|
+
# The list method returns an iterator
|
|
66
|
+
for resource in self.service.Resource.list(**list_params):
|
|
67
|
+
resources.append(self._format_resource_info(resource))
|
|
68
|
+
return resources
|
|
69
|
+
except Exception as e:
|
|
70
|
+
raise RuntimeError(f"Failed to list resources: {e}")
|
|
71
|
+
|
|
72
|
+
def get_resources_batch(self, resource_rids: List[str]) -> List[Dict[str, Any]]:
|
|
73
|
+
"""
|
|
74
|
+
Get multiple resources in a single request.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
resource_rids: List of resource RIDs (max 1000)
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
List of resource information dictionaries
|
|
81
|
+
"""
|
|
82
|
+
if len(resource_rids) > 1000:
|
|
83
|
+
raise ValueError("Maximum batch size is 1000 resources")
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
response = self.service.Resource.get_batch(body=resource_rids, preview=True)
|
|
87
|
+
resources = []
|
|
88
|
+
for resource in response.resources:
|
|
89
|
+
resources.append(self._format_resource_info(resource))
|
|
90
|
+
return resources
|
|
91
|
+
except Exception as e:
|
|
92
|
+
raise RuntimeError(f"Failed to get resources batch: {e}")
|
|
93
|
+
|
|
94
|
+
def get_resource_metadata(self, resource_rid: str) -> Dict[str, Any]:
|
|
95
|
+
"""
|
|
96
|
+
Get metadata for a specific resource.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
resource_rid: Resource Identifier
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Resource metadata dictionary
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
metadata = self.service.Resource.get_metadata(resource_rid, preview=True)
|
|
106
|
+
return self._format_metadata(metadata)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
raise RuntimeError(
|
|
109
|
+
f"Failed to get metadata for resource {resource_rid}: {e}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def set_resource_metadata(
|
|
113
|
+
self, resource_rid: str, metadata: Dict[str, Any]
|
|
114
|
+
) -> Dict[str, Any]:
|
|
115
|
+
"""
|
|
116
|
+
Set metadata for a specific resource.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
resource_rid: Resource Identifier
|
|
120
|
+
metadata: Metadata dictionary to set
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Updated resource metadata
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
updated_metadata = self.service.Resource.set_metadata(
|
|
127
|
+
resource_rid=resource_rid,
|
|
128
|
+
body=metadata,
|
|
129
|
+
preview=True,
|
|
130
|
+
)
|
|
131
|
+
return self._format_metadata(updated_metadata)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
raise RuntimeError(
|
|
134
|
+
f"Failed to set metadata for resource {resource_rid}: {e}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def delete_resource_metadata(self, resource_rid: str, keys: List[str]) -> None:
|
|
138
|
+
"""
|
|
139
|
+
Delete specific metadata keys for a resource.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
resource_rid: Resource Identifier
|
|
143
|
+
keys: List of metadata keys to delete
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
RuntimeError: If deletion fails
|
|
147
|
+
"""
|
|
148
|
+
try:
|
|
149
|
+
self.service.Resource.delete_metadata(
|
|
150
|
+
resource_rid=resource_rid,
|
|
151
|
+
body={"keys": keys},
|
|
152
|
+
preview=True,
|
|
153
|
+
)
|
|
154
|
+
except Exception as e:
|
|
155
|
+
raise RuntimeError(
|
|
156
|
+
f"Failed to delete metadata for resource {resource_rid}: {e}"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def move_resource(
|
|
160
|
+
self, resource_rid: str, target_folder_rid: str
|
|
161
|
+
) -> Dict[str, Any]:
|
|
162
|
+
"""
|
|
163
|
+
Move a resource to a different folder.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
resource_rid: Resource Identifier
|
|
167
|
+
target_folder_rid: Target folder Resource Identifier
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Updated resource information
|
|
171
|
+
"""
|
|
172
|
+
try:
|
|
173
|
+
resource = self.service.Resource.move(
|
|
174
|
+
resource_rid=resource_rid,
|
|
175
|
+
body={"target_folder_rid": target_folder_rid},
|
|
176
|
+
preview=True,
|
|
177
|
+
)
|
|
178
|
+
return self._format_resource_info(resource)
|
|
179
|
+
except Exception as e:
|
|
180
|
+
raise RuntimeError(f"Failed to move resource {resource_rid}: {e}")
|
|
181
|
+
|
|
182
|
+
def search_resources(
|
|
183
|
+
self,
|
|
184
|
+
query: str,
|
|
185
|
+
resource_type: Optional[str] = None,
|
|
186
|
+
folder_rid: Optional[str] = None,
|
|
187
|
+
page_size: Optional[int] = None,
|
|
188
|
+
page_token: Optional[str] = None,
|
|
189
|
+
) -> List[Dict[str, Any]]:
|
|
190
|
+
"""
|
|
191
|
+
Search for resources by query string.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
query: Search query string
|
|
195
|
+
resource_type: Resource type to filter by (optional)
|
|
196
|
+
folder_rid: Folder to search within (optional)
|
|
197
|
+
page_size: Number of items per page (optional)
|
|
198
|
+
page_token: Pagination token (optional)
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of matching resource information dictionaries
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
resources = []
|
|
205
|
+
search_params: Dict[str, Any] = {
|
|
206
|
+
"query": query,
|
|
207
|
+
"preview": True,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if resource_type:
|
|
211
|
+
search_params["resource_type"] = resource_type
|
|
212
|
+
if folder_rid:
|
|
213
|
+
search_params["folder_rid"] = folder_rid
|
|
214
|
+
if page_size:
|
|
215
|
+
search_params["page_size"] = page_size
|
|
216
|
+
if page_token:
|
|
217
|
+
search_params["page_token"] = page_token
|
|
218
|
+
|
|
219
|
+
# The search method returns an iterator
|
|
220
|
+
for resource in self.service.Resource.search(**search_params):
|
|
221
|
+
resources.append(self._format_resource_info(resource))
|
|
222
|
+
return resources
|
|
223
|
+
except Exception as e:
|
|
224
|
+
raise RuntimeError(f"Failed to search resources: {e}")
|
|
225
|
+
|
|
226
|
+
def _format_resource_info(self, resource: Any) -> Dict[str, Any]:
|
|
227
|
+
"""
|
|
228
|
+
Format resource information for consistent output.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
resource: Resource object from Foundry SDK
|
|
232
|
+
|
|
233
|
+
Returns:
|
|
234
|
+
Formatted resource information dictionary
|
|
235
|
+
"""
|
|
236
|
+
return {
|
|
237
|
+
"rid": getattr(resource, "rid", None),
|
|
238
|
+
"display_name": getattr(resource, "display_name", None),
|
|
239
|
+
"name": getattr(resource, "name", None),
|
|
240
|
+
"description": getattr(resource, "description", None),
|
|
241
|
+
"path": getattr(resource, "path", None),
|
|
242
|
+
"type": getattr(resource, "type", None),
|
|
243
|
+
"folder_rid": getattr(resource, "folder_rid", None),
|
|
244
|
+
"created_by": getattr(resource, "created_by", None),
|
|
245
|
+
"created_time": self._format_timestamp(
|
|
246
|
+
getattr(resource, "created_time", None)
|
|
247
|
+
),
|
|
248
|
+
"modified_by": getattr(resource, "modified_by", None),
|
|
249
|
+
"modified_time": self._format_timestamp(
|
|
250
|
+
getattr(resource, "modified_time", None)
|
|
251
|
+
),
|
|
252
|
+
"size_bytes": getattr(resource, "size_bytes", None),
|
|
253
|
+
"trash_status": getattr(resource, "trash_status", None),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
def _format_metadata(self, metadata: Any) -> Dict[str, Any]:
|
|
257
|
+
"""
|
|
258
|
+
Format metadata for consistent output.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
metadata: Metadata object from Foundry SDK
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
Formatted metadata dictionary
|
|
265
|
+
"""
|
|
266
|
+
if hasattr(metadata, "__dict__"):
|
|
267
|
+
return dict(metadata.__dict__)
|
|
268
|
+
elif isinstance(metadata, dict):
|
|
269
|
+
return metadata
|
|
270
|
+
else:
|
|
271
|
+
return {"raw": str(metadata)}
|
|
272
|
+
|
|
273
|
+
def _format_timestamp(self, timestamp: Any) -> Optional[str]:
|
|
274
|
+
"""
|
|
275
|
+
Format timestamp for display.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
timestamp: Timestamp object from SDK
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Formatted timestamp string or None
|
|
282
|
+
"""
|
|
283
|
+
if timestamp is None:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
# Handle different timestamp formats from the SDK
|
|
287
|
+
if hasattr(timestamp, "time"):
|
|
288
|
+
return str(timestamp.time)
|
|
289
|
+
return str(timestamp)
|