diff --git a/pyatlan/client/asset.py b/pyatlan/client/asset.py index a3fa6ddaa..d075922a0 100644 --- a/pyatlan/client/asset.py +++ b/pyatlan/client/asset.py @@ -83,6 +83,7 @@ AtlanDeleteType, CertificateStatus, EntityStatus, + SaveSemantic, SortOrder, ) from pyatlan.model.fields.atlan_fields import AtlanField @@ -1330,29 +1331,64 @@ def append_terms( :param qualified_name: the qualified_name of the asset to which to link the terms :returns: the asset that was updated (note that it will NOT contain details of the appended terms) """ + from pyatlan.client.atlan import AtlanClient + from pyatlan.model.fluent_search import FluentSearch + + client = AtlanClient.get_default_client() if guid: if qualified_name: raise ErrorCode.QN_OR_GUID_NOT_BOTH.exception_with_parameters() - asset = self.get_by_guid(guid=guid, asset_type=asset_type) + results = ( + FluentSearch() + .select() + .where(asset_type.GUID.eq(guid)) + .execute(client=client) + ) elif qualified_name: - asset = self.get_by_qualified_name( - qualified_name=qualified_name, asset_type=asset_type + results = ( + FluentSearch() + .select() + .where(asset_type.QUALIFIED_NAME.eq(qualified_name)) + .execute(client=client) ) else: raise ErrorCode.QN_OR_GUID.exception_with_parameters() - if not terms: - return asset - replacement_terms: List[AtlasGlossaryTerm] = [] - if existing_terms := asset.assigned_terms: - replacement_terms.extend( - term for term in existing_terms if term.relationship_status != "DELETED" - ) - replacement_terms.extend(terms) - asset.assigned_terms = replacement_terms - response = self.save(entity=asset) + + if results and results.current_page(): + first_result = results.current_page()[0] + if not isinstance(first_result, asset_type): + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, qualified_name + ) + else: + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, asset_type.__name__ + ) + else: + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ) + else: + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid) + qualified_name = first_result.qualified_name + name = first_result.name + updated_asset = asset_type.updater(qualified_name=qualified_name, name=name) + for i, term in enumerate(terms): + if hasattr(term, "guid") and term.guid: + terms[i] = AtlasGlossaryTerm.ref_by_guid( + guid=term.guid, semantic=SaveSemantic.APPEND + ) + elif hasattr(term, "qualified_name") and term.qualified_name: + terms[i] = AtlasGlossaryTerm.ref_by_qualified_name( + qualified_name=term.qualified_name, semantic=SaveSemantic.APPEND + ) + updated_asset.assigned_terms = terms + response = self.save(entity=updated_asset) if assets := response.assets_updated(asset_type=asset_type): return assets[0] - return asset + return updated_asset @validate_arguments def replace_terms( @@ -1372,25 +1408,64 @@ def replace_terms( :param qualified_name: the qualified_name of the asset to which to replace the terms :returns: the asset that was updated (note that it will NOT contain details of the replaced terms) """ + from pyatlan.client.atlan import AtlanClient + from pyatlan.model.fluent_search import FluentSearch + + client = AtlanClient.get_default_client() if guid: if qualified_name: raise ErrorCode.QN_OR_GUID_NOT_BOTH.exception_with_parameters() - asset = self.get_by_guid( - guid=guid, asset_type=asset_type, ignore_relationships=False + results = ( + FluentSearch() + .select() + .where(asset_type.GUID.eq(guid)) + .execute(client=client) ) elif qualified_name: - asset = self.get_by_qualified_name( - qualified_name=qualified_name, - asset_type=asset_type, - ignore_relationships=False, + results = ( + FluentSearch() + .select() + .where(asset_type.QUALIFIED_NAME.eq(qualified_name)) + .execute(client=client) ) else: raise ErrorCode.QN_OR_GUID.exception_with_parameters() - asset.assigned_terms = terms - response = self.save(entity=asset) + + if results and results.current_page(): + first_result = results.current_page()[0] + if not isinstance(first_result, asset_type): + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, qualified_name + ) + else: + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, asset_type.__name__ + ) + else: + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ) + else: + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid) + qualified_name = first_result.qualified_name + name = first_result.name + updated_asset = asset_type.updater(qualified_name=qualified_name, name=name) + for i, term in enumerate(terms): + if hasattr(term, "guid") and term.guid: + terms[i] = AtlasGlossaryTerm.ref_by_guid( + guid=term.guid, semantic=SaveSemantic.REPLACE + ) + elif hasattr(term, "qualified_name") and term.qualified_name: + terms[i] = AtlasGlossaryTerm.ref_by_qualified_name( + qualified_name=term.qualified_name, semantic=SaveSemantic.REPLACE + ) + updated_asset.assigned_terms = terms + response = self.save(entity=updated_asset) if assets := response.assets_updated(asset_type=asset_type): return assets[0] - return asset + return updated_asset @validate_arguments def remove_terms( @@ -1412,36 +1487,64 @@ def remove_terms( :param qualified_name: the qualified_name of the asset from which to remove the terms :returns: the asset that was updated (note that it will NOT contain details of the resulting terms) """ - if not terms: - raise ErrorCode.MISSING_TERMS.exception_with_parameters() + from pyatlan.client.atlan import AtlanClient + from pyatlan.model.fluent_search import FluentSearch + + client = AtlanClient.get_default_client() if guid: if qualified_name: raise ErrorCode.QN_OR_GUID_NOT_BOTH.exception_with_parameters() - asset = self.get_by_guid( - guid=guid, asset_type=asset_type, ignore_relationships=False + results = ( + FluentSearch() + .select() + .where(asset_type.GUID.eq(guid)) + .execute(client=client) ) elif qualified_name: - asset = self.get_by_qualified_name( - qualified_name=qualified_name, - asset_type=asset_type, - ignore_relationships=False, + results = ( + FluentSearch() + .select() + .where(asset_type.QUALIFIED_NAME.eq(qualified_name)) + .execute(client=client) ) else: raise ErrorCode.QN_OR_GUID.exception_with_parameters() - replacement_terms: List[AtlasGlossaryTerm] = [] - guids_to_be_removed = {t.guid for t in terms} - if existing_terms := asset.assigned_terms: - replacement_terms.extend( - term - for term in existing_terms - if term.relationship_status != "DELETED" - and term.guid not in guids_to_be_removed - ) - asset.assigned_terms = replacement_terms - response = self.save(entity=asset) + + if results and results.current_page(): + first_result = results.current_page()[0] + if not isinstance(first_result, asset_type): + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_NAME.exception_with_parameters( + asset_type.__name__, qualified_name + ) + else: + raise ErrorCode.ASSET_NOT_TYPE_REQUESTED.exception_with_parameters( + guid, asset_type.__name__ + ) + else: + if guid is None: + raise ErrorCode.ASSET_NOT_FOUND_BY_QN.exception_with_parameters( + qualified_name, asset_type.__name__ + ) + else: + raise ErrorCode.ASSET_NOT_FOUND_BY_GUID.exception_with_parameters(guid) + qualified_name = first_result.qualified_name + name = first_result.name + updated_asset = asset_type.updater(qualified_name=qualified_name, name=name) + for i, term in enumerate(terms): + if hasattr(term, "guid") and term.guid: + terms[i] = AtlasGlossaryTerm.ref_by_guid( + guid=term.guid, semantic=SaveSemantic.REMOVE + ) + elif hasattr(term, "qualified_name") and term.qualified_name: + terms[i] = AtlasGlossaryTerm.ref_by_qualified_name( + qualified_name=term.qualified_name, semantic=SaveSemantic.REMOVE + ) + updated_asset.assigned_terms = terms + response = self.save(entity=updated_asset) if assets := response.assets_updated(asset_type=asset_type): return assets[0] - return asset + return updated_asset @validate_arguments def find_connections_by_name( diff --git a/pyatlan/model/core.py b/pyatlan/model/core.py index 59582d1f4..4a7f9155e 100644 --- a/pyatlan/model/core.py +++ b/pyatlan/model/core.py @@ -395,7 +395,11 @@ def process_relationship_attributes(cls, asset, attribute): replace_attributes = [] exclude_attributes = set() - attribute_name, attribute_value = attribute, getattr(asset, attribute, None) + # Updated to use `asset.attribute` instead of `asset` to align with the API. + # This change ensures the correct value is retrieved regardless of the naming conventions. + attribute_name, attribute_value = attribute, getattr( + asset.attributes, attribute, None + ) # Process list of relationship attributes if attribute_value and isinstance(attribute_value, list): @@ -418,7 +422,9 @@ def process_relationship_attributes(cls, asset, attribute): {to_camel_case(attribute_name): append_attributes} ) if replace_attributes: - setattr(asset, attribute_name, replace_attributes) + # Updated to use `asset.attribute` instead of `asset` to align with the API. + # This change ensures the correct value is retrieved regardless of the naming conventions. + setattr(asset.attributes, attribute_name, replace_attributes) # If 'remove', 'append', or both attributes are present and there are no 'replace' attributes, # add the attribute to the set to exclude it from the bulk request payload. diff --git a/tests/integration/test_client.py b/tests/integration/test_client.py index d1d96a091..d9ddba3c4 100644 --- a/tests/integration/test_client.py +++ b/tests/integration/test_client.py @@ -264,8 +264,11 @@ def _test_remove_announcement( def test_append_terms_with_guid( - client: AtlanClient, term1: AtlasGlossaryTerm, database: Database + client: AtlanClient, + term1: AtlasGlossaryTerm, + database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( guid=database.guid, asset_type=Database, terms=[term1] @@ -284,6 +287,7 @@ def test_append_terms_with_qualified_name( term1: AtlasGlossaryTerm, database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( qualified_name=database.qualified_name, asset_type=Database, terms=[term1] @@ -302,6 +306,7 @@ def test_append_terms_using_ref_by_guid_for_term( term1: AtlasGlossaryTerm, database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( qualified_name=database.qualified_name, @@ -323,6 +328,7 @@ def test_replace_a_term( term2: AtlasGlossaryTerm, database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( qualified_name=database.qualified_name, @@ -359,6 +365,7 @@ def test_replace_all_term( term1: AtlasGlossaryTerm, database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( qualified_name=database.qualified_name, @@ -391,6 +398,7 @@ def test_remove_term( term2: AtlasGlossaryTerm, database: Database, ): + time.sleep(5) assert ( database := client.asset.append_terms( qualified_name=database.qualified_name, @@ -469,6 +477,7 @@ def test_get_asset_by_guid_when_table_specified_and_glossary_returned_raises_not def test_get_by_guid_with_fs(client: AtlanClient, term: AtlasGlossaryTerm): + time.sleep(5) # Default - should call `GET_ENTITY_BY_GUID` API result = client.asset.get_by_guid(guid=term.guid, asset_type=AtlasGlossaryTerm) assert isinstance(result, AtlasGlossaryTerm) @@ -538,6 +547,7 @@ def test_get_by_guid_with_fs(client: AtlanClient, term: AtlasGlossaryTerm): def test_get_by_qualified_name_with_fs(client: AtlanClient, term: AtlasGlossaryTerm): + time.sleep(5) # Default - should call `GET_ENTITY_BY_GUID` API assert term and term.qualified_name result = client.asset.get_by_qualified_name( diff --git a/tests/unit/test_client.py b/tests/unit/test_client.py index dca63984b..9b424c578 100644 --- a/tests/unit/test_client.py +++ b/tests/unit/test_client.py @@ -236,142 +236,108 @@ def glossary_category_by_name_json(): @pytest.mark.parametrize( - "guid, qualified_name, asset_type, assigned_terms, message, error", + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", [ ( - "123", None, - Table, None, - "1 validation error for AppendTerms\\nterms\\n none is not an allowed value ", - ValueError, + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", + InvalidRequestError, ), ( "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +def test_append_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.append_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( None, - None, + "nonexistent_qualified_name", + Table, [AtlasGlossaryTerm()], - "1 validation error for AppendTerms\\nasset_type\\n none is not an allowed value ", - ValueError, + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, ), ( + "nonexistent_guid", None, + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( None, + "default/abc", Table, [AtlasGlossaryTerm()], - "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", - InvalidRequestError, + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, ), ( "123", - "default/abc", + None, Table, [AtlasGlossaryTerm()], - "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", - InvalidRequestError, + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, ), ], ) -def test_append_terms_with_invalid_parameter_raises_error( +@patch("pyatlan.model.fluent_search.FluentSearch.execute") +def test_append_terms_asset_retrieval_errors( + mock_execute, guid, qualified_name, asset_type, assigned_terms, - message, - error, - client: AtlanClient, + mock_results, + expected_message, + expected_error, ): - with pytest.raises(error, match=message): - client.asset.append_terms( - guid=guid, - qualified_name=qualified_name, + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.append_terms( asset_type=asset_type, terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, ) -def test_append_with_valid_guid_and_no_terms_returns_asset(): - asset_type = Table - table = asset_type() - - with patch.object(AssetClient, "get_by_guid", return_value=table) as mock_method: - client = AtlanClient() - guid = "123" - terms = [] - - assert ( - client.asset.append_terms(guid=guid, asset_type=asset_type, terms=terms) - == table - ) - mock_method.assert_called_once_with(guid=guid, asset_type=asset_type) - - -def test_append_with_valid_guid_when_no_terms_present_returns_asset_with_given_terms(): - asset_type = Table - with patch.multiple(AssetClient, get_by_guid=DEFAULT, save=DEFAULT) as mock_methods: - table = Table() - mock_methods["get_by_guid"].return_value = table - mock_methods["save"].return_value.assets_updated.return_value = [table] - client = AtlanClient() - guid = "123" - terms = [AtlasGlossaryTerm()] - - assert ( - asset := client.asset.append_terms( - guid=guid, asset_type=asset_type, terms=terms - ) - ) - assert asset.assigned_terms == terms - - -def test_append_with_valid_guid_when_deleted_terms_present_returns_asset_with_given_terms(): - asset_type = Table - with patch.multiple(AssetClient, get_by_guid=DEFAULT, save=DEFAULT) as mock_methods: - table = Table(attributes=Table.Attributes()) - term = AtlasGlossaryTerm() - term.relationship_status = "DELETED" - table.attributes.meanings = [term] - mock_methods["get_by_guid"].return_value = table - mock_methods["save"].return_value.assets_updated.return_value = [table] - client = AtlanClient() - guid = "123" - terms = [AtlasGlossaryTerm()] - - assert ( - asset := client.asset.append_terms( - guid=guid, asset_type=asset_type, terms=terms - ) - ) - assert asset.assigned_terms == terms - - -def test_append_with_valid_guid_when_terms_present_returns_asset_with_combined_terms(): - asset_type = Table - with patch.multiple(AssetClient, get_by_guid=DEFAULT, save=DEFAULT) as mock_methods: - table = Table(attributes=Table.Attributes()) - exisiting_term = AtlasGlossaryTerm() - table.attributes.meanings = [exisiting_term] - mock_methods["get_by_guid"].return_value = table - mock_methods["save"].return_value.assets_updated.return_value = [table] - client = AtlanClient() - guid = "123" - - new_term = AtlasGlossaryTerm() - terms = [new_term] - - assert ( - asset := client.asset.append_terms( - guid=guid, asset_type=asset_type, terms=terms - ) - ) - assert (updated_terms := asset.assigned_terms) - assert len(updated_terms) == 2 - assert exisiting_term in updated_terms - assert new_term in updated_terms - - @pytest.mark.parametrize( - "guid, qualified_name, asset_type, assigned_terms, message, error", + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", [ ( None, @@ -383,68 +349,96 @@ def test_append_with_valid_guid_when_terms_present_returns_asset_with_combined_t ), ( "123", + "default/abc", + Table, + [AtlasGlossaryTerm()], + "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", + InvalidRequestError, + ), + ], +) +def test_replace_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.replace_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ + ( None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", None, + Table, [AtlasGlossaryTerm()], - "1 validation error for ReplaceTerms\\nasset_type\\n none is not an allowed value ", - ValueError, + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, ), ( - "123", + None, "default/abc", Table, [AtlasGlossaryTerm()], - "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", - InvalidRequestError, + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, ), ( "123", None, Table, - None, - "1 validation error for ReplaceTerms\\nterms\\n none is not an allowed value ", - ValueError, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, ), ], ) -def test_replace_terms_with_invalid_parameter_raises_error( +@patch("pyatlan.model.fluent_search.FluentSearch.execute") +def test_replace_terms_asset_retrieval_errors( + mock_execute, guid, qualified_name, asset_type, assigned_terms, - message, - error, - client: AtlanClient, + mock_results, + expected_message, + expected_error, ): - with pytest.raises(error, match=message): - client.asset.replace_terms( - guid=guid, - qualified_name=qualified_name, + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.replace_terms( asset_type=asset_type, terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, ) -def test_replace_terms(): - asset_type = Table - with patch.multiple(AssetClient, get_by_guid=DEFAULT, save=DEFAULT) as mock_methods: - table = Table() - mock_methods["get_by_guid"].return_value = table - mock_methods["save"].return_value.assets_updated.return_value = [table] - client = AtlanClient() - guid = "123" - terms = [AtlasGlossaryTerm()] - - assert ( - asset := client.asset.replace_terms( - guid=guid, asset_type=asset_type, terms=terms - ) - ) - assert asset.assigned_terms == terms - - @pytest.mark.parametrize( - "guid, qualified_name, asset_type, assigned_terms, message, error", + "guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error", [ ( None, @@ -454,14 +448,6 @@ def test_replace_terms(): "ATLAN-PYTHON-400-043 Either qualified_name or guid should be provided.", InvalidRequestError, ), - ( - "123", - None, - None, - [AtlasGlossaryTerm()], - "1 validation error for RemoveTerms\\nasset_type\\n none is not an allowed value ", - ValueError, - ), ( "123", "default/abc", @@ -470,66 +456,88 @@ def test_replace_terms(): "ATLAN-PYTHON-400-042 Only qualified_name or guid should be provided but not both.", InvalidRequestError, ), + ], +) +def test_remove_terms_invalid_parameters_raises_error( + guid, qualified_name, asset_type, assigned_terms, expected_message, expected_error +): + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.remove_terms( + asset_type=asset_type, + terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, + ) + + +@pytest.mark.parametrize( + "guid, qualified_name, asset_type, assigned_terms, mock_results, expected_message, expected_error", + [ ( - "123", + None, + "nonexistent_qualified_name", + Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-003 Asset with qualifiedName nonexistent_qualified_name of type Table does not exist." + " Suggestion: Verify the qualifiedName and expected type of the asset you are trying to retrieve.", + NotFoundError, + ), + ( + "nonexistent_guid", None, Table, + [AtlasGlossaryTerm()], + [], + "ATLAN-PYTHON-404-001 Asset with GUID nonexistent_guid does not exist." + " Suggestion: Verify the GUID of the asset you are trying to retrieve.", + NotFoundError, + ), + ( None, - "1 validation error for RemoveTerms\\nterms\\n none is not an allowed value ", - ValueError, + "default/abc", + Table, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-014 The Table asset could not be found by name: default/abc." + " Suggestion: Verify the requested asset type and name exist in your Atlan environment.", + NotFoundError, ), ( "123", None, Table, - [], - "ATLAN-PYTHON-400-044 A list of assigned_terms to remove must be specified.", - InvalidRequestError, + [AtlasGlossaryTerm()], + ["DifferentTypeAsset"], + "ATLAN-PYTHON-404-002 Asset with GUID 123 is not of the type requested: Table." + " Suggestion: Verify the GUID and expected type of the asset you are trying to retrieve.", + NotFoundError, ), ], ) -def test_remove_terms_with_invalid_parameter_raises_error( +@patch("pyatlan.model.fluent_search.FluentSearch.execute") +def test_remove_terms_asset_retrieval_errors( + mock_execute, guid, qualified_name, asset_type, assigned_terms, - message, - error, - client: AtlanClient, + mock_results, + expected_message, + expected_error, ): - with pytest.raises(error, match=message): - client.asset.remove_terms( - guid=guid, - qualified_name=qualified_name, + mock_execute.return_value.current_page = lambda: mock_results + client = AtlanClient() + with pytest.raises(expected_error, match=expected_message): + client.remove_terms( asset_type=asset_type, terms=assigned_terms, + guid=guid, + qualified_name=qualified_name, ) -def test_remove_with_valid_guid_when_terms_present_returns_asset_with_terms_removed(): - asset_type = Table - with patch.multiple(AssetClient, get_by_guid=DEFAULT, save=DEFAULT) as mock_methods: - table = Table(attributes=Table.Attributes()) - exisiting_term = AtlasGlossaryTerm() - exisiting_term.guid = "b4113341-251b-4adc-81fb-2420501c30e6" - other_term = AtlasGlossaryTerm() - other_term.guid = "b267858d-8316-4c41-a56a-6e9b840cef4a" - table.attributes.meanings = [exisiting_term, other_term] - mock_methods["get_by_guid"].return_value = table - mock_methods["save"].return_value.assets_updated.return_value = [table] - client = AtlanClient() - guid = "123" - - assert ( - asset := client.asset.remove_terms( - guid=guid, asset_type=asset_type, terms=[exisiting_term] - ) - ) - assert (updated_terms := asset.assigned_terms) - assert len(updated_terms) == 1 - assert other_term in updated_terms - - def test_register_client_with_bad_parameter_raises_value_error(client): with pytest.raises( InvalidRequestError, match="client must be an instance of AtlanClient"