collibra-connector 1.0.19__py3-none-any.whl → 1.1.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.
- collibra_connector/__init__.py +284 -4
- collibra_connector/api/Asset.py +301 -3
- collibra_connector/api/Attribute.py +204 -0
- collibra_connector/api/Base.py +2 -2
- collibra_connector/api/Relation.py +216 -0
- collibra_connector/api/Responsibility.py +5 -5
- collibra_connector/api/Search.py +102 -0
- collibra_connector/api/Workflow.py +50 -16
- collibra_connector/api/__init__.py +23 -13
- collibra_connector/async_connector.py +930 -0
- collibra_connector/cli.py +597 -0
- collibra_connector/connector.py +270 -48
- collibra_connector/helpers.py +845 -0
- collibra_connector/lineage.py +716 -0
- collibra_connector/models.py +897 -0
- collibra_connector/py.typed +0 -0
- collibra_connector/telemetry.py +576 -0
- collibra_connector/testing.py +806 -0
- collibra_connector-1.1.1.dist-info/METADATA +540 -0
- collibra_connector-1.1.1.dist-info/RECORD +32 -0
- {collibra_connector-1.0.19.dist-info → collibra_connector-1.1.1.dist-info}/WHEEL +1 -1
- collibra_connector-1.1.1.dist-info/entry_points.txt +2 -0
- collibra_connector-1.0.19.dist-info/METADATA +0 -157
- collibra_connector-1.0.19.dist-info/RECORD +0 -21
- {collibra_connector-1.0.19.dist-info → collibra_connector-1.1.1.dist-info}/licenses/LICENSE +0 -0
- {collibra_connector-1.0.19.dist-info → collibra_connector-1.1.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Attribute API module for Collibra Connector.
|
|
3
|
+
|
|
4
|
+
Provides methods for working with asset attributes.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import uuid
|
|
9
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from .Base import BaseAPI
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Attribute(BaseAPI):
|
|
18
|
+
"""API class for attribute operations."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, connector: Any) -> None:
|
|
21
|
+
"""Initialize the Attribute API."""
|
|
22
|
+
super().__init__(connector)
|
|
23
|
+
self.__base_api = connector.api + "/attributes"
|
|
24
|
+
|
|
25
|
+
def get_attributes(
|
|
26
|
+
self,
|
|
27
|
+
asset_id: str,
|
|
28
|
+
type_ids: Optional[List[str]] = None,
|
|
29
|
+
limit: int = 100,
|
|
30
|
+
offset: int = 0
|
|
31
|
+
) -> Dict[str, Any]:
|
|
32
|
+
"""
|
|
33
|
+
Get all attributes for an asset.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
asset_id: The UUID of the asset.
|
|
37
|
+
type_ids: Optional list of attribute type IDs to filter by.
|
|
38
|
+
limit: Maximum number of results.
|
|
39
|
+
offset: Offset for pagination.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Dictionary with 'results' list of attributes.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> attrs = connector.attribute.get_attributes("asset-uuid")
|
|
46
|
+
>>> for attr in attrs['results']:
|
|
47
|
+
... print(f"{attr['type']['name']}: {attr['value']}")
|
|
48
|
+
"""
|
|
49
|
+
if not asset_id:
|
|
50
|
+
raise ValueError("asset_id is required")
|
|
51
|
+
if not isinstance(asset_id, str):
|
|
52
|
+
raise ValueError("asset_id must be a string")
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
uuid.UUID(asset_id)
|
|
56
|
+
except ValueError as exc:
|
|
57
|
+
raise ValueError("asset_id must be a valid UUID") from exc
|
|
58
|
+
|
|
59
|
+
params: Dict[str, Any] = {
|
|
60
|
+
"assetId": asset_id,
|
|
61
|
+
"limit": limit,
|
|
62
|
+
"offset": offset
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if type_ids:
|
|
66
|
+
params["typeIds"] = type_ids
|
|
67
|
+
|
|
68
|
+
response = self._get(url=self.__base_api, params=params)
|
|
69
|
+
return self._handle_response(response)
|
|
70
|
+
|
|
71
|
+
def get_attribute(self, attribute_id: str) -> Dict[str, Any]:
|
|
72
|
+
"""
|
|
73
|
+
Get a specific attribute by ID.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
attribute_id: The UUID of the attribute.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Attribute details.
|
|
80
|
+
"""
|
|
81
|
+
if not attribute_id:
|
|
82
|
+
raise ValueError("attribute_id is required")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
uuid.UUID(attribute_id)
|
|
86
|
+
except ValueError as exc:
|
|
87
|
+
raise ValueError("attribute_id must be a valid UUID") from exc
|
|
88
|
+
|
|
89
|
+
response = self._get(url=f"{self.__base_api}/{attribute_id}")
|
|
90
|
+
return self._handle_response(response)
|
|
91
|
+
|
|
92
|
+
def add_attribute(
|
|
93
|
+
self,
|
|
94
|
+
asset_id: str,
|
|
95
|
+
type_id: str,
|
|
96
|
+
value: Any
|
|
97
|
+
) -> Dict[str, Any]:
|
|
98
|
+
"""
|
|
99
|
+
Add an attribute to an asset.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
asset_id: The UUID of the asset.
|
|
103
|
+
type_id: The UUID of the attribute type.
|
|
104
|
+
value: The value of the attribute.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Created attribute details.
|
|
108
|
+
"""
|
|
109
|
+
if not asset_id or not type_id:
|
|
110
|
+
raise ValueError("asset_id and type_id are required")
|
|
111
|
+
|
|
112
|
+
for param_name, param_value in [("asset_id", asset_id), ("type_id", type_id)]:
|
|
113
|
+
try:
|
|
114
|
+
uuid.UUID(param_value)
|
|
115
|
+
except ValueError as exc:
|
|
116
|
+
raise ValueError(f"{param_name} must be a valid UUID") from exc
|
|
117
|
+
|
|
118
|
+
data = {
|
|
119
|
+
"assetId": asset_id,
|
|
120
|
+
"typeId": type_id,
|
|
121
|
+
"value": value
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
response = self._post(url=self.__base_api, data=data)
|
|
125
|
+
return self._handle_response(response)
|
|
126
|
+
|
|
127
|
+
def change_attribute(
|
|
128
|
+
self,
|
|
129
|
+
attribute_id: str,
|
|
130
|
+
value: Any
|
|
131
|
+
) -> Dict[str, Any]:
|
|
132
|
+
"""
|
|
133
|
+
Update an attribute value.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
attribute_id: The UUID of the attribute.
|
|
137
|
+
value: The new value.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Updated attribute details.
|
|
141
|
+
"""
|
|
142
|
+
if not attribute_id:
|
|
143
|
+
raise ValueError("attribute_id is required")
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
uuid.UUID(attribute_id)
|
|
147
|
+
except ValueError as exc:
|
|
148
|
+
raise ValueError("attribute_id must be a valid UUID") from exc
|
|
149
|
+
|
|
150
|
+
data = {
|
|
151
|
+
"id": attribute_id,
|
|
152
|
+
"value": value
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
response = self._patch(url=f"{self.__base_api}/{attribute_id}", data=data)
|
|
156
|
+
return self._handle_response(response)
|
|
157
|
+
|
|
158
|
+
def remove_attribute(self, attribute_id: str) -> Dict[str, Any]:
|
|
159
|
+
"""
|
|
160
|
+
Remove an attribute.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
attribute_id: The UUID of the attribute to remove.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Response from the removal operation.
|
|
167
|
+
"""
|
|
168
|
+
if not attribute_id:
|
|
169
|
+
raise ValueError("attribute_id is required")
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
uuid.UUID(attribute_id)
|
|
173
|
+
except ValueError as exc:
|
|
174
|
+
raise ValueError("attribute_id must be a valid UUID") from exc
|
|
175
|
+
|
|
176
|
+
response = self._delete(url=f"{self.__base_api}/{attribute_id}")
|
|
177
|
+
return self._handle_response(response)
|
|
178
|
+
|
|
179
|
+
def get_attributes_as_dict(self, asset_id: str) -> Dict[str, Any]:
|
|
180
|
+
"""
|
|
181
|
+
Get all attributes for an asset as a simple dictionary.
|
|
182
|
+
|
|
183
|
+
Convenience method that returns attribute values keyed by attribute type name.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
asset_id: The UUID of the asset.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Dictionary mapping attribute type names to values.
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
>>> attrs = connector.attribute.get_attributes_as_dict("asset-uuid")
|
|
193
|
+
>>> print(attrs['Description'])
|
|
194
|
+
>>> print(attrs['Personal Data Classification'])
|
|
195
|
+
"""
|
|
196
|
+
result = self.get_attributes(asset_id, limit=500)
|
|
197
|
+
attrs_dict: Dict[str, Any] = {}
|
|
198
|
+
|
|
199
|
+
for attr in result.get('results', []):
|
|
200
|
+
type_name = attr.get('type', {}).get('name', 'Unknown')
|
|
201
|
+
value = attr.get('value')
|
|
202
|
+
attrs_dict[type_name] = value
|
|
203
|
+
|
|
204
|
+
return attrs_dict
|
collibra_connector/api/Base.py
CHANGED
|
@@ -123,9 +123,9 @@ class BaseAPI:
|
|
|
123
123
|
:param response: The response object from the API request.
|
|
124
124
|
:return: The JSON content of the response if successful, otherwise raises an error.
|
|
125
125
|
"""
|
|
126
|
-
if response.status_code in [200, 201]:
|
|
126
|
+
if response.status_code in [200, 201, 204]:
|
|
127
127
|
# Check if response has content before trying to parse JSON
|
|
128
|
-
if response.text.strip():
|
|
128
|
+
if response.status_code != 204 and response.text.strip():
|
|
129
129
|
try:
|
|
130
130
|
return response.json()
|
|
131
131
|
except ValueError as e:
|
|
@@ -204,3 +204,219 @@ class Relation(BaseAPI):
|
|
|
204
204
|
|
|
205
205
|
response = self._patch(url=f"{self.__base_api}/{relation_id}", data=data)
|
|
206
206
|
return self._handle_response(response)
|
|
207
|
+
|
|
208
|
+
def find_relations(
|
|
209
|
+
self,
|
|
210
|
+
source_id: str = None,
|
|
211
|
+
target_id: str = None,
|
|
212
|
+
type_id: str = None,
|
|
213
|
+
source_type_id: str = None,
|
|
214
|
+
target_type_id: str = None,
|
|
215
|
+
limit: int = 100,
|
|
216
|
+
offset: int = 0
|
|
217
|
+
):
|
|
218
|
+
"""
|
|
219
|
+
Find relations matching the given criteria.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
source_id: Filter by source asset ID.
|
|
223
|
+
target_id: Filter by target asset ID.
|
|
224
|
+
type_id: Filter by relation type ID.
|
|
225
|
+
source_type_id: Filter by source asset type ID.
|
|
226
|
+
target_type_id: Filter by target asset type ID.
|
|
227
|
+
limit: Maximum results to retrieve (max 1000).
|
|
228
|
+
offset: First result to retrieve.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
List of relations matching criteria.
|
|
232
|
+
|
|
233
|
+
Example:
|
|
234
|
+
>>> # Get all outgoing relations from an asset
|
|
235
|
+
>>> relations = connector.relation.find_relations(source_id="asset-uuid")
|
|
236
|
+
>>> # Get all incoming relations to an asset
|
|
237
|
+
>>> relations = connector.relation.find_relations(target_id="asset-uuid")
|
|
238
|
+
"""
|
|
239
|
+
params = {
|
|
240
|
+
"limit": limit,
|
|
241
|
+
"offset": offset
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# Validate and add source_id
|
|
245
|
+
if source_id is not None:
|
|
246
|
+
if not isinstance(source_id, str):
|
|
247
|
+
raise ValueError("source_id must be a string")
|
|
248
|
+
try:
|
|
249
|
+
uuid.UUID(source_id)
|
|
250
|
+
except ValueError as exc:
|
|
251
|
+
raise ValueError("source_id must be a valid UUID") from exc
|
|
252
|
+
params["sourceId"] = source_id
|
|
253
|
+
|
|
254
|
+
# Validate and add target_id
|
|
255
|
+
if target_id is not None:
|
|
256
|
+
if not isinstance(target_id, str):
|
|
257
|
+
raise ValueError("target_id must be a string")
|
|
258
|
+
try:
|
|
259
|
+
uuid.UUID(target_id)
|
|
260
|
+
except ValueError as exc:
|
|
261
|
+
raise ValueError("target_id must be a valid UUID") from exc
|
|
262
|
+
params["targetId"] = target_id
|
|
263
|
+
|
|
264
|
+
# Validate and add type_id
|
|
265
|
+
if type_id is not None:
|
|
266
|
+
if not isinstance(type_id, str):
|
|
267
|
+
raise ValueError("type_id must be a string")
|
|
268
|
+
try:
|
|
269
|
+
uuid.UUID(type_id)
|
|
270
|
+
except ValueError as exc:
|
|
271
|
+
raise ValueError("type_id must be a valid UUID") from exc
|
|
272
|
+
params["typeId"] = type_id
|
|
273
|
+
|
|
274
|
+
# Add optional type filters
|
|
275
|
+
if source_type_id is not None:
|
|
276
|
+
params["sourceTypeId"] = source_type_id
|
|
277
|
+
if target_type_id is not None:
|
|
278
|
+
params["targetTypeId"] = target_type_id
|
|
279
|
+
|
|
280
|
+
response = self._get(url=self.__base_api, params=params)
|
|
281
|
+
return self._handle_response(response)
|
|
282
|
+
|
|
283
|
+
def get_relation_type(self, relation_type_id: str):
|
|
284
|
+
"""
|
|
285
|
+
Get relation type details by ID.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
relation_type_id: The UUID of the relation type.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Relation type details including role and coRole names.
|
|
292
|
+
"""
|
|
293
|
+
if not relation_type_id:
|
|
294
|
+
raise ValueError("relation_type_id is required")
|
|
295
|
+
|
|
296
|
+
try:
|
|
297
|
+
uuid.UUID(relation_type_id)
|
|
298
|
+
except ValueError as exc:
|
|
299
|
+
raise ValueError("relation_type_id must be a valid UUID") from exc
|
|
300
|
+
|
|
301
|
+
url = self._BaseAPI__connector.api + f"/relationTypes/{relation_type_id}"
|
|
302
|
+
response = self._get(url=url)
|
|
303
|
+
return self._handle_response(response)
|
|
304
|
+
|
|
305
|
+
def get_asset_relations(
|
|
306
|
+
self,
|
|
307
|
+
asset_id: str,
|
|
308
|
+
direction: str = "BOTH",
|
|
309
|
+
limit: int = 500,
|
|
310
|
+
include_type_details: bool = True
|
|
311
|
+
):
|
|
312
|
+
"""
|
|
313
|
+
Get all relations for an asset, grouped by direction and type.
|
|
314
|
+
|
|
315
|
+
Convenience method that returns a structured view of all relations.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
asset_id: The UUID of the asset.
|
|
319
|
+
direction: "OUTGOING", "INCOMING", or "BOTH".
|
|
320
|
+
limit: Maximum relations per direction.
|
|
321
|
+
include_type_details: If True, fetches relation type names (slower but more informative).
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Dictionary with 'outgoing' and 'incoming' keys, each containing
|
|
325
|
+
relations grouped by relation type name.
|
|
326
|
+
|
|
327
|
+
Example:
|
|
328
|
+
>>> relations = connector.relation.get_asset_relations("asset-uuid")
|
|
329
|
+
>>> print(relations['outgoing']) # Relations where asset is source
|
|
330
|
+
>>> print(relations['incoming']) # Relations where asset is target
|
|
331
|
+
"""
|
|
332
|
+
if not asset_id:
|
|
333
|
+
raise ValueError("asset_id is required")
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
uuid.UUID(asset_id)
|
|
337
|
+
except ValueError as exc:
|
|
338
|
+
raise ValueError("asset_id must be a valid UUID") from exc
|
|
339
|
+
|
|
340
|
+
result = {
|
|
341
|
+
"outgoing": {},
|
|
342
|
+
"incoming": {},
|
|
343
|
+
"outgoing_count": 0,
|
|
344
|
+
"incoming_count": 0
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# Cache for relation type details
|
|
348
|
+
type_cache = {}
|
|
349
|
+
|
|
350
|
+
def get_type_name(type_id, is_source=True):
|
|
351
|
+
"""Get relation type name, using cache."""
|
|
352
|
+
if not include_type_details or not type_id:
|
|
353
|
+
return "Unknown"
|
|
354
|
+
|
|
355
|
+
if type_id not in type_cache:
|
|
356
|
+
try:
|
|
357
|
+
type_details = self.get_relation_type(type_id)
|
|
358
|
+
type_cache[type_id] = type_details
|
|
359
|
+
except Exception:
|
|
360
|
+
type_cache[type_id] = {}
|
|
361
|
+
|
|
362
|
+
cached = type_cache.get(type_id, {})
|
|
363
|
+
if is_source:
|
|
364
|
+
return cached.get("role", "Unknown")
|
|
365
|
+
else:
|
|
366
|
+
return cached.get("coRole", "Unknown")
|
|
367
|
+
|
|
368
|
+
# Get outgoing relations
|
|
369
|
+
if direction in ("BOTH", "OUTGOING"):
|
|
370
|
+
outgoing = self.find_relations(source_id=asset_id, limit=limit)
|
|
371
|
+
result["outgoing_count"] = outgoing.get("total", 0)
|
|
372
|
+
|
|
373
|
+
for rel in outgoing.get("results", []):
|
|
374
|
+
rel_type = rel.get("type", {})
|
|
375
|
+
type_id = rel_type.get("id")
|
|
376
|
+
type_name = get_type_name(type_id, is_source=True)
|
|
377
|
+
target = rel.get("target", {})
|
|
378
|
+
|
|
379
|
+
# Get target asset type by looking up the relation type
|
|
380
|
+
target_type_name = None
|
|
381
|
+
if type_id in type_cache:
|
|
382
|
+
target_type_name = type_cache[type_id].get("targetType", {}).get("name")
|
|
383
|
+
|
|
384
|
+
if type_name not in result["outgoing"]:
|
|
385
|
+
result["outgoing"][type_name] = []
|
|
386
|
+
|
|
387
|
+
result["outgoing"][type_name].append({
|
|
388
|
+
"id": rel.get("id"),
|
|
389
|
+
"target_id": target.get("id"),
|
|
390
|
+
"target_name": target.get("name"),
|
|
391
|
+
"target_type": target_type_name or target.get("type", {}).get("name"),
|
|
392
|
+
"target_status": target.get("status", {}).get("name") if target.get("status") else "N/A"
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
# Get incoming relations
|
|
396
|
+
if direction in ("BOTH", "INCOMING"):
|
|
397
|
+
incoming = self.find_relations(target_id=asset_id, limit=limit)
|
|
398
|
+
result["incoming_count"] = incoming.get("total", 0)
|
|
399
|
+
|
|
400
|
+
for rel in incoming.get("results", []):
|
|
401
|
+
rel_type = rel.get("type", {})
|
|
402
|
+
type_id = rel_type.get("id")
|
|
403
|
+
type_name = get_type_name(type_id, is_source=False)
|
|
404
|
+
source = rel.get("source", {})
|
|
405
|
+
|
|
406
|
+
# Get source asset type by looking up the relation type
|
|
407
|
+
source_type_name = None
|
|
408
|
+
if type_id in type_cache:
|
|
409
|
+
source_type_name = type_cache[type_id].get("sourceType", {}).get("name")
|
|
410
|
+
|
|
411
|
+
if type_name not in result["incoming"]:
|
|
412
|
+
result["incoming"][type_name] = []
|
|
413
|
+
|
|
414
|
+
result["incoming"][type_name].append({
|
|
415
|
+
"id": rel.get("id"),
|
|
416
|
+
"source_id": source.get("id"),
|
|
417
|
+
"source_name": source.get("name"),
|
|
418
|
+
"source_type": source_type_name or source.get("type", {}).get("name"),
|
|
419
|
+
"source_status": source.get("status", {}).get("name") if source.get("status") else "N/A"
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
return result
|
|
@@ -182,13 +182,13 @@ class Responsibility(BaseAPI):
|
|
|
182
182
|
raise ValueError(f"type must be one of: {', '.join(valid_types)}")
|
|
183
183
|
|
|
184
184
|
# Validate that globalOnly and type are mutually exclusive
|
|
185
|
-
if global_only is not None and
|
|
185
|
+
if global_only is not None and _type is not None:
|
|
186
186
|
raise ValueError("globalOnly and type parameters are mutually exclusive")
|
|
187
187
|
|
|
188
188
|
# Validate owner_ids if provided
|
|
189
189
|
if owner_ids is not None:
|
|
190
190
|
if not isinstance(owner_ids, list):
|
|
191
|
-
raise ValueError("
|
|
191
|
+
raise ValueError("owner_id must be a list")
|
|
192
192
|
for owner_id in owner_ids:
|
|
193
193
|
if not isinstance(owner_id, str):
|
|
194
194
|
raise ValueError("owner_id must be a string")
|
|
@@ -246,10 +246,10 @@ class Responsibility(BaseAPI):
|
|
|
246
246
|
params["sortField"] = sort_field
|
|
247
247
|
if sort_order != "DESC":
|
|
248
248
|
params["sortOrder"] = sort_order
|
|
249
|
-
if
|
|
250
|
-
params["type"] =
|
|
249
|
+
if _type is not None:
|
|
250
|
+
params["type"] = _type
|
|
251
251
|
|
|
252
|
-
response = self._get(params=params)
|
|
252
|
+
response = self._get(url=self.__base_api, params=params)
|
|
253
253
|
return self._handle_response(response)
|
|
254
254
|
|
|
255
255
|
def get_asset_responsibilities(self, asset_id: str, role_ids: list = None):
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
from .Base import BaseAPI
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Search(BaseAPI):
|
|
6
|
+
"""
|
|
7
|
+
Search API implementation.
|
|
8
|
+
Wraps the /search endpoint of Collibra DGC.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, connector):
|
|
12
|
+
super().__init__(connector)
|
|
13
|
+
self.__base_api = connector.api + "/search"
|
|
14
|
+
|
|
15
|
+
def find(
|
|
16
|
+
self,
|
|
17
|
+
query: str,
|
|
18
|
+
limit: int = 10,
|
|
19
|
+
offset: int = 0,
|
|
20
|
+
filter_options: Optional[Dict[str, Any]] = None,
|
|
21
|
+
sort_options: Optional[Dict[str, Any]] = None,
|
|
22
|
+
highlight_options: Optional[Dict[str, Any]] = None,
|
|
23
|
+
search_fields: Optional[List[str]] = None,
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""
|
|
26
|
+
Perform a search query.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
query: The search query string (can use wildcards like *).
|
|
30
|
+
limit: Max number of results.
|
|
31
|
+
offset: Offset for pagination.
|
|
32
|
+
filter_options: Dictionary of filters (e.g., {'category': 'ASSET', 'typeIds': [...]}).
|
|
33
|
+
sort_options: Dictionary of sort options (e.g., {'field': 'name', 'order': 'ASC'}).
|
|
34
|
+
highlight_options: Dictionary of highlight options.
|
|
35
|
+
search_fields: List of fields to search in.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
Search results dictionary with 'results', 'total', etc.
|
|
39
|
+
"""
|
|
40
|
+
data = {
|
|
41
|
+
"keywords": query,
|
|
42
|
+
"limit": limit,
|
|
43
|
+
"offset": offset,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if filter_options:
|
|
47
|
+
data.update(filter_options)
|
|
48
|
+
|
|
49
|
+
if sort_options:
|
|
50
|
+
data["sort"] = [sort_options] # API expects a list of sorts
|
|
51
|
+
|
|
52
|
+
if highlight_options:
|
|
53
|
+
data["highlights"] = [highlight_options]
|
|
54
|
+
|
|
55
|
+
if search_fields:
|
|
56
|
+
data["searchFields"] = search_fields
|
|
57
|
+
|
|
58
|
+
# Default to ASSET category if not specified, usually what users want
|
|
59
|
+
# But maybe better to leave it open. The API defaults to all.
|
|
60
|
+
|
|
61
|
+
response = self._post(url=self.__base_api, data=data)
|
|
62
|
+
return self._handle_response(response)
|
|
63
|
+
|
|
64
|
+
def find_assets(
|
|
65
|
+
self,
|
|
66
|
+
query: str,
|
|
67
|
+
limit: int = 10,
|
|
68
|
+
offset: int = 0,
|
|
69
|
+
type_ids: Optional[List[str]] = None,
|
|
70
|
+
domain_ids: Optional[List[str]] = None,
|
|
71
|
+
community_ids: Optional[List[str]] = None,
|
|
72
|
+
status_ids: Optional[List[str]] = None,
|
|
73
|
+
) -> Dict[str, Any]:
|
|
74
|
+
"""
|
|
75
|
+
Helper specifically for searching assets.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
query: Search text.
|
|
79
|
+
limit: Max results.
|
|
80
|
+
offset: Pagination offset.
|
|
81
|
+
type_ids: Filter by asset type IDs.
|
|
82
|
+
domain_ids: Filter by domain IDs.
|
|
83
|
+
community_ids: Filter by community IDs.
|
|
84
|
+
status_ids: Filter by status IDs.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Search results.
|
|
88
|
+
"""
|
|
89
|
+
filters = {
|
|
90
|
+
"category": "ASSET"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if type_ids:
|
|
94
|
+
filters["typeIds"] = type_ids
|
|
95
|
+
if domain_ids:
|
|
96
|
+
filters["domainIds"] = domain_ids
|
|
97
|
+
if community_ids:
|
|
98
|
+
filters["communityIds"] = community_ids
|
|
99
|
+
if status_ids:
|
|
100
|
+
filters["statusIds"] = status_ids
|
|
101
|
+
|
|
102
|
+
return self.find(query=query, limit=limit, offset=offset, filter_options=filters)
|
|
@@ -10,15 +10,26 @@ class Workflow(BaseAPI):
|
|
|
10
10
|
def start_workflow_instance(
|
|
11
11
|
self,
|
|
12
12
|
workflow_definition_id: str,
|
|
13
|
-
|
|
13
|
+
business_item_ids: list[str] = None,
|
|
14
|
+
business_item_type: str = None,
|
|
15
|
+
form_properties: dict[str, str] = None,
|
|
16
|
+
guest_user_id: str = None,
|
|
17
|
+
send_notification: bool = False,
|
|
14
18
|
return_all: bool = False
|
|
15
19
|
):
|
|
16
20
|
"""
|
|
17
|
-
Start
|
|
18
|
-
|
|
19
|
-
:param
|
|
21
|
+
Start multiple workflow instances based on the provided request.
|
|
22
|
+
|
|
23
|
+
:param workflow_definition_id: The ID of the workflow definition (required).
|
|
24
|
+
:param business_item_ids: The list of IDs for the business items (optional).
|
|
25
|
+
:param business_item_type: The resource type of the passed in business items.
|
|
26
|
+
Allowed values: ASSET, DOMAIN, COMMUNITY, GLOBAL, USER (optional).
|
|
27
|
+
:param form_properties: The properties of the workflow as a dictionary (optional).
|
|
28
|
+
:param guest_user_id: The ID of the guest user starting the workflow (optional).
|
|
29
|
+
:param send_notification: Whether a mail notification on starting the workflows should be sent.
|
|
30
|
+
This notification is only used in the asynchronous api version (optional).
|
|
20
31
|
:param return_all: Whether to return all workflow data or just the ID.
|
|
21
|
-
:return: Workflow instance ID or full response data.
|
|
32
|
+
:return: Workflow instance ID or full response data (array of workflow instances).
|
|
22
33
|
"""
|
|
23
34
|
if not workflow_definition_id:
|
|
24
35
|
raise ValueError("workflow_definition_id is required")
|
|
@@ -32,21 +43,44 @@ class Workflow(BaseAPI):
|
|
|
32
43
|
|
|
33
44
|
data = {
|
|
34
45
|
"workflowDefinitionId": workflow_definition_id,
|
|
35
|
-
"sendNotification":
|
|
46
|
+
"sendNotification": send_notification
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
if
|
|
39
|
-
if not isinstance(
|
|
40
|
-
raise ValueError("
|
|
49
|
+
if business_item_ids is not None:
|
|
50
|
+
if not isinstance(business_item_ids, list):
|
|
51
|
+
raise ValueError("business_item_ids must be a list")
|
|
52
|
+
if not all(isinstance(item_id, str) for item_id in business_item_ids):
|
|
53
|
+
raise ValueError("All business_item_ids must be strings")
|
|
54
|
+
for item_id in business_item_ids:
|
|
55
|
+
try:
|
|
56
|
+
uuid.UUID(item_id)
|
|
57
|
+
except ValueError as exc:
|
|
58
|
+
raise ValueError(f"business_item_id '{item_id}' must be a valid UUID") from exc
|
|
59
|
+
data["businessItemIds"] = business_item_ids
|
|
60
|
+
|
|
61
|
+
if business_item_type is not None:
|
|
62
|
+
if not isinstance(business_item_type, str):
|
|
63
|
+
raise ValueError("business_item_type must be a string")
|
|
64
|
+
valid_types = ["ASSET", "DOMAIN", "COMMUNITY", "GLOBAL", "USER"]
|
|
65
|
+
if business_item_type not in valid_types:
|
|
66
|
+
raise ValueError(f"business_item_type must be one of: {', '.join(valid_types)}")
|
|
67
|
+
data["businessItemType"] = business_item_type
|
|
68
|
+
|
|
69
|
+
if form_properties is not None:
|
|
70
|
+
if not isinstance(form_properties, dict):
|
|
71
|
+
raise ValueError("form_properties must be a dictionary")
|
|
72
|
+
if not all(isinstance(k, str) and isinstance(v, str) for k, v in form_properties.items()):
|
|
73
|
+
raise ValueError("All form_properties keys and values must be strings")
|
|
74
|
+
data["formProperties"] = form_properties
|
|
75
|
+
|
|
76
|
+
if guest_user_id is not None:
|
|
77
|
+
if not isinstance(guest_user_id, str):
|
|
78
|
+
raise ValueError("guest_user_id must be a string")
|
|
41
79
|
try:
|
|
42
|
-
uuid.UUID(
|
|
80
|
+
uuid.UUID(guest_user_id)
|
|
43
81
|
except ValueError as exc:
|
|
44
|
-
raise ValueError("
|
|
45
|
-
|
|
46
|
-
data.update({
|
|
47
|
-
"businessItemIds": [asset_id],
|
|
48
|
-
"businessItemType": "ASSET",
|
|
49
|
-
})
|
|
82
|
+
raise ValueError("guest_user_id must be a valid UUID") from exc
|
|
83
|
+
data["guestUserId"] = guest_user_id
|
|
50
84
|
|
|
51
85
|
response = self._post(url=f"{self.__base_api}/workflowInstances", data=data)
|
|
52
86
|
result = self._handle_response(response)
|