Skip to content

Commit 4f055f3

Browse files
committed
Refactored to support type hints for MutatedEntities
1 parent c1c5951 commit 4f055f3

File tree

6 files changed

+323
-127
lines changed

6 files changed

+323
-127
lines changed

pyatlan/client/entity.py

+26-22
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@
1717
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1818
# See the License for the specific language governing permissions and
1919
# limitations under the License.
20-
from typing import Type, TypeVar
20+
from typing import Type, TypeVar, Union
2121

2222
from pyatlan.client.atlan import AtlanClient
23-
from pyatlan.model.core import (
24-
AssetResponse,
23+
from pyatlan.model.assets import (
24+
Asset,
25+
Referenceable,
26+
AtlasGlossary,
27+
AtlasGlossaryCategory,
28+
AtlasGlossaryTerm,
2529
AssetMutationResponse,
26-
BulkRequest,
2730
)
28-
31+
from pyatlan.model.core import AssetResponse, BulkRequest
2932
from pyatlan.model.enums import AtlanDeleteType
3033
from pyatlan.utils import (
3134
API,
@@ -37,7 +40,7 @@
3740
HTTPStatus,
3841
)
3942

40-
T = TypeVar("T")
43+
T = TypeVar("T", bound=Referenceable)
4144

4245

4346
class EntityClient:
@@ -211,13 +214,18 @@ class EntityClient:
211214
def __init__(self, client: AtlanClient):
212215
self.client = client
213216

217+
Assets = Union[AtlasGlossary, AtlasGlossaryCategory, AtlasGlossaryTerm]
218+
Asset_Types = Union[
219+
Type[AtlasGlossary], Type[AtlasGlossaryCategory], Type[AtlasGlossaryTerm]
220+
]
221+
214222
def get_entity_by_guid(
215223
self,
216224
guid,
217-
asset_type: Type[T],
225+
asset_type: Asset_Types,
218226
min_ext_info: bool = False,
219227
ignore_relationships: bool = False,
220-
) -> T:
228+
) -> Assets:
221229
query_params = {
222230
"minExtInfo": min_ext_info,
223231
"ignoreRelationships": ignore_relationships,
@@ -231,25 +239,21 @@ def get_entity_by_guid(
231239
raw_json["entity"]["relationshipAttributes"]
232240
)
233241
raw_json["entity"]["relationshipAttributes"] = {}
234-
response = AssetResponse[asset_type](**raw_json)
235-
return response.entity
236-
237-
def update_entity(self, entity: T) -> AssetMutationResponse[T]:
238-
request = BulkRequest[T](entities=[entity])
239-
raw_json = self.client.call_api(EntityClient.BULK_UPDATE, None, request)
240-
response: AssetMutationResponse[T] = AssetMutationResponse(**raw_json)
241-
return response
242+
if issubclass(asset_type, AtlasGlossary):
243+
return AssetResponse[AtlasGlossary](**raw_json).entity
244+
if issubclass(asset_type, AtlasGlossaryCategory):
245+
return AssetResponse[AtlasGlossaryCategory](**raw_json).entity
246+
if issubclass(asset_type, AtlasGlossaryTerm):
247+
return AssetResponse[AtlasGlossaryTerm](**raw_json).entity
242248

243-
def create_entity(self, entity: T) -> AssetMutationResponse[T]:
249+
def upsert(self, entity: Asset) -> AssetMutationResponse:
244250
request = BulkRequest[T](entities=[entity])
245251
raw_json = self.client.call_api(EntityClient.BULK_UPDATE, None, request)
246-
response: AssetMutationResponse[T] = AssetMutationResponse(**raw_json)
247-
return response
252+
return AssetMutationResponse(**raw_json)
248253

249-
def purge_entity_by_guid(self, guid) -> AssetMutationResponse[T]:
254+
def purge_entity_by_guid(self, guid) -> AssetMutationResponse:
250255
raw_json = self.client.call_api(
251256
EntityClient.DELETE_ENTITY_BY_GUID.format_path_with_params(guid),
252257
{"deleteType": AtlanDeleteType.HARD.value},
253258
)
254-
response: AssetMutationResponse[T] = AssetMutationResponse(**raw_json)
255-
return response
259+
return AssetMutationResponse(**raw_json)

pyatlan/generator/templates/entity.jinja2

+64-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class {{struct.name}}(AtlanObject):
2424
{% endfor %}
2525
{% for entity_def in entity_defs %}
2626
{%- set super_classes = ['AtlanObject'] if not entity_def.super_types else entity_def.super_types -%}
27-
class {{ entity_def.name }}({{super_classes[0]}}):
27+
class {{ entity_def.name }}({{super_classes[0]}} {%- if "Asset" in super_classes %}, type_name='{{ entity_def.name }}'{% endif %}):
2828
"""Description"""
2929
{%- if entity_def.name == "Referenceable" %}
3030
class Attributes(AtlanObject):
@@ -57,6 +57,11 @@ class {{ entity_def.name }}({{super_classes[0]}}):
5757
description='Time (epoch) at which this object was created, in milliseconds.\n',
5858
example=1648852296555,
5959
)
60+
delete_handler: Optional[str] = Field(
61+
None,
62+
description="Details on the handler used for deletion of the asset.",
63+
example="Hard",
64+
)
6065
guid: Optional[str] = Field(
6166
description='Unique identifier of the entity instance.\n',
6267
example='917ffec9-fa84-4c59-8e6c-c7b114d04be3',
@@ -123,6 +128,36 @@ class {{ entity_def.name }}({{super_classes[0]}}):
123128
None, description="", alias="meanings"
124129
)
125130
{%- else %}
131+
{%- if entity_def.name == "Asset" %}
132+
_subtypes_:dict[str, type] = dict()
133+
134+
def __init_subclass__(cls, type_name=None):
135+
cls._subtypes_[type_name or cls.__name__.lower()] = cls
136+
137+
@classmethod
138+
def __get_validators__(cls):
139+
yield cls._convert_to_real_type_
140+
141+
@classmethod
142+
def _convert_to_real_type_(cls, data):
143+
144+
if isinstance(data, Asset):
145+
return data
146+
147+
data_type = (
148+
data.get("type_name") if "type_name" in data else data.get("typeName")
149+
)
150+
151+
if data_type is None:
152+
raise ValueError("Missing 'type' in Asset")
153+
154+
sub = cls._subtypes_.get(data_type)
155+
156+
if sub is None:
157+
raise TypeError(f"Unsupport sub-type: {data_type}")
158+
159+
return sub(**data)
160+
{%- endif %}
126161
{%- if entity_def.attribute_defs %}
127162
{%- if not entity_def.sub_types %}
128163

