collibra-connector 1.0.7__py3-none-any.whl → 1.0.10a0__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.
- collibra_connector/__init__.py +21 -21
- collibra_connector/api/Asset.py +218 -19
- collibra_connector/api/Base.py +92 -11
- collibra_connector/api/Comment.py +125 -0
- collibra_connector/api/Community.py +112 -0
- collibra_connector/api/Domain.py +116 -0
- collibra_connector/api/Exceptions.py +5 -5
- collibra_connector/api/Metadata.py +120 -0
- collibra_connector/api/Responsibility.py +303 -0
- collibra_connector/api/User.py +203 -0
- collibra_connector/api/Workflow.py +260 -0
- collibra_connector/api/__init__.py +14 -0
- collibra_connector/connector.py +23 -3
- collibra_connector-1.0.10a0.dist-info/METADATA +157 -0
- collibra_connector-1.0.10a0.dist-info/RECORD +18 -0
- collibra_connector-1.0.7.dist-info/METADATA +0 -27
- collibra_connector-1.0.7.dist-info/RECORD +0 -11
- {collibra_connector-1.0.7.dist-info → collibra_connector-1.0.10a0.dist-info}/WHEEL +0 -0
- {collibra_connector-1.0.7.dist-info → collibra_connector-1.0.10a0.dist-info}/licenses/LICENSE +0 -0
- {collibra_connector-1.0.7.dist-info → collibra_connector-1.0.10a0.dist-info}/top_level.txt +0 -0
collibra_connector/__init__.py
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
#
|
|
2
2
|
#
|
|
3
|
-
# ███
|
|
4
|
-
# ██ ███ ███
|
|
5
|
-
# ████ ███
|
|
6
|
-
# ███ ████ ██ ███ ████
|
|
7
|
-
# ███ ████ ███ ███ ████
|
|
8
|
-
# ███ ███
|
|
9
|
-
# ███ ███
|
|
10
|
-
# ██████████████ ███ ███ ████████
|
|
11
|
-
# ███████████ █ ███ ██████
|
|
3
|
+
# ███
|
|
4
|
+
# ██ ███ ███
|
|
5
|
+
# ████ ███
|
|
6
|
+
# ███ ████ ██ ███ ████
|
|
7
|
+
# ███ ████ ███ ███ ████
|
|
8
|
+
# ███ ███
|
|
9
|
+
# ███ ███
|
|
10
|
+
# ██████████████ ███ ███ ████████
|
|
11
|
+
# ███████████ █ ███ ██████
|
|
12
12
|
#
|
|
13
|
-
# ███ █████████████ ████████████ ███
|
|
14
|
-
# ███ █████████████ ████████████ ███
|
|
13
|
+
# ███ █████████████ ████████████ ███
|
|
14
|
+
# ███ █████████████ ████████████ ███
|
|
15
15
|
#
|
|
16
|
-
# ██████ █ █████████████
|
|
17
|
-
# ███████ ████ ███ ██████████████
|
|
18
|
-
# ████ ███
|
|
19
|
-
# ████ ███
|
|
20
|
-
# ███ ████ ███ ███ ████
|
|
21
|
-
# ██ ████ ██ ███ ██
|
|
22
|
-
# ████ ███
|
|
23
|
-
# ██ ███ ███
|
|
24
|
-
# █
|
|
16
|
+
# ██████ █ █████████████
|
|
17
|
+
# ███████ ████ ███ ██████████████
|
|
18
|
+
# ████ ███
|
|
19
|
+
# ████ ███
|
|
20
|
+
# ███ ████ ███ ███ ████
|
|
21
|
+
# ██ ████ ██ ███ ██
|
|
22
|
+
# ████ ███
|
|
23
|
+
# ██ ███ ███
|
|
24
|
+
# █
|
|
25
25
|
#
|
|
26
26
|
#
|
|
27
27
|
"""
|
|
@@ -36,4 +36,4 @@ This library provides a simple interface to handle connection and URLs
|
|
|
36
36
|
|
|
37
37
|
from .connector import CollibraConnector
|
|
38
38
|
|
|
39
|
-
__version__ = "0.1.0"
|
|
39
|
+
__version__ = "0.1.0"
|
collibra_connector/api/Asset.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import uuid
|
|
1
2
|
from .Base import BaseAPI
|
|
2
3
|
|
|
4
|
+
|
|
3
5
|
class Asset(BaseAPI):
|
|
4
6
|
def __init__(self, connector):
|
|
5
7
|
super().__init__(connector)
|
|
6
8
|
self.__base_api = connector.api + "/assets"
|
|
7
9
|
|
|
8
|
-
def
|
|
10
|
+
def _get(self, url: str = None, params: dict = None, headers: dict = None):
|
|
9
11
|
"""
|
|
10
12
|
Makes a GET request to the asset API.
|
|
11
13
|
:param url: The URL to send the GET request to.
|
|
@@ -13,28 +15,16 @@ class Asset(BaseAPI):
|
|
|
13
15
|
:param headers: Optional headers to include in the GET request.
|
|
14
16
|
:return: The response from the GET request.
|
|
15
17
|
"""
|
|
16
|
-
return super().
|
|
17
|
-
|
|
18
|
-
def __post(self, url: str, data: dict):
|
|
19
|
-
"""
|
|
20
|
-
Makes a POST request to the asset API.
|
|
21
|
-
:param url: The URL to send the POST request to.
|
|
22
|
-
:param data: The data to send in the POST request.
|
|
23
|
-
:return: The response from the POST request.
|
|
24
|
-
"""
|
|
25
|
-
return super().__post(url, data)
|
|
18
|
+
return super()._get(self.__base_api if not url else url, params, headers)
|
|
26
19
|
|
|
27
|
-
def __handle_response(self, response):
|
|
28
|
-
return super().__handle_response(response)
|
|
29
|
-
|
|
30
20
|
def get_asset(self, asset_id):
|
|
31
21
|
"""
|
|
32
22
|
Retrieves an asset by its ID.
|
|
33
23
|
:param asset_id: The ID of the asset to retrieve.
|
|
34
24
|
:return: Asset details.
|
|
35
25
|
"""
|
|
36
|
-
response = self.
|
|
37
|
-
return self.
|
|
26
|
+
response = self._get(url=f"{self.__base_api}/{asset_id}")
|
|
27
|
+
return self._handle_response(response)
|
|
38
28
|
|
|
39
29
|
def add_asset(
|
|
40
30
|
self,
|
|
@@ -42,7 +32,7 @@ class Asset(BaseAPI):
|
|
|
42
32
|
domain_id: str,
|
|
43
33
|
display_name: str = None,
|
|
44
34
|
type_id: str = None,
|
|
45
|
-
|
|
35
|
+
_id: str = None,
|
|
46
36
|
status_id: str = None,
|
|
47
37
|
excluded_from_auto_hyperlink: bool = False,
|
|
48
38
|
type_public_id: str = None,
|
|
@@ -59,6 +49,30 @@ class Asset(BaseAPI):
|
|
|
59
49
|
:param type_public_id: Optional public ID for the asset type.
|
|
60
50
|
:return: Details of the created asset.
|
|
61
51
|
"""
|
|
52
|
+
# Parameter type validation
|
|
53
|
+
if not name or not domain_id:
|
|
54
|
+
raise ValueError("Name and domain_id are required parameters.")
|
|
55
|
+
if not isinstance(excluded_from_auto_hyperlink, bool):
|
|
56
|
+
raise ValueError("excluded_from_auto_hyperlink must be a boolean value.")
|
|
57
|
+
if type_id and not isinstance(type_id, str):
|
|
58
|
+
raise ValueError("type_id must be a string if provided.")
|
|
59
|
+
if _id and not isinstance(_id, str):
|
|
60
|
+
raise ValueError("_id must be a string if provided.")
|
|
61
|
+
if status_id and not isinstance(status_id, str):
|
|
62
|
+
raise ValueError("status_id must be a string if provided.")
|
|
63
|
+
if type_public_id and not isinstance(type_public_id, str):
|
|
64
|
+
raise ValueError("type_public_id must be a string if provided.")
|
|
65
|
+
|
|
66
|
+
# Check Ids are UUIDS
|
|
67
|
+
if _id and self._uuid_validation(_id) is False:
|
|
68
|
+
raise ValueError("id must be a valid UUID.")
|
|
69
|
+
if domain_id and self._uuid_validation(domain_id) is False:
|
|
70
|
+
raise ValueError("domain_id must be a valid UUID.")
|
|
71
|
+
if type_id and self._uuid_validation(type_id) is False:
|
|
72
|
+
raise ValueError("type_id must be a valid UUID.")
|
|
73
|
+
if status_id and self._uuid_validation(status_id) is False:
|
|
74
|
+
raise ValueError("status_id must be a valid UUID.")
|
|
75
|
+
|
|
62
76
|
data = {
|
|
63
77
|
"name": name,
|
|
64
78
|
"domainId": domain_id,
|
|
@@ -69,5 +83,190 @@ class Asset(BaseAPI):
|
|
|
69
83
|
"excludedFromAutoHyperlink": excluded_from_auto_hyperlink,
|
|
70
84
|
"typePublicId": type_public_id
|
|
71
85
|
}
|
|
72
|
-
response = self.
|
|
73
|
-
return self.
|
|
86
|
+
response = self._post(url=self.__base_api, data=data)
|
|
87
|
+
return self._handle_response(response)
|
|
88
|
+
|
|
89
|
+
def update_asset_properties(
|
|
90
|
+
self,
|
|
91
|
+
asset_id: str,
|
|
92
|
+
name: str = None,
|
|
93
|
+
display_name: str = None,
|
|
94
|
+
type_id: str = None,
|
|
95
|
+
status_id: str = None,
|
|
96
|
+
domain_id: str = None,
|
|
97
|
+
excluded_from_auto_hyperlinking: bool = None,
|
|
98
|
+
type_public_id: str = None
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Update asset properties.
|
|
102
|
+
:param asset_id: The ID of the asset to update.
|
|
103
|
+
:param name: Optional new name for the asset.
|
|
104
|
+
:param display_name: Optional new display name.
|
|
105
|
+
:param type_id: Optional new type ID.
|
|
106
|
+
:param status_id: Optional new status ID.
|
|
107
|
+
:param domain_id: Optional new domain ID.
|
|
108
|
+
:param excluded_from_auto_hyperlinking: Optional auto-hyperlinking setting.
|
|
109
|
+
:param type_public_id: Optional new type public ID.
|
|
110
|
+
:return: Updated asset details.
|
|
111
|
+
"""
|
|
112
|
+
if not asset_id:
|
|
113
|
+
raise ValueError("asset_id is required")
|
|
114
|
+
if not isinstance(asset_id, str):
|
|
115
|
+
raise ValueError("asset_id must be a string")
|
|
116
|
+
|
|
117
|
+
try:
|
|
118
|
+
uuid.UUID(asset_id)
|
|
119
|
+
except ValueError as exc:
|
|
120
|
+
raise ValueError("asset_id must be a valid UUID") from exc
|
|
121
|
+
|
|
122
|
+
# Validate UUID fields if provided
|
|
123
|
+
uuid_fields = [
|
|
124
|
+
("type_id", type_id),
|
|
125
|
+
("status_id", status_id),
|
|
126
|
+
("domain_id", domain_id)
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
for field_name, field_value in uuid_fields:
|
|
130
|
+
if field_value:
|
|
131
|
+
if not isinstance(field_value, str):
|
|
132
|
+
raise ValueError(f"{field_name} must be a string")
|
|
133
|
+
try:
|
|
134
|
+
uuid.UUID(field_value)
|
|
135
|
+
except ValueError as exc:
|
|
136
|
+
raise ValueError(f"{field_name} must be a valid UUID") from exc
|
|
137
|
+
|
|
138
|
+
data = {
|
|
139
|
+
"id": asset_id,
|
|
140
|
+
"name": name,
|
|
141
|
+
"displayName": display_name,
|
|
142
|
+
"typeId": type_id,
|
|
143
|
+
"statusId": status_id,
|
|
144
|
+
"domainId": domain_id,
|
|
145
|
+
"excludedFromAutoHyperlinking": excluded_from_auto_hyperlinking,
|
|
146
|
+
"typePublicId": type_public_id,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
# Use PATCH method through requests directly since BaseAPI doesn't have _patch
|
|
150
|
+
import requests
|
|
151
|
+
response = requests.patch(
|
|
152
|
+
f"{self.__base_api}/{asset_id}",
|
|
153
|
+
auth=self.__connector.auth,
|
|
154
|
+
json=data,
|
|
155
|
+
headers=self.__header,
|
|
156
|
+
timeout=30
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return self._handle_response(response)
|
|
160
|
+
|
|
161
|
+
def update_asset_attribute(self, asset_id: str, attribute_id: str, value):
|
|
162
|
+
"""
|
|
163
|
+
Update an asset attribute.
|
|
164
|
+
:param asset_id: The ID of the asset.
|
|
165
|
+
:param attribute_id: The ID of the attribute type.
|
|
166
|
+
:param value: The new value for the attribute.
|
|
167
|
+
:return: The response from updating the attribute.
|
|
168
|
+
"""
|
|
169
|
+
if not all([asset_id, attribute_id]):
|
|
170
|
+
raise ValueError("asset_id and attribute_id are required")
|
|
171
|
+
|
|
172
|
+
for param_name, param_value in [("asset_id", asset_id), ("attribute_id", attribute_id)]:
|
|
173
|
+
if not isinstance(param_value, str):
|
|
174
|
+
raise ValueError(f"{param_name} must be a string")
|
|
175
|
+
try:
|
|
176
|
+
uuid.UUID(param_value)
|
|
177
|
+
except ValueError as exc:
|
|
178
|
+
raise ValueError(f"{param_name} must be a valid UUID") from exc
|
|
179
|
+
|
|
180
|
+
data = {
|
|
181
|
+
"values": [value],
|
|
182
|
+
"typeId": attribute_id
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
# Use PUT method through requests directly since BaseAPI doesn't have _put
|
|
186
|
+
import requests
|
|
187
|
+
response = requests.put(
|
|
188
|
+
f"{self.__base_api}/{asset_id}/attributes",
|
|
189
|
+
auth=self.__connector.auth,
|
|
190
|
+
json=data,
|
|
191
|
+
headers=self.__header,
|
|
192
|
+
timeout=30
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return self._handle_response(response)
|
|
196
|
+
|
|
197
|
+
def find_assets(
|
|
198
|
+
self,
|
|
199
|
+
community_id: str = None,
|
|
200
|
+
asset_type_ids: list = None,
|
|
201
|
+
domain_id: str = None,
|
|
202
|
+
limit: int = 1000
|
|
203
|
+
):
|
|
204
|
+
"""
|
|
205
|
+
Find assets with optional filters.
|
|
206
|
+
:param community_id: Optional community ID to filter by.
|
|
207
|
+
:param asset_type_ids: Optional list of asset type IDs to filter by.
|
|
208
|
+
:param domain_id: Optional domain ID to filter by.
|
|
209
|
+
:param limit: Maximum number of results per page.
|
|
210
|
+
:return: List of assets matching the criteria.
|
|
211
|
+
"""
|
|
212
|
+
params = {"limit": limit}
|
|
213
|
+
|
|
214
|
+
if community_id:
|
|
215
|
+
if not isinstance(community_id, str):
|
|
216
|
+
raise ValueError("community_id must be a string")
|
|
217
|
+
try:
|
|
218
|
+
uuid.UUID(community_id)
|
|
219
|
+
except ValueError as exc:
|
|
220
|
+
raise ValueError("community_id must be a valid UUID") from exc
|
|
221
|
+
params["communityId"] = community_id
|
|
222
|
+
|
|
223
|
+
if asset_type_ids:
|
|
224
|
+
if not isinstance(asset_type_ids, list):
|
|
225
|
+
raise ValueError("asset_type_ids must be a list")
|
|
226
|
+
for type_id in asset_type_ids:
|
|
227
|
+
if not isinstance(type_id, str):
|
|
228
|
+
raise ValueError("All asset_type_ids must be strings")
|
|
229
|
+
try:
|
|
230
|
+
uuid.UUID(type_id)
|
|
231
|
+
except ValueError as exc:
|
|
232
|
+
raise ValueError("All asset_type_ids must be valid UUIDs") from exc
|
|
233
|
+
params["typeIds"] = asset_type_ids
|
|
234
|
+
|
|
235
|
+
if domain_id:
|
|
236
|
+
if not isinstance(domain_id, str):
|
|
237
|
+
raise ValueError("domain_id must be a string")
|
|
238
|
+
try:
|
|
239
|
+
uuid.UUID(domain_id)
|
|
240
|
+
except ValueError as exc:
|
|
241
|
+
raise ValueError("domain_id must be a valid UUID") from exc
|
|
242
|
+
params["domainId"] = domain_id
|
|
243
|
+
|
|
244
|
+
response = self._get(params=params)
|
|
245
|
+
return self._handle_response(response)
|
|
246
|
+
|
|
247
|
+
def get_asset_activities(self, asset_id: str, limit: int = 50):
|
|
248
|
+
"""
|
|
249
|
+
Get activities for a specific asset.
|
|
250
|
+
:param asset_id: The ID of the asset.
|
|
251
|
+
:param limit: Maximum number of activities to retrieve.
|
|
252
|
+
:return: List of activities for the asset.
|
|
253
|
+
"""
|
|
254
|
+
if not asset_id:
|
|
255
|
+
raise ValueError("asset_id is required")
|
|
256
|
+
if not isinstance(asset_id, str):
|
|
257
|
+
raise ValueError("asset_id must be a string")
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
uuid.UUID(asset_id)
|
|
261
|
+
except ValueError as exc:
|
|
262
|
+
raise ValueError("asset_id must be a valid UUID") from exc
|
|
263
|
+
|
|
264
|
+
params = {
|
|
265
|
+
"contextId": asset_id,
|
|
266
|
+
"resourceTypes": ["Asset"],
|
|
267
|
+
"limit": limit
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
response = self._get(url=f"{self.__base_api}/activities", params=params)
|
|
271
|
+
result = self._handle_response(response)
|
|
272
|
+
return result.get("results", [])
|
collibra_connector/api/Base.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import requests
|
|
2
3
|
from .Exceptions import (
|
|
3
4
|
UnauthorizedError,
|
|
@@ -6,7 +7,9 @@ from .Exceptions import (
|
|
|
6
7
|
ServerError
|
|
7
8
|
)
|
|
8
9
|
|
|
10
|
+
|
|
9
11
|
class BaseAPI:
|
|
12
|
+
|
|
10
13
|
def __init__(self, connector):
|
|
11
14
|
self.__connector = connector
|
|
12
15
|
self.__base_api = connector.api
|
|
@@ -16,7 +19,7 @@ class BaseAPI:
|
|
|
16
19
|
}
|
|
17
20
|
self.__params = None
|
|
18
21
|
|
|
19
|
-
def
|
|
22
|
+
def _get(self, url: str = None, params: dict = None, headers: dict = None):
|
|
20
23
|
"""
|
|
21
24
|
Makes a GET request to the specified URL.
|
|
22
25
|
:param url: The URL to send the GET request to.
|
|
@@ -31,10 +34,11 @@ class BaseAPI:
|
|
|
31
34
|
url,
|
|
32
35
|
auth=self.__connector.auth,
|
|
33
36
|
params=params,
|
|
34
|
-
headers=headers
|
|
37
|
+
headers=headers,
|
|
38
|
+
timeout=self.__connector.timeout
|
|
35
39
|
)
|
|
36
40
|
|
|
37
|
-
def
|
|
41
|
+
def _post(self, url: str, data: dict, headers: dict = None):
|
|
38
42
|
"""
|
|
39
43
|
Makes a POST request to the specified URL with the given data.
|
|
40
44
|
:param url: The URL to send the POST request to.
|
|
@@ -51,22 +55,99 @@ class BaseAPI:
|
|
|
51
55
|
url,
|
|
52
56
|
auth=self.__connector.auth,
|
|
53
57
|
json=data,
|
|
54
|
-
headers=
|
|
58
|
+
headers=headers,
|
|
59
|
+
timeout=self.__connector.timeout
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def _put(self, url: str, data: dict, headers: dict = None):
|
|
63
|
+
"""
|
|
64
|
+
Makes a PUT request to the specified URL with the given data.
|
|
65
|
+
:param url: The URL to send the PUT request to.
|
|
66
|
+
:param data: The data to send in the PUT request.
|
|
67
|
+
:return: The response from the PUT request.
|
|
68
|
+
"""
|
|
69
|
+
url = self.__base_api if not url else url
|
|
70
|
+
headers = self.__header if not headers else headers
|
|
71
|
+
if not isinstance(data, dict):
|
|
72
|
+
raise ValueError("Data must be a dictionary")
|
|
73
|
+
if not data:
|
|
74
|
+
raise ValueError("Data cannot be empty")
|
|
75
|
+
return requests.put(
|
|
76
|
+
url,
|
|
77
|
+
auth=self.__connector.auth,
|
|
78
|
+
json=data,
|
|
79
|
+
headers=headers,
|
|
80
|
+
timeout=self.__connector.timeout
|
|
55
81
|
)
|
|
56
82
|
|
|
57
|
-
def
|
|
83
|
+
def _delete(self, url: str, headers: dict = None):
|
|
84
|
+
"""
|
|
85
|
+
Makes a DELETE request to the specified URL.
|
|
86
|
+
:param url: The URL to send the DELETE request to.
|
|
87
|
+
:return: The response from the DELETE request.
|
|
88
|
+
"""
|
|
89
|
+
url = self.__base_api if not url else url
|
|
90
|
+
headers = self.__header if not headers else headers
|
|
91
|
+
return requests.delete(
|
|
92
|
+
url,
|
|
93
|
+
auth=self.__connector.auth,
|
|
94
|
+
headers=headers,
|
|
95
|
+
timeout=self.__connector.timeout
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def _patch(self, url: str, data: dict, headers: dict = None):
|
|
99
|
+
"""
|
|
100
|
+
Makes a PATCH request to the specified URL with the given data.
|
|
101
|
+
:param url: The URL to send the PATCH request to.
|
|
102
|
+
:param data: The data to send in the PATCH request.
|
|
103
|
+
:return: The response from the PATCH request.
|
|
104
|
+
"""
|
|
105
|
+
url = self.__base_api if not url else url
|
|
106
|
+
headers = self.__header if not headers else headers
|
|
107
|
+
if not isinstance(data, dict):
|
|
108
|
+
raise ValueError("Data must be a dictionary")
|
|
109
|
+
if not data:
|
|
110
|
+
raise ValueError("Data cannot be empty")
|
|
111
|
+
return requests.patch(
|
|
112
|
+
url,
|
|
113
|
+
auth=self.__connector.auth,
|
|
114
|
+
json=data,
|
|
115
|
+
headers=headers,
|
|
116
|
+
timeout=self.__connector.timeout
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def _handle_response(self, response):
|
|
58
120
|
"""
|
|
59
121
|
Handles the response from the API.
|
|
60
122
|
:param response: The response object from the API request.
|
|
61
123
|
:return: The JSON content of the response if successful, otherwise raises an error.
|
|
62
124
|
"""
|
|
63
|
-
if response.status_code
|
|
64
|
-
|
|
125
|
+
if response.status_code in [200, 201]:
|
|
126
|
+
# Check if response has content before trying to parse JSON
|
|
127
|
+
if response.text.strip():
|
|
128
|
+
try:
|
|
129
|
+
return response.json()
|
|
130
|
+
except ValueError as e:
|
|
131
|
+
raise ValueError(f"Invalid JSON response: {e}") from e
|
|
132
|
+
return {}
|
|
65
133
|
elif response.status_code == 401:
|
|
66
|
-
raise UnauthorizedError("Unauthorized access - invalid credentials")
|
|
134
|
+
raise UnauthorizedError("Unauthorized access - invalid credentials: " + response.text)
|
|
67
135
|
elif response.status_code == 403:
|
|
68
|
-
raise ForbiddenError("Forbidden access - insufficient permissions")
|
|
136
|
+
raise ForbiddenError("Forbidden access - insufficient permissions: " + response.text)
|
|
69
137
|
elif response.status_code == 404:
|
|
70
|
-
raise NotFoundError("The specified resource was not found")
|
|
138
|
+
raise NotFoundError("The specified resource was not found: " + response.text)
|
|
71
139
|
elif response.status_code >= 500:
|
|
72
|
-
raise ServerError("Internal server error - something went wrong on the server")
|
|
140
|
+
raise ServerError("Internal server error - something went wrong on the server: " + response.text)
|
|
141
|
+
else:
|
|
142
|
+
raise Exception(f"Unexpected status code {response.status_code}: {response.text}")
|
|
143
|
+
|
|
144
|
+
def _uuid_validation(self, id: str):
|
|
145
|
+
"""
|
|
146
|
+
Validates if the provided ID is a valid UUID.
|
|
147
|
+
:param id: The ID to validate.
|
|
148
|
+
:return: True if the ID is a valid UUID, False otherwise.
|
|
149
|
+
"""
|
|
150
|
+
if not id or not isinstance(id, str):
|
|
151
|
+
return False
|
|
152
|
+
pattern = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
|
153
|
+
return bool(pattern.match(id))
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from .Base import BaseAPI
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Comment(BaseAPI):
|
|
6
|
+
def __init__(self, connector):
|
|
7
|
+
super().__init__(connector)
|
|
8
|
+
self.__base_api = connector.api + "/comments"
|
|
9
|
+
|
|
10
|
+
def add_comment(self, asset_id: str, content: str):
|
|
11
|
+
"""
|
|
12
|
+
Add a comment to an asset.
|
|
13
|
+
:param asset_id: The ID of the asset to comment on.
|
|
14
|
+
:param content: The content of the comment.
|
|
15
|
+
:return: The created comment details.
|
|
16
|
+
"""
|
|
17
|
+
if not asset_id or not content:
|
|
18
|
+
raise ValueError("asset_id and content are required parameters")
|
|
19
|
+
if not isinstance(asset_id, str) or not isinstance(content, str):
|
|
20
|
+
raise ValueError("asset_id and content must be strings")
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
uuid.UUID(asset_id)
|
|
24
|
+
except ValueError as exc:
|
|
25
|
+
raise ValueError("asset_id must be a valid UUID") from exc
|
|
26
|
+
|
|
27
|
+
data = {
|
|
28
|
+
"content": content,
|
|
29
|
+
"baseResource": {
|
|
30
|
+
"id": asset_id,
|
|
31
|
+
"resourceType": "Asset"
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
response = self._post(url=self.__base_api, data=data)
|
|
36
|
+
return self._handle_response(response)
|
|
37
|
+
|
|
38
|
+
def find_comments(
|
|
39
|
+
self,
|
|
40
|
+
base_resource_id: str = None,
|
|
41
|
+
count_limit: int = -1,
|
|
42
|
+
limit: int = 0,
|
|
43
|
+
offset: int = 0,
|
|
44
|
+
parent_id: str = None,
|
|
45
|
+
resolved: bool = None,
|
|
46
|
+
root_comment: bool = None,
|
|
47
|
+
sort_order: str = "DESC",
|
|
48
|
+
user_id: str = None,
|
|
49
|
+
user_threads: bool = False
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Find comments matching the given search criteria.
|
|
53
|
+
:param base_resource_id: The ID of the resource which the searched comments refer to.
|
|
54
|
+
:param count_limit: Limit the number of elements counted. -1 counts everything, 0 skips count.
|
|
55
|
+
:param limit: Maximum number of results to retrieve. 0 uses default, max 1000.
|
|
56
|
+
:param offset: First result to retrieve. 0 starts from beginning.
|
|
57
|
+
:param parent_id: ID of the comment for which reply comments should be searched.
|
|
58
|
+
:param resolved: Whether the searched comments should be resolved.
|
|
59
|
+
:param root_comment: Whether the searched comments should be root comments.
|
|
60
|
+
:param sort_order: Sort order on creation date. 'ASC' or 'DESC'.
|
|
61
|
+
:param user_id: ID of the user to filter comments by.
|
|
62
|
+
:param user_threads: Whether to search for root comments created by or mentioning the user.
|
|
63
|
+
:return: List of comments matching the criteria.
|
|
64
|
+
"""
|
|
65
|
+
# Validate base_resource_id if provided
|
|
66
|
+
if base_resource_id is not None:
|
|
67
|
+
if not isinstance(base_resource_id, str):
|
|
68
|
+
raise ValueError("base_resource_id must be a string")
|
|
69
|
+
try:
|
|
70
|
+
uuid.UUID(base_resource_id)
|
|
71
|
+
except ValueError as exc:
|
|
72
|
+
raise ValueError("base_resource_id must be a valid UUID") from exc
|
|
73
|
+
|
|
74
|
+
# Validate parent_id if provided
|
|
75
|
+
if parent_id is not None:
|
|
76
|
+
if not isinstance(parent_id, str):
|
|
77
|
+
raise ValueError("parent_id must be a string")
|
|
78
|
+
try:
|
|
79
|
+
uuid.UUID(parent_id)
|
|
80
|
+
except ValueError as exc:
|
|
81
|
+
raise ValueError("parent_id must be a valid UUID") from exc
|
|
82
|
+
|
|
83
|
+
# Validate user_id if provided
|
|
84
|
+
if user_id is not None:
|
|
85
|
+
if not isinstance(user_id, str):
|
|
86
|
+
raise ValueError("user_id must be a string")
|
|
87
|
+
try:
|
|
88
|
+
uuid.UUID(user_id)
|
|
89
|
+
except ValueError as exc:
|
|
90
|
+
raise ValueError("user_id must be a valid UUID") from exc
|
|
91
|
+
|
|
92
|
+
# Validate sort_order
|
|
93
|
+
if sort_order not in ["ASC", "DESC"]:
|
|
94
|
+
raise ValueError("sort_order must be 'ASC' or 'DESC'")
|
|
95
|
+
|
|
96
|
+
# Validate limit
|
|
97
|
+
if limit < 0 or limit > 1000:
|
|
98
|
+
raise ValueError("limit must be between 0 and 1000")
|
|
99
|
+
|
|
100
|
+
# Build parameters - only include non-None values
|
|
101
|
+
params = {}
|
|
102
|
+
|
|
103
|
+
if base_resource_id is not None:
|
|
104
|
+
params["baseResourceId"] = base_resource_id
|
|
105
|
+
if count_limit != -1: # Only add if different from default
|
|
106
|
+
params["countLimit"] = count_limit
|
|
107
|
+
if limit != 0: # Only add if different from default
|
|
108
|
+
params["limit"] = limit
|
|
109
|
+
if offset != 0: # Only add if different from default
|
|
110
|
+
params["offset"] = offset
|
|
111
|
+
if parent_id is not None:
|
|
112
|
+
params["parentId"] = parent_id
|
|
113
|
+
if resolved is not None:
|
|
114
|
+
params["resolved"] = resolved
|
|
115
|
+
if root_comment is not None:
|
|
116
|
+
params["rootComment"] = root_comment
|
|
117
|
+
if sort_order != "DESC": # Only add if different from default
|
|
118
|
+
params["sortOrder"] = sort_order
|
|
119
|
+
if user_id is not None:
|
|
120
|
+
params["userId"] = user_id
|
|
121
|
+
if user_threads is not False: # Only add if different from default
|
|
122
|
+
params["userThreads"] = user_threads
|
|
123
|
+
|
|
124
|
+
response = self._get(params=params)
|
|
125
|
+
return self._handle_response(response)
|