Skip to content

Commit 190fd91

Browse files
committed
APP-6703: Added AtlanResponse and AtlanTagTranslator models
1 parent 92cce13 commit 190fd91

4 files changed

Lines changed: 135 additions & 36 deletions

File tree

pyatlan/client/asset.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
AssetRequest,
7171
AssetResponse,
7272
AtlanObject,
73+
AtlanResponse,
7374
AtlanTag,
7475
AtlanTagName,
7576
BulkRequest,
@@ -246,7 +247,10 @@ def search(self, criteria: IndexSearchRequest, bulk=False) -> IndexSearchResults
246247
unflatten_custom_metadata_for_entity(
247248
entity=entity, attributes=criteria.attributes
248249
)
249-
assets = parse_obj_as(List[Asset], raw_json["entities"])
250+
translated_entities = AtlanResponse(
251+
raw_json=raw_json["entities"], client=self._client
252+
).to_dict()
253+
assets = parse_obj_as(List[Asset], translated_entities)
250254
except ValidationError as err:
251255
raise ErrorCode.JSON_ERROR.exception_with_parameters(
252256
raw_json, 200, str(err)
@@ -318,7 +322,10 @@ def get_lineage_list(
318322
unflatten_custom_metadata_for_entity(
319323
entity=entity, attributes=lineage_request.attributes
320324
)
321-
assets = parse_obj_as(List[Asset], raw_json["entities"])
325+
translated_entities = AtlanResponse(
326+
raw_json=raw_json["entities"], client=self._client
327+
).to_dict()
328+
assets = parse_obj_as(List[Asset], translated_entities)
322329
has_more = parse_obj_as(bool, raw_json["hasMore"])
323330
except ValidationError as err:
324331
raise ErrorCode.JSON_ERROR.exception_with_parameters(
@@ -546,7 +553,10 @@ def _handle_relationships(self, raw_json):
546553
raw_json["entity"]["relationshipAttributes"]
547554
)
548555
raw_json["entity"]["relationshipAttributes"] = {}
549-
asset = AssetResponse[A](**raw_json).entity
556+
translated_raw_json = AtlanResponse(
557+
raw_json=raw_json, client=self._client
558+
).to_dict()
559+
asset = AssetResponse[A](**translated_raw_json).entity
550560
asset.is_incomplete = False
551561
return asset
552562

@@ -2082,7 +2092,10 @@ def _process_entities(self, entities):
20822092
unflatten_custom_metadata_for_entity(
20832093
entity=entity, attributes=self._criteria.attributes
20842094
)
2085-
self._assets = parse_obj_as(List[Asset], entities)
2095+
translated_entities = AtlanResponse(
2096+
raw_json=entities, client=self._client
2097+
).to_dict()
2098+
self._assets = parse_obj_as(List[Asset], translated_entities)
20862099

20872100
def _update_first_last_record_creation_times(self):
20882101
self._first_record_creation_time = self._last_record_creation_time = -2

pyatlan/model/assets/core/referenceable.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,21 @@ def can_be_archived(self) -> bool:
8585
"""
8686
return True
8787

88+
# @property
89+
# def atlan_tag_names(self) -> List[str]:
90+
# from pyatlan.client.atlan import AtlanClient
91+
# from pyatlan.model.constants import DELETED_
92+
93+
# if self.classification_names:
94+
# return [
95+
# AtlanClient.get_current_client().atlan_tag_cache.get_name_for_id(tag_id)
96+
# or DELETED_
97+
# for tag_id in self.classification_names
98+
# ]
99+
# return []
88100
@property
89101
def atlan_tag_names(self) -> List[str]:
90-
from pyatlan.client.atlan import AtlanClient
91-
from pyatlan.model.constants import DELETED_
92-
93-
if self.classification_names:
94-
return [
95-
AtlanClient.get_current_client().atlan_tag_cache.get_name_for_id(tag_id)
96-
or DELETED_
97-
for tag_id in self.classification_names
98-
]
99-
return []
102+
return self.classification_names or []
100103

101104
def __setattr__(self, name, value):
102105
if name in Referenceable._convenience_properties:

pyatlan/model/core.py

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@
1313

1414
if TYPE_CHECKING:
1515
from dataclasses import dataclass
16+
17+
from pyatlan.client.atlan import AtlanClient
1618
else:
1719
from pydantic.v1.dataclasses import dataclass
1820

19-
from typing import Any, Dict, Generic, List, Optional, TypeVar
21+
from typing import Any, Dict, Generic, List, Optional, TypeVar, Union
2022

2123
from pydantic.v1.generics import GenericModel
2224

2325
from pyatlan.model.constants import DELETED_, DELETED_SENTINEL
2426
from pyatlan.model.enums import AnnouncementType, EntityStatus, SaveSemantic
2527
from pyatlan.model.structs import SourceTagAttachment
28+
from pyatlan.model.translators import AtlanTagTranslator
2629

2730

2831
class AtlanTagName:
@@ -178,6 +181,38 @@ def from_yaml(cls, yaml_str: str):
178181
return cls(**data)
179182

180183

184+
class AtlanResponse:
185+
def __init__(self, raw_json: Dict[str, Any], client: AtlanClient):
186+
self.raw_json = raw_json
187+
self.client = client
188+
self.translators = [
189+
AtlanTagTranslator(client),
190+
# Register more translators here
191+
]
192+
self.translated = self._deep_translate(self.raw_json)
193+
194+
def _deep_translate(
195+
self, data: Union[Dict[str, Any], List[Any], Any]
196+
) -> Union[Dict[str, Any], List[Any], Any]:
197+
if isinstance(data, dict):
198+
# Apply translators to this dict if any apply
199+
for translator in self.translators:
200+
if translator.applies_to(data):
201+
data = translator.translate(data)
202+
203+
# Recursively apply to each value
204+
return {key: self._deep_translate(value) for key, value in data.items()}
205+
206+
elif isinstance(data, list):
207+
return [self._deep_translate(item) for item in data]
208+
209+
else:
210+
return data
211+
212+
def to_dict(self) -> Dict[str, Any]:
213+
return self.translated
214+
215+
181216
class SearchRequest(AtlanObject, ABC):
182217
attributes: Optional[List[str]] = Field(
183218
default_factory=list,
@@ -202,7 +237,7 @@ class AtlanTag(AtlanObject):
202237
class Config:
203238
extra = "forbid"
204239

205-
type_name: Optional[AtlanTagName] = Field(
240+
type_name: Optional[str] = Field(
206241
default=None,
207242
description="Name of the type definition that defines this instance.\n",
208243
alias="typeName",
@@ -255,26 +290,26 @@ class Config:
255290
def source_tag_attachements(self) -> List[SourceTagAttachment]:
256291
return self._source_tag_attachements
257292

258-
@validator("type_name", pre=True)
259-
def type_name_is_tag_name(cls, value):
260-
if isinstance(value, AtlanTagName):
261-
return value
262-
return AtlanTagName._convert_to_display_text(value)
263-
264-
def __init__(self, *args, **kwargs):
265-
from pyatlan.client.atlan import AtlanClient
266-
267-
super().__init__(*args, **kwargs)
268-
if self.type_name != AtlanTagName.get_deleted_sentinel():
269-
attr_id = AtlanClient.get_current_client().atlan_tag_cache.get_source_tags_attr_id(
270-
self.type_name.id
271-
)
272-
if self.attributes and attr_id in self.attributes:
273-
self._source_tag_attachements = [
274-
SourceTagAttachment(**source_tag["attributes"])
275-
for source_tag in self.attributes[attr_id]
276-
if isinstance(source_tag, dict) and source_tag.get("attributes")
277-
]
293+
# @validator("type_name", pre=True)
294+
# def type_name_is_tag_name(cls, value):
295+
# if isinstance(value, AtlanTagName):
296+
# return value
297+
# return AtlanTagName._convert_to_display_text(value)
298+
299+
# def __init__(self, *args, **kwargs):
300+
# from pyatlan.client.atlan import AtlanClient
301+
302+
# super().__init__(*args, **kwargs)
303+
# if self.type_name != AtlanTagName.get_deleted_sentinel():
304+
# attr_id = AtlanClient.get_current_client().atlan_tag_cache.get_source_tags_attr_id(
305+
# self.type_name.id
306+
# )
307+
# if self.attributes and attr_id in self.attributes:
308+
# self._source_tag_attachements = [
309+
# SourceTagAttachment(**source_tag["attributes"])
310+
# for source_tag in self.attributes[attr_id]
311+
# if isinstance(source_tag, dict) and source_tag.get("attributes")
312+
# ]
278313

279314
@classmethod
280315
def of(

pyatlan/model/translators.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from __future__ import annotations
2+
3+
from abc import ABC, abstractmethod
4+
from typing import TYPE_CHECKING, Any, Dict
5+
6+
if TYPE_CHECKING:
7+
from pyatlan.client.atlan import AtlanClient
8+
9+
10+
class BaseTranslator(ABC):
11+
@abstractmethod
12+
def applies_to(self, data: Dict[str, Any]) -> bool:
13+
pass
14+
15+
@abstractmethod
16+
def translate(self, data: Dict[str, Any]) -> Dict[str, Any]:
17+
pass
18+
19+
20+
class AtlanTagTranslator(BaseTranslator):
21+
_TYPE_NAME = "typeName"
22+
_CLASSIFICATIONS = "classifications"
23+
_CLASSIFICATION_NAMES = "classificationNames"
24+
25+
def __init__(self, client: AtlanClient):
26+
self.client = client
27+
28+
def applies_to(self, data: Dict[str, Any]) -> bool:
29+
return self._CLASSIFICATION_NAMES in data or self._CLASSIFICATIONS in data
30+
31+
def translate(self, data: Dict[str, Any]) -> Dict[str, Any]:
32+
raw_json = data.copy()
33+
34+
if self._CLASSIFICATION_NAMES in raw_json:
35+
raw_json[self._CLASSIFICATION_NAMES] = [
36+
self.client.atlan_tag_cache.get_name_for_id(tag_id)
37+
for tag_id in raw_json[self._CLASSIFICATION_NAMES]
38+
]
39+
40+
if self._CLASSIFICATIONS in raw_json:
41+
for classification in raw_json[self._CLASSIFICATIONS]:
42+
tag_id = classification.get(self._TYPE_NAME)
43+
if tag_id:
44+
classification[self._TYPE_NAME] = (
45+
self.client.atlan_tag_cache.get_name_for_id(tag_id)
46+
)
47+
48+
return raw_json

0 commit comments

Comments
 (0)