collibra-connector 1.0.19__py3-none-any.whl → 1.1.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.
- 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/__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.0.dist-info/METADATA +540 -0
- collibra_connector-1.1.0.dist-info/RECORD +32 -0
- collibra_connector-1.1.0.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.0.dist-info}/WHEEL +0 -0
- {collibra_connector-1.0.19.dist-info → collibra_connector-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {collibra_connector-1.0.19.dist-info → collibra_connector-1.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic models for Collibra API responses.
|
|
3
|
+
|
|
4
|
+
This module provides typed models for all Collibra resources, enabling
|
|
5
|
+
full IDE autocompletion, validation, and type safety.
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> asset = conn.asset.get_asset("uuid") # Returns AssetModel
|
|
9
|
+
>>> print(asset.name)
|
|
10
|
+
>>> print(asset.status.name)
|
|
11
|
+
>>> print(asset.type.id)
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from enum import Enum
|
|
17
|
+
from typing import Any, Dict, Generic, Iterator, List, Optional, TypeVar, Union
|
|
18
|
+
|
|
19
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Type variable for generic paginated responses
|
|
23
|
+
T = TypeVar('T', bound='BaseCollibraModel')
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ResourceType(str, Enum):
|
|
27
|
+
"""Enum for Collibra resource types."""
|
|
28
|
+
ASSET = "Asset"
|
|
29
|
+
DOMAIN = "Domain"
|
|
30
|
+
COMMUNITY = "Community"
|
|
31
|
+
USER = "User"
|
|
32
|
+
ATTRIBUTE = "Attribute"
|
|
33
|
+
RELATION = "Relation"
|
|
34
|
+
COMMENT = "Comment"
|
|
35
|
+
RESPONSIBILITY = "Responsibility"
|
|
36
|
+
WORKFLOW = "Workflow"
|
|
37
|
+
STATUS = "Status"
|
|
38
|
+
ASSET_TYPE = "AssetType"
|
|
39
|
+
DOMAIN_TYPE = "DomainType"
|
|
40
|
+
ATTRIBUTE_TYPE = "AttributeType"
|
|
41
|
+
RELATION_TYPE = "RelationType"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BaseCollibraModel(BaseModel):
|
|
45
|
+
"""Base class for all Collibra models with common configuration."""
|
|
46
|
+
model_config = ConfigDict(
|
|
47
|
+
populate_by_name=True,
|
|
48
|
+
extra="allow", # Allow extra fields from API
|
|
49
|
+
str_strip_whitespace=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
53
|
+
"""Convert model to dictionary with camelCase keys."""
|
|
54
|
+
return self.model_dump(by_alias=True, exclude_none=True)
|
|
55
|
+
|
|
56
|
+
def to_json(self) -> str:
|
|
57
|
+
"""Convert model to JSON string."""
|
|
58
|
+
return self.model_dump_json(by_alias=True, exclude_none=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ResourceReference(BaseCollibraModel):
|
|
62
|
+
"""Reference to another resource (lightweight representation)."""
|
|
63
|
+
id: str
|
|
64
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
65
|
+
name: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
def __str__(self) -> str:
|
|
68
|
+
return self.name or self.id
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
return f"ResourceReference(id={self.id!r}, name={self.name!r})"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TypedResourceReference(ResourceReference):
|
|
75
|
+
"""Resource reference with additional type information."""
|
|
76
|
+
public_id: Optional[str] = Field(default=None, alias="publicId")
|
|
77
|
+
description: Optional[str] = None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class NamedResource(BaseCollibraModel):
|
|
81
|
+
"""Base class for resources with id, name, and description."""
|
|
82
|
+
id: str
|
|
83
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
84
|
+
name: str
|
|
85
|
+
description: Optional[str] = None
|
|
86
|
+
|
|
87
|
+
def __str__(self) -> str:
|
|
88
|
+
return self.name
|
|
89
|
+
|
|
90
|
+
def __repr__(self) -> str:
|
|
91
|
+
return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})"
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TimestampMixin:
|
|
95
|
+
"""Mixin for resources with created/modified timestamps (fields only)."""
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _get_created_datetime(self: Any) -> Optional[datetime]:
|
|
100
|
+
"""Get created_on as datetime object."""
|
|
101
|
+
created_on = getattr(self, 'created_on', None)
|
|
102
|
+
if created_on:
|
|
103
|
+
return datetime.fromtimestamp(created_on / 1000)
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _get_last_modified_datetime(self: Any) -> Optional[datetime]:
|
|
108
|
+
"""Get last_modified_on as datetime object."""
|
|
109
|
+
last_modified_on = getattr(self, 'last_modified_on', None)
|
|
110
|
+
if last_modified_on:
|
|
111
|
+
return datetime.fromtimestamp(last_modified_on / 1000)
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# ============================================================================
|
|
116
|
+
# Status Model
|
|
117
|
+
# ============================================================================
|
|
118
|
+
|
|
119
|
+
class StatusModel(NamedResource):
|
|
120
|
+
"""Collibra Status Model."""
|
|
121
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
122
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
123
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
124
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
125
|
+
signifies_approval: Optional[bool] = Field(default=None, alias="signifiesApproval")
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
129
|
+
return _get_created_datetime(self)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
133
|
+
return _get_last_modified_datetime(self)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ============================================================================
|
|
137
|
+
# Asset Type Model
|
|
138
|
+
# ============================================================================
|
|
139
|
+
|
|
140
|
+
class AssetTypeModel(NamedResource):
|
|
141
|
+
"""Collibra Asset Type Model."""
|
|
142
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
143
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
144
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
145
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
146
|
+
public_id: Optional[str] = Field(default=None, alias="publicId")
|
|
147
|
+
symbol_type: Optional[str] = Field(default=None, alias="symbolType")
|
|
148
|
+
acronym_code: Optional[str] = Field(default=None, alias="acronymCode")
|
|
149
|
+
parent: Optional[ResourceReference] = None
|
|
150
|
+
is_meta: Optional[bool] = Field(default=False, alias="isMeta")
|
|
151
|
+
display_name_enabled: Optional[bool] = Field(default=False, alias="displayNameEnabled")
|
|
152
|
+
rating_enabled: Optional[bool] = Field(default=False, alias="ratingEnabled")
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
156
|
+
return _get_created_datetime(self)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
160
|
+
return _get_last_modified_datetime(self)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ============================================================================
|
|
164
|
+
# Community Model
|
|
165
|
+
# ============================================================================
|
|
166
|
+
|
|
167
|
+
class CommunityModel(NamedResource):
|
|
168
|
+
"""Collibra Community Model."""
|
|
169
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
170
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
171
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
172
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
173
|
+
parent: Optional[ResourceReference] = None
|
|
174
|
+
|
|
175
|
+
def is_root(self) -> bool:
|
|
176
|
+
"""Check if this is a root community (no parent)."""
|
|
177
|
+
return self.parent is None
|
|
178
|
+
|
|
179
|
+
@property
|
|
180
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
181
|
+
return _get_created_datetime(self)
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
185
|
+
return _get_last_modified_datetime(self)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ============================================================================
|
|
189
|
+
# Domain Model
|
|
190
|
+
# ============================================================================
|
|
191
|
+
|
|
192
|
+
class DomainTypeModel(NamedResource):
|
|
193
|
+
"""Collibra Domain Type Model."""
|
|
194
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
195
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
196
|
+
public_id: Optional[str] = Field(default=None, alias="publicId")
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
200
|
+
return _get_created_datetime(self)
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
204
|
+
return _get_last_modified_datetime(self)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
class DomainModel(NamedResource):
|
|
208
|
+
"""Collibra Domain Model."""
|
|
209
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
210
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
211
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
212
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
213
|
+
type: ResourceReference
|
|
214
|
+
community: ResourceReference
|
|
215
|
+
excluded_from_auto_hyperlinking: bool = Field(
|
|
216
|
+
default=False,
|
|
217
|
+
alias="excludedFromAutoHyperlinking"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
222
|
+
return _get_created_datetime(self)
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
226
|
+
return _get_last_modified_datetime(self)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ============================================================================
|
|
230
|
+
# Asset Model (Core)
|
|
231
|
+
# ============================================================================
|
|
232
|
+
|
|
233
|
+
class AssetModel(NamedResource):
|
|
234
|
+
"""
|
|
235
|
+
Collibra Asset Model - the core data object.
|
|
236
|
+
|
|
237
|
+
This model represents a Collibra asset with full type information
|
|
238
|
+
and convenient property accessors.
|
|
239
|
+
|
|
240
|
+
Example:
|
|
241
|
+
>>> asset = conn.asset.get_asset("uuid")
|
|
242
|
+
>>> print(f"Name: {asset.name}")
|
|
243
|
+
>>> print(f"Type: {asset.type.name}")
|
|
244
|
+
>>> print(f"Status: {asset.status.name}")
|
|
245
|
+
>>> print(f"Domain: {asset.domain.name}")
|
|
246
|
+
>>> if asset.is_approved:
|
|
247
|
+
... print("Asset is approved!")
|
|
248
|
+
"""
|
|
249
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
250
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
251
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
252
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
253
|
+
display_name: Optional[str] = Field(default=None, alias="displayName")
|
|
254
|
+
type: ResourceReference
|
|
255
|
+
status: ResourceReference
|
|
256
|
+
domain: ResourceReference
|
|
257
|
+
avg_rating: float = Field(default=0.0, alias="avgRating")
|
|
258
|
+
ratings_count: int = Field(default=0, alias="ratingsCount")
|
|
259
|
+
excluded_from_auto_hyperlinking: bool = Field(
|
|
260
|
+
default=False,
|
|
261
|
+
alias="excludedFromAutoHyperlinking"
|
|
262
|
+
)
|
|
263
|
+
articulation_score: Optional[float] = Field(default=None, alias="articulationScore")
|
|
264
|
+
|
|
265
|
+
@property
|
|
266
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
267
|
+
return _get_created_datetime(self)
|
|
268
|
+
|
|
269
|
+
@property
|
|
270
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
271
|
+
return _get_last_modified_datetime(self)
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def effective_name(self) -> str:
|
|
275
|
+
"""Get display_name if available, otherwise name."""
|
|
276
|
+
return self.display_name or self.name
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def type_name(self) -> str:
|
|
280
|
+
"""Convenience accessor for type.name."""
|
|
281
|
+
return self.type.name or "Unknown"
|
|
282
|
+
|
|
283
|
+
@property
|
|
284
|
+
def status_name(self) -> str:
|
|
285
|
+
"""Convenience accessor for status.name."""
|
|
286
|
+
return self.status.name or "Unknown"
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def domain_name(self) -> str:
|
|
290
|
+
"""Convenience accessor for domain.name."""
|
|
291
|
+
return self.domain.name or "Unknown"
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def has_rating(self) -> bool:
|
|
295
|
+
"""Check if asset has any ratings."""
|
|
296
|
+
return self.ratings_count > 0
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
# ============================================================================
|
|
300
|
+
# User Model
|
|
301
|
+
# ============================================================================
|
|
302
|
+
|
|
303
|
+
class UserModel(NamedResource):
|
|
304
|
+
"""Collibra User Model."""
|
|
305
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
306
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
307
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
308
|
+
last_modified_by: Optional[str] = Field(default=None, alias="lastModifiedBy")
|
|
309
|
+
first_name: Optional[str] = Field(default=None, alias="firstName")
|
|
310
|
+
last_name: Optional[str] = Field(default=None, alias="lastName")
|
|
311
|
+
email_address: Optional[str] = Field(default=None, alias="emailAddress")
|
|
312
|
+
gender: Optional[str] = None
|
|
313
|
+
language: Optional[str] = None
|
|
314
|
+
activated: bool = False
|
|
315
|
+
enabled: bool = False
|
|
316
|
+
ldap_user: Optional[bool] = Field(default=None, alias="ldapUser")
|
|
317
|
+
guest_user: Optional[bool] = Field(default=None, alias="guestUser")
|
|
318
|
+
api_user: Optional[bool] = Field(default=None, alias="apiUser")
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
322
|
+
return _get_created_datetime(self)
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
326
|
+
return _get_last_modified_datetime(self)
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def full_name(self) -> str:
|
|
330
|
+
"""Get user's full name."""
|
|
331
|
+
parts = [self.first_name, self.last_name]
|
|
332
|
+
return " ".join(p for p in parts if p) or self.name
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def is_active(self) -> bool:
|
|
336
|
+
"""Check if user is both activated and enabled."""
|
|
337
|
+
return self.activated and self.enabled
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# ============================================================================
|
|
341
|
+
# Attribute Model
|
|
342
|
+
# ============================================================================
|
|
343
|
+
|
|
344
|
+
class AttributeTypeModel(NamedResource):
|
|
345
|
+
"""Collibra Attribute Type Model."""
|
|
346
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
347
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
348
|
+
public_id: Optional[str] = Field(default=None, alias="publicId")
|
|
349
|
+
kind: Optional[str] = None
|
|
350
|
+
statistics_enabled: Optional[bool] = Field(default=False, alias="statisticsEnabled")
|
|
351
|
+
is_integer: Optional[bool] = Field(default=False, alias="isInteger")
|
|
352
|
+
allowed_values: Optional[List[str]] = Field(default=None, alias="allowedValues")
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
356
|
+
return _get_created_datetime(self)
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
360
|
+
return _get_last_modified_datetime(self)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
class AttributeModel(BaseCollibraModel):
|
|
364
|
+
"""Collibra Attribute Model."""
|
|
365
|
+
id: str
|
|
366
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
367
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
368
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
369
|
+
type: ResourceReference
|
|
370
|
+
value: Any
|
|
371
|
+
asset: Optional[ResourceReference] = None
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
375
|
+
return _get_created_datetime(self)
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
379
|
+
return _get_last_modified_datetime(self)
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def type_name(self) -> str:
|
|
383
|
+
"""Convenience accessor for type.name."""
|
|
384
|
+
return self.type.name or "Unknown"
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def string_value(self) -> str:
|
|
388
|
+
"""Get value as string."""
|
|
389
|
+
if self.value is None:
|
|
390
|
+
return ""
|
|
391
|
+
return str(self.value)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
# ============================================================================
|
|
395
|
+
# Relation Model
|
|
396
|
+
# ============================================================================
|
|
397
|
+
|
|
398
|
+
class RelationTypeModel(NamedResource):
|
|
399
|
+
"""Collibra Relation Type Model."""
|
|
400
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
401
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
402
|
+
public_id: Optional[str] = Field(default=None, alias="publicId")
|
|
403
|
+
role: Optional[str] = None
|
|
404
|
+
co_role: Optional[str] = Field(default=None, alias="coRole")
|
|
405
|
+
source_type: Optional[ResourceReference] = Field(default=None, alias="sourceType")
|
|
406
|
+
target_type: Optional[ResourceReference] = Field(default=None, alias="targetType")
|
|
407
|
+
|
|
408
|
+
@property
|
|
409
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
410
|
+
return _get_created_datetime(self)
|
|
411
|
+
|
|
412
|
+
@property
|
|
413
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
414
|
+
return _get_last_modified_datetime(self)
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
class RelationModel(BaseCollibraModel):
|
|
418
|
+
"""Collibra Relation Model."""
|
|
419
|
+
id: str
|
|
420
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
421
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
422
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
423
|
+
type: ResourceReference
|
|
424
|
+
source: ResourceReference
|
|
425
|
+
target: ResourceReference
|
|
426
|
+
starting_date: Optional[int] = Field(default=None, alias="startingDate")
|
|
427
|
+
ending_date: Optional[int] = Field(default=None, alias="endingDate")
|
|
428
|
+
|
|
429
|
+
@property
|
|
430
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
431
|
+
return _get_created_datetime(self)
|
|
432
|
+
|
|
433
|
+
@property
|
|
434
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
435
|
+
return _get_last_modified_datetime(self)
|
|
436
|
+
|
|
437
|
+
@property
|
|
438
|
+
def type_name(self) -> str:
|
|
439
|
+
"""Convenience accessor for type.name."""
|
|
440
|
+
return self.type.name or "Unknown"
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# ============================================================================
|
|
444
|
+
# Responsibility Model
|
|
445
|
+
# ============================================================================
|
|
446
|
+
|
|
447
|
+
class RoleModel(NamedResource):
|
|
448
|
+
"""Collibra Role Model."""
|
|
449
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
450
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
451
|
+
global_role: Optional[bool] = Field(default=False, alias="global")
|
|
452
|
+
|
|
453
|
+
@property
|
|
454
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
455
|
+
return _get_created_datetime(self)
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
459
|
+
return _get_last_modified_datetime(self)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class ResponsibilityModel(BaseCollibraModel):
|
|
463
|
+
"""Collibra Responsibility Model."""
|
|
464
|
+
id: str
|
|
465
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
466
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
467
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
468
|
+
role: ResourceReference
|
|
469
|
+
owner: ResourceReference
|
|
470
|
+
resource: Optional[ResourceReference] = None
|
|
471
|
+
base_resource: Optional[ResourceReference] = Field(default=None, alias="baseResource")
|
|
472
|
+
|
|
473
|
+
@property
|
|
474
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
475
|
+
return _get_created_datetime(self)
|
|
476
|
+
|
|
477
|
+
@property
|
|
478
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
479
|
+
return _get_last_modified_datetime(self)
|
|
480
|
+
|
|
481
|
+
@property
|
|
482
|
+
def role_name(self) -> str:
|
|
483
|
+
"""Convenience accessor for role.name."""
|
|
484
|
+
return self.role.name or "Unknown"
|
|
485
|
+
|
|
486
|
+
@property
|
|
487
|
+
def owner_name(self) -> str:
|
|
488
|
+
"""Convenience accessor for owner.name."""
|
|
489
|
+
return self.owner.name or "Unknown"
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# ============================================================================
|
|
493
|
+
# Comment Model
|
|
494
|
+
# ============================================================================
|
|
495
|
+
|
|
496
|
+
class CommentModel(BaseCollibraModel):
|
|
497
|
+
"""Collibra Comment Model."""
|
|
498
|
+
id: str
|
|
499
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
500
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
501
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
502
|
+
created_by: Optional[str] = Field(default=None, alias="createdBy")
|
|
503
|
+
content: str
|
|
504
|
+
base_resource: Optional[ResourceReference] = Field(default=None, alias="baseResource")
|
|
505
|
+
parent: Optional[ResourceReference] = None
|
|
506
|
+
|
|
507
|
+
@property
|
|
508
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
509
|
+
return _get_created_datetime(self)
|
|
510
|
+
|
|
511
|
+
@property
|
|
512
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
513
|
+
return _get_last_modified_datetime(self)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
# ============================================================================
|
|
517
|
+
# Search Models
|
|
518
|
+
# ============================================================================
|
|
519
|
+
|
|
520
|
+
class SearchHighlight(BaseCollibraModel):
|
|
521
|
+
"""Search result highlight."""
|
|
522
|
+
field: Optional[str] = None
|
|
523
|
+
values: Optional[List[str]] = None
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
class SearchResource(BaseCollibraModel):
|
|
527
|
+
"""The resource found in search."""
|
|
528
|
+
id: str
|
|
529
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
530
|
+
name: Optional[str] = None
|
|
531
|
+
display_name: Optional[str] = Field(default=None, alias="displayName")
|
|
532
|
+
description: Optional[str] = None
|
|
533
|
+
|
|
534
|
+
@property
|
|
535
|
+
def effective_name(self) -> str:
|
|
536
|
+
"""Get display_name if available, otherwise name."""
|
|
537
|
+
return self.display_name or self.name or "Unnamed"
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
class SearchResultModel(BaseCollibraModel):
|
|
541
|
+
"""Model for a single search result."""
|
|
542
|
+
resource: SearchResource
|
|
543
|
+
highlights: Optional[List[SearchHighlight]] = None
|
|
544
|
+
score: Optional[float] = 0.0
|
|
545
|
+
|
|
546
|
+
@property
|
|
547
|
+
def id(self) -> str:
|
|
548
|
+
"""Convenience accessor for resource.id."""
|
|
549
|
+
return self.resource.id
|
|
550
|
+
|
|
551
|
+
@property
|
|
552
|
+
def name(self) -> str:
|
|
553
|
+
"""Convenience accessor for resource name."""
|
|
554
|
+
return self.resource.effective_name
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
# ============================================================================
|
|
558
|
+
# Workflow Models
|
|
559
|
+
# ============================================================================
|
|
560
|
+
|
|
561
|
+
class WorkflowDefinitionModel(NamedResource):
|
|
562
|
+
"""Collibra Workflow Definition Model."""
|
|
563
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
564
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
565
|
+
enabled: bool = False
|
|
566
|
+
start_events: Optional[List[str]] = Field(default=None, alias="startEvents")
|
|
567
|
+
process_id: Optional[str] = Field(default=None, alias="processId")
|
|
568
|
+
guest_user_accessible: Optional[bool] = Field(default=False, alias="guestUserAccessible")
|
|
569
|
+
registered_user_accessible: Optional[bool] = Field(default=False, alias="registeredUserAccessible")
|
|
570
|
+
candidate_user_check_disabled: Optional[bool] = Field(default=False, alias="candidateUserCheckDisabled")
|
|
571
|
+
global_create: Optional[bool] = Field(default=False, alias="globalCreate")
|
|
572
|
+
start_label: Optional[str] = Field(default=None, alias="startLabel")
|
|
573
|
+
domain_type: Optional[ResourceReference] = Field(default=None, alias="domainType")
|
|
574
|
+
asset_type: Optional[ResourceReference] = Field(default=None, alias="assetType")
|
|
575
|
+
community: Optional[ResourceReference] = None
|
|
576
|
+
domain: Optional[ResourceReference] = None
|
|
577
|
+
form_required: Optional[bool] = Field(default=False, alias="formRequired")
|
|
578
|
+
business_item_resource_type: Optional[str] = Field(default=None, alias="businessItemResourceType")
|
|
579
|
+
exclusivity: Optional[str] = None
|
|
580
|
+
|
|
581
|
+
@property
|
|
582
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
583
|
+
return _get_created_datetime(self)
|
|
584
|
+
|
|
585
|
+
@property
|
|
586
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
587
|
+
return _get_last_modified_datetime(self)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
class WorkflowInstanceModel(BaseCollibraModel):
|
|
591
|
+
"""Collibra Workflow Instance Model."""
|
|
592
|
+
id: str
|
|
593
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
594
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
595
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
596
|
+
workflow_definition: Optional[ResourceReference] = Field(default=None, alias="workflowDefinition")
|
|
597
|
+
business_item: Optional[ResourceReference] = Field(default=None, alias="businessItem")
|
|
598
|
+
start_date: Optional[int] = Field(default=None, alias="startDate")
|
|
599
|
+
ended: bool = False
|
|
600
|
+
in_error: bool = Field(default=False, alias="inError")
|
|
601
|
+
sub_process_instances_count: int = Field(default=0, alias="subProcessInstancesCount")
|
|
602
|
+
|
|
603
|
+
@property
|
|
604
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
605
|
+
return _get_created_datetime(self)
|
|
606
|
+
|
|
607
|
+
@property
|
|
608
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
609
|
+
return _get_last_modified_datetime(self)
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
class WorkflowTaskModel(BaseCollibraModel):
|
|
613
|
+
"""Collibra Workflow Task Model."""
|
|
614
|
+
id: str
|
|
615
|
+
resource_type: Optional[str] = Field(default=None, alias="resourceType")
|
|
616
|
+
created_on: Optional[int] = Field(default=None, alias="createdOn")
|
|
617
|
+
last_modified_on: Optional[int] = Field(default=None, alias="lastModifiedOn")
|
|
618
|
+
key: Optional[str] = None
|
|
619
|
+
type: Optional[str] = None
|
|
620
|
+
title: Optional[str] = None
|
|
621
|
+
description: Optional[str] = None
|
|
622
|
+
due_date: Optional[int] = Field(default=None, alias="dueDate")
|
|
623
|
+
priority: Optional[int] = None
|
|
624
|
+
cancelable: bool = False
|
|
625
|
+
reassignable: bool = False
|
|
626
|
+
form_required: Optional[bool] = Field(default=False, alias="formRequired")
|
|
627
|
+
owner: Optional[ResourceReference] = None
|
|
628
|
+
candidate_users: Optional[List[ResourceReference]] = Field(default=None, alias="candidateUsers")
|
|
629
|
+
workflow_instance: Optional[ResourceReference] = Field(default=None, alias="workflowInstance")
|
|
630
|
+
workflow_definition: Optional[ResourceReference] = Field(default=None, alias="workflowDefinition")
|
|
631
|
+
business_item: Optional[ResourceReference] = Field(default=None, alias="businessItem")
|
|
632
|
+
|
|
633
|
+
@property
|
|
634
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
635
|
+
return _get_created_datetime(self)
|
|
636
|
+
|
|
637
|
+
@property
|
|
638
|
+
def last_modified_datetime(self) -> Optional[datetime]:
|
|
639
|
+
return _get_last_modified_datetime(self)
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
# ============================================================================
|
|
643
|
+
# Paginated Response Model
|
|
644
|
+
# ============================================================================
|
|
645
|
+
|
|
646
|
+
class PaginatedResponseModel(BaseCollibraModel, Generic[T]):
|
|
647
|
+
"""
|
|
648
|
+
Generic paginated response model.
|
|
649
|
+
|
|
650
|
+
This model wraps API responses that return paginated results,
|
|
651
|
+
providing convenient iteration and pagination helpers.
|
|
652
|
+
|
|
653
|
+
Example:
|
|
654
|
+
>>> response = conn.asset.find_assets()
|
|
655
|
+
>>> print(f"Total: {response.total}")
|
|
656
|
+
>>> for asset in response:
|
|
657
|
+
... print(asset.name)
|
|
658
|
+
>>> if response.has_more:
|
|
659
|
+
... next_page = conn.asset.find_assets(offset=response.next_offset)
|
|
660
|
+
"""
|
|
661
|
+
results: List[T] = Field(default_factory=list)
|
|
662
|
+
total: int = 0
|
|
663
|
+
offset: int = 0
|
|
664
|
+
limit: int = 0
|
|
665
|
+
|
|
666
|
+
@property
|
|
667
|
+
def has_more(self) -> bool:
|
|
668
|
+
"""Check if there are more results available."""
|
|
669
|
+
if self.limit <= 0:
|
|
670
|
+
return False
|
|
671
|
+
return self.offset + self.limit < self.total
|
|
672
|
+
|
|
673
|
+
@property
|
|
674
|
+
def next_offset(self) -> int:
|
|
675
|
+
"""Get the offset for the next page."""
|
|
676
|
+
return self.offset + len(self.results)
|
|
677
|
+
|
|
678
|
+
@property
|
|
679
|
+
def page_count(self) -> int:
|
|
680
|
+
"""Get the total number of pages."""
|
|
681
|
+
if self.limit <= 0:
|
|
682
|
+
return 1
|
|
683
|
+
return (self.total + self.limit - 1) // self.limit
|
|
684
|
+
|
|
685
|
+
@property
|
|
686
|
+
def current_page(self) -> int:
|
|
687
|
+
"""Get the current page number (1-indexed)."""
|
|
688
|
+
if self.limit <= 0:
|
|
689
|
+
return 1
|
|
690
|
+
return (self.offset // self.limit) + 1
|
|
691
|
+
|
|
692
|
+
def __len__(self) -> int:
|
|
693
|
+
"""Return the number of results in this page."""
|
|
694
|
+
return len(self.results)
|
|
695
|
+
|
|
696
|
+
def __iter__(self) -> Iterator[T]:
|
|
697
|
+
"""Iterate over results in this page."""
|
|
698
|
+
return iter(self.results)
|
|
699
|
+
|
|
700
|
+
def __getitem__(self, index: int) -> T:
|
|
701
|
+
"""Get a result by index."""
|
|
702
|
+
return self.results[index]
|
|
703
|
+
|
|
704
|
+
def __bool__(self) -> bool:
|
|
705
|
+
"""Check if there are any results."""
|
|
706
|
+
return len(self.results) > 0
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
# ============================================================================
|
|
710
|
+
# Full Asset Profile Model
|
|
711
|
+
# ============================================================================
|
|
712
|
+
|
|
713
|
+
class RelationSummary(BaseCollibraModel):
|
|
714
|
+
"""Summary of a relation for an asset."""
|
|
715
|
+
id: str
|
|
716
|
+
target_id: Optional[str] = None
|
|
717
|
+
target_name: Optional[str] = None
|
|
718
|
+
target_type: Optional[str] = None
|
|
719
|
+
target_status: Optional[str] = None
|
|
720
|
+
source_id: Optional[str] = None
|
|
721
|
+
source_name: Optional[str] = None
|
|
722
|
+
source_type: Optional[str] = None
|
|
723
|
+
source_status: Optional[str] = None
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
class RelationsGrouped(BaseCollibraModel):
|
|
727
|
+
"""Relations grouped by direction and type."""
|
|
728
|
+
outgoing: Dict[str, List[RelationSummary]] = Field(default_factory=dict)
|
|
729
|
+
incoming: Dict[str, List[RelationSummary]] = Field(default_factory=dict)
|
|
730
|
+
outgoing_count: int = 0
|
|
731
|
+
incoming_count: int = 0
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
class ResponsibilitySummary(BaseCollibraModel):
|
|
735
|
+
"""Summary of a responsibility assignment."""
|
|
736
|
+
role: str
|
|
737
|
+
owner: str
|
|
738
|
+
owner_id: Optional[str] = None
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
class AssetProfileModel(BaseCollibraModel):
|
|
742
|
+
"""
|
|
743
|
+
Complete asset profile with all related information.
|
|
744
|
+
|
|
745
|
+
This model provides a comprehensive view of an asset including
|
|
746
|
+
its attributes, relations, responsibilities, and more.
|
|
747
|
+
|
|
748
|
+
Example:
|
|
749
|
+
>>> profile = conn.asset.get_full_profile("uuid")
|
|
750
|
+
>>> print(profile.asset.name)
|
|
751
|
+
>>> print(profile.attributes.get("Description"))
|
|
752
|
+
>>> for rel_type, targets in profile.relations.outgoing.items():
|
|
753
|
+
... print(f"{rel_type}: {len(targets)} targets")
|
|
754
|
+
"""
|
|
755
|
+
asset: AssetModel
|
|
756
|
+
attributes: Dict[str, Any] = Field(default_factory=dict)
|
|
757
|
+
relations: RelationsGrouped = Field(default_factory=RelationsGrouped)
|
|
758
|
+
responsibilities: List[ResponsibilitySummary] = Field(default_factory=list)
|
|
759
|
+
comments: List[CommentModel] = Field(default_factory=list)
|
|
760
|
+
activities: List[Dict[str, Any]] = Field(default_factory=list)
|
|
761
|
+
|
|
762
|
+
@property
|
|
763
|
+
def description(self) -> Optional[str]:
|
|
764
|
+
"""Get the Description attribute if available."""
|
|
765
|
+
return self.attributes.get("Description")
|
|
766
|
+
|
|
767
|
+
@property
|
|
768
|
+
def data_steward(self) -> Optional[str]:
|
|
769
|
+
"""Get the Data Steward if assigned."""
|
|
770
|
+
for resp in self.responsibilities:
|
|
771
|
+
if "steward" in resp.role.lower():
|
|
772
|
+
return resp.owner
|
|
773
|
+
return None
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
# ============================================================================
|
|
777
|
+
# Type Aliases for common patterns
|
|
778
|
+
# ============================================================================
|
|
779
|
+
|
|
780
|
+
AssetList = PaginatedResponseModel[AssetModel]
|
|
781
|
+
DomainList = PaginatedResponseModel[DomainModel]
|
|
782
|
+
CommunityList = PaginatedResponseModel[CommunityModel]
|
|
783
|
+
UserList = PaginatedResponseModel[UserModel]
|
|
784
|
+
AttributeList = PaginatedResponseModel[AttributeModel]
|
|
785
|
+
RelationList = PaginatedResponseModel[RelationModel]
|
|
786
|
+
SearchResults = PaginatedResponseModel[SearchResultModel]
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
# ============================================================================
|
|
790
|
+
# Model Factory Functions
|
|
791
|
+
# ============================================================================
|
|
792
|
+
|
|
793
|
+
def parse_asset(data: Dict[str, Any]) -> AssetModel:
|
|
794
|
+
"""Parse a dictionary into an AssetModel."""
|
|
795
|
+
return AssetModel.model_validate(data)
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def parse_assets(data: Dict[str, Any]) -> AssetList:
|
|
799
|
+
"""Parse a paginated response into AssetList."""
|
|
800
|
+
results = [AssetModel.model_validate(item) for item in data.get("results", [])]
|
|
801
|
+
return PaginatedResponseModel[AssetModel](
|
|
802
|
+
results=results,
|
|
803
|
+
total=data.get("total", 0),
|
|
804
|
+
offset=data.get("offset", 0),
|
|
805
|
+
limit=data.get("limit", 0)
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def parse_domain(data: Dict[str, Any]) -> DomainModel:
|
|
810
|
+
"""Parse a dictionary into a DomainModel."""
|
|
811
|
+
return DomainModel.model_validate(data)
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def parse_domains(data: Dict[str, Any]) -> DomainList:
|
|
815
|
+
"""Parse a paginated response into DomainList."""
|
|
816
|
+
results = [DomainModel.model_validate(item) for item in data.get("results", [])]
|
|
817
|
+
return PaginatedResponseModel[DomainModel](
|
|
818
|
+
results=results,
|
|
819
|
+
total=data.get("total", 0),
|
|
820
|
+
offset=data.get("offset", 0),
|
|
821
|
+
limit=data.get("limit", 0)
|
|
822
|
+
)
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def parse_community(data: Dict[str, Any]) -> CommunityModel:
|
|
826
|
+
"""Parse a dictionary into a CommunityModel."""
|
|
827
|
+
return CommunityModel.model_validate(data)
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
def parse_communities(data: Dict[str, Any]) -> CommunityList:
|
|
831
|
+
"""Parse a paginated response into CommunityList."""
|
|
832
|
+
results = [CommunityModel.model_validate(item) for item in data.get("results", [])]
|
|
833
|
+
return PaginatedResponseModel[CommunityModel](
|
|
834
|
+
results=results,
|
|
835
|
+
total=data.get("total", 0),
|
|
836
|
+
offset=data.get("offset", 0),
|
|
837
|
+
limit=data.get("limit", 0)
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def parse_user(data: Dict[str, Any]) -> UserModel:
|
|
842
|
+
"""Parse a dictionary into a UserModel."""
|
|
843
|
+
return UserModel.model_validate(data)
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
def parse_users(data: Dict[str, Any]) -> UserList:
|
|
847
|
+
"""Parse a paginated response into UserList."""
|
|
848
|
+
results = [UserModel.model_validate(item) for item in data.get("results", [])]
|
|
849
|
+
return PaginatedResponseModel[UserModel](
|
|
850
|
+
results=results,
|
|
851
|
+
total=data.get("total", 0),
|
|
852
|
+
offset=data.get("offset", 0),
|
|
853
|
+
limit=data.get("limit", 0)
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
def parse_attribute(data: Dict[str, Any]) -> AttributeModel:
|
|
858
|
+
"""Parse a dictionary into an AttributeModel."""
|
|
859
|
+
return AttributeModel.model_validate(data)
|
|
860
|
+
|
|
861
|
+
|
|
862
|
+
def parse_attributes(data: Dict[str, Any]) -> AttributeList:
|
|
863
|
+
"""Parse a paginated response into AttributeList."""
|
|
864
|
+
results = [AttributeModel.model_validate(item) for item in data.get("results", [])]
|
|
865
|
+
return PaginatedResponseModel[AttributeModel](
|
|
866
|
+
results=results,
|
|
867
|
+
total=data.get("total", 0),
|
|
868
|
+
offset=data.get("offset", 0),
|
|
869
|
+
limit=data.get("limit", 0)
|
|
870
|
+
)
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
def parse_relation(data: Dict[str, Any]) -> RelationModel:
|
|
874
|
+
"""Parse a dictionary into a RelationModel."""
|
|
875
|
+
return RelationModel.model_validate(data)
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
def parse_relations(data: Dict[str, Any]) -> RelationList:
|
|
879
|
+
"""Parse a paginated response into RelationList."""
|
|
880
|
+
results = [RelationModel.model_validate(item) for item in data.get("results", [])]
|
|
881
|
+
return PaginatedResponseModel[RelationModel](
|
|
882
|
+
results=results,
|
|
883
|
+
total=data.get("total", 0),
|
|
884
|
+
offset=data.get("offset", 0),
|
|
885
|
+
limit=data.get("limit", 0)
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def parse_search_results(data: Dict[str, Any]) -> SearchResults:
|
|
890
|
+
"""Parse a paginated response into SearchResults."""
|
|
891
|
+
results = [SearchResultModel.model_validate(item) for item in data.get("results", [])]
|
|
892
|
+
return PaginatedResponseModel[SearchResultModel](
|
|
893
|
+
results=results,
|
|
894
|
+
total=data.get("total", 0),
|
|
895
|
+
offset=data.get("offset", 0),
|
|
896
|
+
limit=data.get("limit", 0)
|
|
897
|
+
)
|