@@ -176,6 +211,34 @@ class {{ entity_def.name }}({{super_classes[0]}}):
176211
{% endif %}
177212
{%- endif %}
178213
{% endfor %}
214+
class MutatedEntities(AtlanObject):
215+
CREATE: Optional[list[Asset]] = Field(
216+
None,
217+
description="Assets that were created. The detailed properties of the returned asset will vary based on the "
218+
"type of asset, but listed in the example are the common set of properties across assets.",
219+
alias="CREATE",
220+
)
221+
UPDATE: Optional[list[Asset]] = Field(
222+
None,
223+
description="Assets that were updated. The detailed properties of the returned asset will vary based on the "
224+
"type of asset, but listed in the example are the common set of properties across assets.",
225+
alias="UPDATE",
226+
)
227+
DELETE: Optional[list[Asset]] = Field(
228+
None,
229+
description="Assets that were deleted. The detailed properties of the returned asset will vary based on the "
230+
"type of asset, but listed in the example are the common set of properties across assets.",
231+
alias="DELETE",
232+
)
233+
234+
235+
class AssetMutationResponse(AtlanObject):
236+
guid_assignments: dict[str, Any] = Field(
237+
None, description="Map of assigned unique identifiers for the changed assets."
238+
)
239+
mutated_entities: Optional[MutatedEntities] = Field(
240+
None, description="Assets that were changed."
241+
)
179242
Referenceable.update_forward_refs()
180243
AtlasGlossary.update_forward_refs()
181244
{% for entity_def in entity_defs %}

