Skip to content

Commit 95ebee7

Browse files
committed
[change] Fallback relationship_attributes to dict when typeName is absent
- Fixed failing unit tests.
1 parent ff5d069 commit 95ebee7

File tree

7 files changed

+141
-10
lines changed

7 files changed

+141
-10
lines changed

pyatlan/generator/templates/referenceable_attributes.jinja2

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
{%- set type = attribute_def.typeName | get_type %}
1010
{{attribute_def.name | to_snake_case }}: {% if attribute_def.isOptional %}Optional[{% endif %}{{type}}{% if attribute_def.isOptional %}]{% endif %} = Field({% if attribute_def.isOptional %}default=None,{% endif %} description='') # relationship
1111
{%- endfor %}
12-
relationship_attributes: Optional[RelationshipAttributes] = Field(
12+
relationship_attributes: Optional[Union[RelationshipAttributes, Dict[str, Any]]] = Field(
1313
default=None,
1414
description="Map of relationships for the entity. The specific keys of this map will vary by type, "
1515
"so are described in the sub-types of this schema.",
@@ -127,7 +127,7 @@
127127
)
128128
is_incomplete: Optional[bool] = Field(default=None, description="", example=True)
129129
labels: Optional[List[str]] = Field(default=None, description='Arbitrary textual labels for the asset.')
130-
relationship_attributes: Optional[RelationshipAttributes] = Field(
130+
relationship_attributes: Optional[Union[RelationshipAttributes, Dict[str, Any]]] = Field(
131131
default=None,
132132
description="Map of relationships for the entity. The specific keys of this map will vary by type, "
133133
"so are described in the sub-types of this schema.",

pyatlan/model/assets/core/referenceable.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from __future__ import annotations
66

77
from json import JSONDecodeError, loads
8-
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional
8+
from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Union
99

1010
from pydantic.v1 import Field, PrivateAttr, root_validator
1111

@@ -187,7 +187,9 @@ class Attributes(AtlanObject):
187187
meanings: Optional[List[AtlasGlossaryTerm]] = Field(
188188
default=None, description=""
189189
) # relationship
190-
relationship_attributes: Optional[RelationshipAttributes] = Field(
190+
relationship_attributes: Optional[
191+
Union[RelationshipAttributes, Dict[str, Any]]
192+
] = Field(
191193
default=None,
192194
description="Map of relationships for the entity. The specific keys of this map will vary by type, "
193195
"so are described in the sub-types of this schema.",
@@ -309,10 +311,12 @@ def validate_required(self):
309311
labels: Optional[List[str]] = Field(
310312
default=None, description="Arbitrary textual labels for the asset."
311313
)
312-
relationship_attributes: Optional[RelationshipAttributes] = Field(
313-
default=None,
314-
description="Map of relationships for the entity. The specific keys of this map will vary by type, "
315-
"so are described in the sub-types of this schema.",
314+
relationship_attributes: Optional[Union[RelationshipAttributes, Dict[str, Any]]] = (
315+
Field(
316+
default=None,
317+
description="Map of relationships for the entity. The specific keys of this map will vary by type, "
318+
"so are described in the sub-types of this schema.",
319+
)
316320
)
317321
status: Optional[EntityStatus] = Field(
318322
default=None, description="Status of the entity", example=EntityStatus.ACTIVE

pyatlan/model/assets/relations/relationship_attributes.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ def _parse_relationship_attributes(cls, data):
3030
type_name = (
3131
data.get("type_name") if "type_name" in data else data.get("typeName")
3232
)
33+
34+
# If no typeName in data, return stored
35+
# relationship attributes as Dict[str, Any]
36+
# (backward compatible with pyatlan versions < 7.1.0)
37+
if not type_name:
38+
return data
39+
3340
relationship_attribute_cls = getattr(
3441
importlib.import_module("pyatlan.model.assets.relations"),
3542
to_python_class_name(type_name),
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"typeName": "AtlasGlossaryTerm",
3+
"relationshipAttributes": {
4+
"attributes": {
5+
"description": "test description 0"
6+
}
7+
},
8+
"attributes": {
9+
"relationshipAttributes": {
10+
"attributes": {
11+
"description": "test description 1"
12+
}
13+
},
14+
"validValues": [
15+
{
16+
"guid": "color-red-guid",
17+
"typeName": "AtlasGlossaryTerm",
18+
"attributes": {
19+
"relationshipAttributes": {
20+
"attributes": {
21+
"description": "test description 11",
22+
"expression": "test expression 11",
23+
"status": "ACTIVE"
24+
}
25+
},
26+
"name": "red"
27+
},
28+
"uniqueAttributes": {
29+
"qualifiedName": "red@color-values"
30+
}
31+
},
32+
{
33+
"guid": "color-blue-guid",
34+
"typeName": "AtlasGlossaryTerm",
35+
"attributes": {
36+
"relationshipAttributes": {
37+
"attributes": {
38+
"description": "test description 12",
39+
"expression": "test expression 12",
40+
"status": "ACTIVE"
41+
}
42+
},
43+
"name": "blue"
44+
},
45+
"uniqueAttributes": {
46+
"qualifiedName": "blue@color-values"
47+
}
48+
}
49+
],
50+
"validValuesFor": [
51+
{
52+
"guid": "parent-concept-guid",
53+
"typeName": "AtlasGlossaryTerm",
54+
"attributes": {
55+
"relationshipAttributes": {
56+
"attributes": {
57+
"description": "test description 21",
58+
"alias": "test alias 21",
59+
"status": "ACTIVE"
60+
}
61+
},
62+
"name": "visual-attributes"
63+
},
64+
"uniqueAttributes": {
65+
"qualifiedName": "visual-attributes@business-glossary"
66+
}
67+
}
68+
],
69+
"qualifiedName": "color@business-glossary",
70+
"name": "color"
71+
},
72+
"guid": "color-term-guid",
73+
"status": "ACTIVE",
74+
"displayText": "color",
75+
"classificationNames": [],
76+
"classifications": [],
77+
"meaningNames": [],
78+
"meanings": [],
79+
"isIncomplete": false,
80+
"labels": [],
81+
"createdBy": "data-modeler",
82+
"updatedBy": "data-modeler",
83+
"createTime": 1750078015371,
84+
"updateTime": 1750156224299
85+
}

tests/unit/data/data_mesh_requests/data_product_assets_dsl.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
"objectStorageUploadThreshold",
9898
"outputPortDataProducts"
9999
],
100-
"suppressLogs": true
100+
"suppressLogs": true,
101+
"includeRelationshipAttributes": false
101102
},
102103
"filterScrubbed": true
103104
}

tests/unit/test_custom_relationships.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,3 +878,37 @@ def test_indistinct_relationship_deserialization(mock_asset_guid):
878878
assert reverse_attrs["description"] == "test description 21"
879879
assert reverse_attrs["alias"] == "test alias 21"
880880
assert reverse_attrs["status"] == "ACTIVE"
881+
882+
883+
def test_relationship_attrs_no_typename_fallback_dict_deserialization(mock_asset_guid):
884+
"""Test deserialization of Relationship attributes when typeName is absent."""
885+
raw_json = _load_test_data("relationship_attributes_dict_when_no_typename.json")
886+
887+
term = AtlasGlossaryTerm(**raw_json)
888+
889+
# Fallback to dict when typeName is absent (cannot determine specific Relationship model type)
890+
assert term.relationship_attributes
891+
assert isinstance(term.relationship_attributes, dict)
892+
assert term.relationship_attributes["attributes"]["description"]
893+
894+
assert term.attributes.relationship_attributes
895+
assert isinstance(term.attributes.relationship_attributes, dict)
896+
assert term.relationship_attributes["attributes"]["description"]
897+
898+
assert term.name and term.guid and term.qualified_name
899+
assert len(term.valid_values) == 2
900+
assert len(term.valid_values_for) == 1
901+
902+
valid_value_relation = term.valid_values[0]
903+
assert valid_value_relation.attributes
904+
assert valid_value_relation.attributes.relationship_attributes
905+
assert valid_value_relation.attributes.relationship_attributes["attributes"][
906+
"description"
907+
]
908+
909+
valid_value_for_relation = term.valid_values_for[0]
910+
assert valid_value_for_relation.attributes
911+
assert valid_value_for_relation.attributes.relationship_attributes
912+
assert valid_value_for_relation.attributes.relationship_attributes["attributes"][
913+
"description"
914+
]

tests/unit/test_search_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def test_index_search_request():
292292
' "dsl": {"from": 0, "size": 300, "aggregations": {}, "track_total_hits": true, '
293293
'"post_filter": {"term": {"databaseName.keyword": '
294294
'{"value": "ATLAN_SAMPLE_DATA"}}}, "query": {"term": {"__typeName.keyword": {"value": "Schema"}}}, '
295-
'"sort": [{"__guid": {"order": "asc"}}]}, "relationAttributes": [], '
295+
'"sort": [{"__guid": {"order": "asc"}}]}, "relationAttributes": [], "includeRelationshipAttributes": false, '
296296
'"requestMetadata": {"saveSearchLog": false, "utmTags": ["project_sdk_python"]}}'
297297
)
298298

0 commit comments

Comments
 (0)