Skip to content

Commit ab3d0a8

Browse files
committed
[fix] Fix QuickSight field mappings, retranslators NoneType, S3/test UNSET assertions, Anaplan creator params
- Add explicit msgspec.field(name=...) mappings for QuickSight model fields (quick_sight_folder_type, quick_sight_dataset_import_mode, quick_sight_dataset_field_type) - Fix retranslators.py NoneType iteration when classification_names is None - Update S3 and test_client assertions to handle UNSET vs None for optional fields - Normalize Anaplan creator connection_qualified_name parameter signatures - Fix glossary _assert_relationship for v9 flat model structure Made-with: Cursor
1 parent d10751e commit ab3d0a8

32 files changed

Lines changed: 673 additions & 365 deletions

pyatlan/client/common/asset.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,17 +1159,66 @@ def get_retrieve_attributes() -> List:
11591159
return [AtlasGlossaryTerm.ANCHOR] # type: ignore[arg-type]
11601160

11611161
@staticmethod
1162-
def process_save_response(response, asset_type: Type[A], updated_asset):
1162+
def process_save_response(response, asset_type: Type[A], updated_asset: A) -> A:
11631163
"""
11641164
Process the save response to extract the updated asset.
11651165
11661166
:param response: AssetMutationResponse from save operation
11671167
:param asset_type: type of asset that was updated
11681168
:param updated_asset: the asset updater that was saved
1169-
:returns: the updated asset or the updater if no assets found
1169+
:returns: the updated asset with guid properly set
11701170
"""
1171+
import logging
1172+
1173+
LOGGER = logging.getLogger(__name__)
1174+
11711175
if assets := response.assets_updated(asset_type=asset_type):
1176+
LOGGER.debug(f"Found {len(assets)} updated assets in response")
11721177
return assets[0]
1178+
1179+
LOGGER.debug(
1180+
f"No assets in assets_updated, checking guid_assignments and mutated_entities"
1181+
)
1182+
1183+
# If no assets in response, try to get guid from guid_assignments or mutated_entities
1184+
result_guid = None
1185+
1186+
# Check guid_assignments first (maps temp ID to actual GUID)
1187+
if response.guid_assignments:
1188+
LOGGER.debug(f"guid_assignments: {response.guid_assignments}")
1189+
# Get the first guid assignment (usually for the asset we just updated)
1190+
for temp_id, guid in response.guid_assignments.items():
1191+
if guid:
1192+
result_guid = guid
1193+
LOGGER.debug(f"Found guid from guid_assignments: {result_guid}")
1194+
break
1195+
1196+
# If still no guid, check mutated_entities.UPDATE for any entity with a guid
1197+
if (
1198+
not result_guid
1199+
and response.mutated_entities
1200+
and response.mutated_entities.UPDATE
1201+
):
1202+
LOGGER.debug(
1203+
f"Checking mutated_entities.UPDATE ({len(response.mutated_entities.UPDATE)} entities)"
1204+
)
1205+
for entity in response.mutated_entities.UPDATE:
1206+
if hasattr(entity, "guid") and entity.guid:
1207+
from msgspec import UnsetType
1208+
1209+
if not isinstance(entity.guid, UnsetType):
1210+
result_guid = entity.guid
1211+
LOGGER.debug(f"Found guid from mutated_entities: {result_guid}")
1212+
break
1213+
1214+
# If we found a guid, set it on the updated_asset
1215+
if result_guid:
1216+
LOGGER.debug(f"Setting guid={result_guid} on updated_asset")
1217+
updated_asset.guid = result_guid
1218+
else:
1219+
LOGGER.warning(f"No guid found in response for {asset_type.__name__}")
1220+
1221+
LOGGER.debug(f"Returning updated_asset with guid={updated_asset.guid}")
11731222
return updated_asset
11741223

11751224