pyatlan/model/assets.py

+76-12
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ class Attributes(AtlanObject):
177177
description="Time (epoch) at which this object was created, in milliseconds.\n",
178178
example=1648852296555,
179179
)
180+
delete_handler: Optional[str] = Field(
181+
None,
182+
description="Details on the handler used for deletion of the asset.",
183+
example="Hard",
184+
)
180185
guid: Optional[str] = Field(
181186
description="Unique identifier of the entity instance.\n",
182187
example="917ffec9-fa84-4c59-8e6c-c7b114d04be3",
@@ -245,6 +250,35 @@ class Attributes(AtlanObject):
245250
class Asset(Referenceable):
246251
"""Description"""
247252

253+
_subtypes_: dict[str, type] = dict()
254+
255+
def __init_subclass__(cls, type_name=None):
256+
cls._subtypes_[type_name or cls.__name__.lower()] = cls
257+
258+
@classmethod
259+
def __get_validators__(cls):
260+
yield cls._convert_to_real_type_
261+
262+
@classmethod
263+
def _convert_to_real_type_(cls, data):
264+
265+
if isinstance(data, Asset):
266+
return data
267+
268+
data_type = (
269+
data.get("type_name") if "type_name" in data else data.get("typeName")
270+
)
271+
272+
if data_type is None:
273+
raise ValueError("Missing 'type' in Asset")
274+
275+
sub = cls._subtypes_.get(data_type)
276+
277+
if sub is None:
278+
raise TypeError(f"Unsupport sub-type: {data_type}")
279+
280+
return sub(**data)
281+
248282
class Attributes(Referenceable.Attributes):
249283
name: str = Field(None, description="", alias="name")
250284
display_name: Optional[str] = Field(None, description="", alias="displayName")
@@ -558,7 +592,7 @@ def clear_announcment(self):
558592
self.attributes.announcement_type = None
559593

560594

561-
class AtlasGlossary(Asset):
595+
class AtlasGlossary(Asset, type_name="AtlasGlossary"):
562596
"""Description"""
563597

564598
type_name: Literal["AtlasGlossary"] = Field("AtlasGlossary")
@@ -601,15 +635,15 @@ class Attributes(Asset.Attributes):
601635
)
602636

603637

604-
class DataSet(Asset):
638+
class DataSet(Asset, type_name="DataSet"):
605639
"""Description"""
606640

607641

608-
class ProcessExecution(Asset):
642+
class ProcessExecution(Asset, type_name="ProcessExecution"):
609643
"""Description"""
610644

611645

612-
class AtlasGlossaryTerm(Asset):
646+
class AtlasGlossaryTerm(Asset, type_name="AtlasGlossaryTerm"):
613647
"""Description"""
614648

615649
type_name: Literal["AtlasGlossaryTerm"] = Field("AtlasGlossaryTerm")
@@ -695,15 +729,15 @@ class Attributes(Asset.Attributes):
695729
)
696730

697731

698-
class Cloud(Asset):
732+
class Cloud(Asset, type_name="Cloud"):
699733
"""Description"""
700734

701735

702-
class Infrastructure(Asset):
736+
class Infrastructure(Asset, type_name="Infrastructure"):
703737
"""Description"""
704738

705739

706-
class Connection(Asset):
740+
class Connection(Asset, type_name="Connection"):
707741
"""Description"""
708742

709743
type_name: Literal["Connection"] = Field("Connection")
@@ -770,7 +804,7 @@ class Attributes(Asset.Attributes):
770804
)
771805

772806

773-
class Process(Asset):
807+
class Process(Asset, type_name="Process"):
774808
"""Description"""
775809

776810
class Attributes(Asset.Attributes):
@@ -802,7 +836,7 @@ class Attributes(Asset.Attributes):
802836
)
803837

804838