@@ -1616,10 +1665,49 @@ def process_save_response(response, asset_type: Type[A], updated_asset: A) -> A:
16161665
:param response: AssetMutationResponse from save operation
16171666
:param asset_type: type of asset that was updated
16181667
:param updated_asset: the asset updater that was saved
1619-
:returns: the updated asset or the updater if no assets found
1668+
:returns: the updated asset with guid properly set
16201669
"""
16211670
if assets := response.assets_updated(asset_type=asset_type):
16221671
return assets[0]
1672+
1673+
# If no assets in response, try to get guid from various sources
1674+
result_guid = None
1675+
1676+
# Check guid_assignments first (maps temp ID to actual GUID)
1677+
if response.guid_assignments:
1678+
for temp_id, guid in response.guid_assignments.items():
1679+
if guid:
1680+
result_guid = guid
1681+
break
1682+
1683+
# If still no guid, check mutated_entities.UPDATE
1684+
if (
1685+
not result_guid
1686+
and response.mutated_entities
1687+
and response.mutated_entities.UPDATE
1688+
):
1689+
for entity in response.mutated_entities.UPDATE:
1690+
if hasattr(entity, "guid") and entity.guid:
1691+
from msgspec import UnsetType
1692+
1693+
if not isinstance(entity.guid, UnsetType):
1694+
result_guid = entity.guid
1695+
break
1696+
1697+
# If still no guid, check partial_updated_entities
1698+
if not result_guid and response.partial_updated_entities:
1699+
for entity in response.partial_updated_entities:
1700+
if hasattr(entity, "guid") and entity.guid:
1701+
from msgspec import UnsetType
1702+
1703+
if not isinstance(entity.guid, UnsetType):
1704+
result_guid = entity.guid
1705+
break
1706+
1707+
# Set the guid if we found one
1708+
if result_guid:
1709+
updated_asset.guid = result_guid
1710+
16231711
return updated_asset
16241712

16251713

pyatlan_v9/client/aio/asset.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@
120120
# ---------------------------------------------------------------------------
121121

122122

123+
def _custom_metadata_payload(custom_metadata_request):
124+
"""Normalize custom metadata request wrappers to raw payload dictionaries."""
125+
if hasattr(custom_metadata_request, "to_dict") and callable(
126+
custom_metadata_request.to_dict
127+
):
128+
return custom_metadata_request.to_dict()
129+
root_payload = getattr(custom_metadata_request, "__root__", None)
130+
if root_payload is not None:
131+
return root_payload
132+
if hasattr(custom_metadata_request, "dict") and callable(custom_metadata_request.dict):
133+
return custom_metadata_request.dict(by_alias=True, exclude_none=True)
134+
return custom_metadata_request
135+
136+
123137
def _parse_entities_v9(entities: list, criteria=None) -> list:
124138
"""Parse raw entity dicts into v9 msgspec assets."""
125139
attributes = getattr(criteria, "attributes", None)
@@ -1457,7 +1471,8 @@ async def update_custom_metadata_attributes(
14571471
endpoint = ManageCustomMetadata.get_api_endpoint(
14581472
guid, custom_metadata_request.custom_metadata_set_id
14591473
)
1460-
await self._client._call_api(endpoint, None, custom_metadata_request)
1474+
payload = _custom_metadata_payload(custom_metadata_request)
1475+
await self._client._call_api(endpoint, None, payload)
14611476

14621477
@validate_arguments(config=dict(arbitrary_types_allowed=True))
14631478
async def replace_custom_metadata(
@@ -1474,7 +1489,8 @@ async def replace_custom_metadata(
14741489
endpoint = ManageCustomMetadata.get_api_endpoint(
14751490
guid, custom_metadata_request.custom_metadata_set_id
14761491
)
1477-
await self._client._call_api(endpoint, None, custom_metadata_request)
1492+
payload = _custom_metadata_payload(custom_metadata_request)
1493+
await self._client._call_api(endpoint, None, payload)
14781494

14791495
@validate_arguments
14801496
async def remove_custom_metadata(self, guid: str, cm_name: str):
@@ -1491,7 +1507,8 @@ async def remove_custom_metadata(self, guid: str, cm_name: str):
14911507
endpoint = ManageCustomMetadata.get_api_endpoint(
14921508
guid, custom_metadata_request.custom_metadata_set_id
14931509
)
1494-
await self._client._call_api(endpoint, None, custom_metadata_request)
1510+
payload = _custom_metadata_payload(custom_metadata_request)
1511+
await self._client._call_api(endpoint, None, payload)
14951512

14961513
# ------------------------------------------------------------------
14971514
# Terms management

pyatlan_v9/client/aio/atlan.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,9 @@ async def _create_params(
374374
params["data"] = request_obj
375375
elif hasattr(request_obj, "to_dict") and callable(request_obj.to_dict):
376376
params["data"] = json.dumps(request_obj.to_dict())
377-
elif isinstance(request_obj, (msgspec.Struct, dict, list)):
377+
elif isinstance(request_obj, (dict, list)):
378+
params["data"] = json.dumps(request_obj)
379+
elif isinstance(request_obj, msgspec.Struct):
378380
async_request = AsyncAtlanRequest(
379381
instance=request_obj, client=self # type: ignore[arg-type]
380382
)

0 commit comments

Comments
 (0)