805-
class AtlasGlossaryCategory(Asset):
839+
class AtlasGlossaryCategory(Asset, type_name="AtlasGlossaryCategory"):
806840
"""Description"""
807841

808842
type_name: Literal["AtlasGlossaryCategory"] = Field("AtlasGlossaryCategory")
@@ -849,7 +883,7 @@ class Attributes(Asset.Attributes):
849883
)
850884

851885

852-
class Badge(Asset):
886+
class Badge(Asset, type_name="Badge"):
853887
"""Description"""
854888

855889
type_name: Literal["Badge"] = Field("Badge")
@@ -881,11 +915,11 @@ class Attributes(Asset.Attributes):
881915
)
882916

883917

884-
class Namespace(Asset):
918+
class Namespace(Asset, type_name="Namespace"):
885919
"""Description"""
886920

887921

888-
class Catalog(Asset):
922+
class Catalog(Asset, type_name="Catalog"):
889923
"""Description"""
890924

891925

@@ -5566,6 +5600,36 @@ class Attributes(Salesforce.Attributes):
55665600
)
55675601

55685602

5603+
class MutatedEntities(AtlanObject):
5604+
CREATE: Optional[list[Asset]] = Field(
5605+
None,
5606+
description="Assets that were created. The detailed properties of the returned asset will vary based on the "
5607+
"type of asset, but listed in the example are the common set of properties across assets.",
5608+
alias="CREATE",
5609+
)
5610+
UPDATE: Optional[list[Asset]] = Field(
5611+
None,
5612+
description="Assets that were updated. The detailed properties of the returned asset will vary based on the "
5613+
"type of asset, but listed in the example are the common set of properties across assets.",
5614+
alias="UPDATE",
5615+
)
5616+
DELETE: Optional[list[Asset]] = Field(
5617+
None,
5618+
description="Assets that were deleted. The detailed properties of the returned asset will vary based on the "
5619+
"type of asset, but listed in the example are the common set of properties across assets.",
5620+
alias="DELETE",
5621+
)
5622+
5623+
5624+
class AssetMutationResponse(AtlanObject):
5625+
guid_assignments: dict[str, Any] = Field(
5626+
None, description="Map of assigned unique identifiers for the changed assets."
5627+
)
5628+
mutated_entities: Optional[MutatedEntities] = Field(
5629+
None, description="Assets that were changed."
5630+
)
5631+
5632+
55695633
Referenceable.update_forward_refs()
55705634
AtlasGlossary.update_forward_refs()
55715635

pyatlan/model/core.py

-30
Original file line numberDiff line numberDiff line change
@@ -101,33 +101,3 @@ class AssetRequest(AtlanObject, GenericModel, Generic[T]):
101101

102102
class BulkRequest(AtlanObject, GenericModel, Generic[T]):
103103
entities: list[T]
104-
105-
106-
class MutatedEntities(AtlanObject, GenericModel, Generic[T]):
107-
CREATE: Optional[list[T]] = Field(
108-
None,
109-
description="Assets that were created. The detailed properties of the returned asset will vary based on the "
110-
"type of asset, but listed in the example are the common set of properties across assets.",
111-
alias="CREATE",
112-
)
113-
UPDATE: Optional[list[T]] = Field(
114-
None,
115-
description="Assets that were updated. The detailed properties of the returned asset will vary based on the "
116-
"type of asset, but listed in the example are the common set of properties across assets.",
117-
alias="UPDATE",
118-
)
119-
DELETE: Optional[list[T]] = Field(
120-
None,
121-
description="Assets that were deleted. The detailed properties of the returned asset will vary based on the "
122-
"type of asset, but listed in the example are the common set of properties across assets.",
123-
alias="DELETE",
124-
)
125-
126-
127-
class AssetMutationResponse(AtlanObject, GenericModel, Generic[T]):
128-
guid_assignments: dict[str, Any] = Field(
129-
None, description="Map of assigned unique identifiers for the changed assets."
130-
)
131-
mutated_entities: Optional[MutatedEntities] = Field(
132-
None, description="Assets that were changed."
133-
)

0 commit comments

Comments
 (0)