From 6515f87de186a4f94b958f7afb87ce1029e94239 Mon Sep 17 00:00:00 2001 From: Yotam Date: Wed, 18 Sep 2024 11:00:34 -0400 Subject: [PATCH 1/3] introduce method for removing an entity record from a campaign --- parsons/action_builder/action_builder.py | 20 +++++++++++++++++++ .../test_action_builder.py | 14 +++++++++++++ 2 files changed, 34 insertions(+) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index 5249213a7b..c501715f44 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -246,6 +246,26 @@ def update_entity_record(self, identifier, data, campaign=None): return self._upsert_entity(data=data, campaign=campaign) + def remove_entity_record_from_campaign(self, identifier, campaign=None): + """ + Remove an entity record from a campaign. Records cannot be permanently deleted, but a + record that has been removed from a campaign will not appear in the UI. + `Args:` + identifier: str + The unique identifier for the record being removed. ID strings will need to begin + with the origin system, followed by a colon, e.g. `action_builder:abc123-...`. + campaign: str + Optional. The 36-character "interact ID" of the campaign whose data is to be + retrieved or edited. Not necessary if supplied when instantiating the class. + `Returns:` + Dict with HTTP response. + """ + + campaign = self._campaign_check(campaign) + + url = f"campaigns/{campaign}/people/{identifier}" + return self.api.delete_request(url=url) + def add_section_field_values_to_record(self, identifier, section, field_values, campaign=None): """ Add one or more tags (i.e. custom field value) to an existing entity record in Action diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index d0a71dd2e5..d858a63f35 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -343,6 +343,20 @@ def test_update_entity_record(self, m): self.assertEqual(person_comp, update_response_comp) + @requests_mock.Mocker() + def test_remove_entity_record_from_campaign(self, m): + m.delete( + f"{self.api_url}/people/{self.fake_entity_id}", + json = "{'message': 'Entity has been removed from the campaign'}", + ) + + remove_response = self.bldr.remove_entity_record_from_campaign(self.fake_entity_id) + + self.assertEqual( + remove_response, + "{'message': 'Entity has been removed from the campaign'}", + ) + def tagging_callback(self, request, context): # Internal method for returning the constructed tag data to test From 392af7a1695bee3a258c5b591965710feb57c595 Mon Sep 17 00:00:00 2001 From: Yotam Date: Wed, 18 Sep 2024 11:08:57 -0400 Subject: [PATCH 2/3] lint with black --- parsons/action_builder/action_builder.py | 34 ++++++++++++++----- .../test_action_builder.py | 34 ++++++++++++++----- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index c501715f44..34870bdcb7 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -59,7 +59,9 @@ def _get_page(self, campaign, object_name, page, per_page=25, filter=None): return self.api.get_request(url=url, params=params) - def _get_all_records(self, campaign, object_name, limit=None, per_page=25, filter=None): + def _get_all_records( + self, campaign, object_name, limit=None, per_page=25, filter=None + ): # Returns a list of entries for a given object, such as people, tags, or connections. # See Action Builder API docs for more: https://www.actionbuilder.org/docs/v1/index.html @@ -70,7 +72,9 @@ def _get_all_records(self, campaign, object_name, limit=None, per_page=25, filte # Keep getting the next page until record limit is exceeded or an empty result returns while True: # Get this page and increase page number to the next one - response = self._get_page(campaign, object_name, page, per_page, filter=filter) + response = self._get_page( + campaign, object_name, page, per_page, filter=filter + ) page = page + 1 # Check that there's actually data @@ -233,7 +237,9 @@ def update_entity_record(self, identifier, data, campaign=None): identifier = [identifier] # Default to assuming identifier comes from Action Builder and add prefix if missing - identifiers = [f"action_builder:{id}" if ":" not in id else id for id in identifier] + identifiers = [ + f"action_builder:{id}" if ":" not in id else id for id in identifier + ] if not isinstance(data, dict): data = {} @@ -262,11 +268,13 @@ def remove_entity_record_from_campaign(self, identifier, campaign=None): """ campaign = self._campaign_check(campaign) - + url = f"campaigns/{campaign}/people/{identifier}" return self.api.delete_request(url=url) - def add_section_field_values_to_record(self, identifier, section, field_values, campaign=None): + def add_section_field_values_to_record( + self, identifier, section, field_values, campaign=None + ): """ Add one or more tags (i.e. custom field value) to an existing entity record in Action Builder. The tags, along with their field and section, must already exist (except for @@ -297,7 +305,9 @@ def add_section_field_values_to_record(self, identifier, section, field_values, data = {"add_tags": tag_data} - return self.update_entity_record(identifier=identifier, data=data, campaign=campaign) + return self.update_entity_record( + identifier=identifier, data=data, campaign=campaign + ) def remove_tagging( self, @@ -339,7 +349,9 @@ def remove_tagging( raise ValueError("Please supply a tag_name or tag_id!") if {identifier, tagging_id} == {None}: - raise ValueError("Please supply an entity or connection identifier, or a tagging id!") + raise ValueError( + "Please supply an entity or connection identifier, or a tagging id!" + ) campaign = self._campaign_check(campaign) endpoint = "tags/{}/taggings" @@ -379,7 +391,9 @@ def remove_tagging( f"campaigns/{campaign}/{endpoint.format(tag_id)}/{tagging_id}" ) - def upsert_connection(self, identifiers, tag_data=None, campaign=None, reactivate=True): + def upsert_connection( + self, identifiers, tag_data=None, campaign=None, reactivate=True + ): """ Load or update a connection record in Action Builder between two existing entity records. Only one connection record is allowed per pair of entities, so if the connection already @@ -464,7 +478,9 @@ def deactivate_connection( # Check that either connection or second entity identifier are provided if {connection_identifier, to_identifier} == {None}: - raise ValueError("Must provide a connection ID or an ID for the second entity") + raise ValueError( + "Must provide a connection ID or an ID for the second entity" + ) campaign = self._campaign_check(campaign) diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index d858a63f35..783a0e95ba 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -203,11 +203,15 @@ def setUp(self, m): self.fake_update_person = { k: v for k, v in self.fake_insert_person.items() if k != "entity_type" } - self.fake_update_person["identifier"] = [f"action_builder:{self.fake_entity_id}"] + self.fake_update_person["identifier"] = [ + f"action_builder:{self.fake_entity_id}" + ] self.fake_tag_id = "fake_tag_id" self.fake_tagging_id = "fake_tagging_id" - self.fake_remove_tag_resp = {"message": "Tag has been removed from Taggable Logbook"} + self.fake_remove_tag_resp = { + "message": "Tag has been removed from Taggable Logbook" + } # self.fake_connection = {"person_id": "fake-entity-id-2"} self.fake_connection = { @@ -221,7 +225,9 @@ def test_get_page(self, m): f"{self.api_url}/tags?page=2&per_page=2", text=json.dumps(self.fake_tags_list_2), ) - self.assertEqual(self.bldr._get_page(self.campaign, "tags", 2, 2), self.fake_tags_list_2) + self.assertEqual( + self.bldr._get_page(self.campaign, "tags", 2, 2), self.fake_tags_list_2 + ) @requests_mock.Mocker() def test_get_all_records(self, m): @@ -256,7 +262,9 @@ def test_get_campaign_tags(self, m): f"{self.api_url}/tags?page=3&per_page=25", text=json.dumps({"_embedded": {"osdi:tags": []}}), ) - assert_matching_tables(self.bldr.get_campaign_tags(), Table(self.fake_tags_list)) + assert_matching_tables( + self.bldr.get_campaign_tags(), Table(self.fake_tags_list) + ) @requests_mock.Mocker() def test_get_tag_by_name(self, m): @@ -278,7 +286,9 @@ def prepare_dict_key_intersection(self, dict1, dict2): # keys whose values are not lists (i.e. nested). common_keys = { - key for key, value in dict1.items() if key in dict2 and not isinstance(value, list) + key + for key, value in dict1.items() + if key in dict2 and not isinstance(value, list) } dict1_comp = {key: value for key, value in dict1.items() if key in common_keys} @@ -293,7 +303,9 @@ def test_upsert_entity(self, m): # Flatten and remove items added for spreadable arguments upsert_person = self.fake_upsert_person["person"] - upsert_response = self.bldr._upsert_entity(self.fake_upsert_person, self.campaign) + upsert_response = self.bldr._upsert_entity( + self.fake_upsert_person, self.campaign + ) person_comp, upsert_response_comp = self.prepare_dict_key_intersection( upsert_person, upsert_response @@ -347,10 +359,12 @@ def test_update_entity_record(self, m): def test_remove_entity_record_from_campaign(self, m): m.delete( f"{self.api_url}/people/{self.fake_entity_id}", - json = "{'message': 'Entity has been removed from the campaign'}", + json="{'message': 'Entity has been removed from the campaign'}", ) - remove_response = self.bldr.remove_entity_record_from_campaign(self.fake_entity_id) + remove_response = self.bldr.remove_entity_record_from_campaign( + self.fake_entity_id + ) self.assertEqual( remove_response, @@ -407,7 +421,9 @@ def test_upsert_connection(self, m): f"{self.api_url}/people/{self.fake_entity_id}/connections", json=self.connect_callback, ) - connect_response = self.bldr.upsert_connection([self.fake_entity_id, "fake-entity-id-2"]) + connect_response = self.bldr.upsert_connection( + [self.fake_entity_id, "fake-entity-id-2"] + ) self.assertEqual( connect_response, { From 36cd6123f1869cd90d2de7d0b8a3f26c93cd80d1 Mon Sep 17 00:00:00 2001 From: Yotam Date: Wed, 18 Sep 2024 11:19:11 -0400 Subject: [PATCH 3/3] lint with ruff --- parsons/action_builder/action_builder.py | 32 +++++-------------- .../test_action_builder.py | 32 +++++-------------- 2 files changed, 16 insertions(+), 48 deletions(-) diff --git a/parsons/action_builder/action_builder.py b/parsons/action_builder/action_builder.py index 34870bdcb7..12d0f6f69f 100644 --- a/parsons/action_builder/action_builder.py +++ b/parsons/action_builder/action_builder.py @@ -59,9 +59,7 @@ def _get_page(self, campaign, object_name, page, per_page=25, filter=None): return self.api.get_request(url=url, params=params) - def _get_all_records( - self, campaign, object_name, limit=None, per_page=25, filter=None - ): + def _get_all_records(self, campaign, object_name, limit=None, per_page=25, filter=None): # Returns a list of entries for a given object, such as people, tags, or connections. # See Action Builder API docs for more: https://www.actionbuilder.org/docs/v1/index.html @@ -72,9 +70,7 @@ def _get_all_records( # Keep getting the next page until record limit is exceeded or an empty result returns while True: # Get this page and increase page number to the next one - response = self._get_page( - campaign, object_name, page, per_page, filter=filter - ) + response = self._get_page(campaign, object_name, page, per_page, filter=filter) page = page + 1 # Check that there's actually data @@ -237,9 +233,7 @@ def update_entity_record(self, identifier, data, campaign=None): identifier = [identifier] # Default to assuming identifier comes from Action Builder and add prefix if missing - identifiers = [ - f"action_builder:{id}" if ":" not in id else id for id in identifier - ] + identifiers = [f"action_builder:{id}" if ":" not in id else id for id in identifier] if not isinstance(data, dict): data = {} @@ -272,9 +266,7 @@ def remove_entity_record_from_campaign(self, identifier, campaign=None): url = f"campaigns/{campaign}/people/{identifier}" return self.api.delete_request(url=url) - def add_section_field_values_to_record( - self, identifier, section, field_values, campaign=None - ): + def add_section_field_values_to_record(self, identifier, section, field_values, campaign=None): """ Add one or more tags (i.e. custom field value) to an existing entity record in Action Builder. The tags, along with their field and section, must already exist (except for @@ -305,9 +297,7 @@ def add_section_field_values_to_record( data = {"add_tags": tag_data} - return self.update_entity_record( - identifier=identifier, data=data, campaign=campaign - ) + return self.update_entity_record(identifier=identifier, data=data, campaign=campaign) def remove_tagging( self, @@ -349,9 +339,7 @@ def remove_tagging( raise ValueError("Please supply a tag_name or tag_id!") if {identifier, tagging_id} == {None}: - raise ValueError( - "Please supply an entity or connection identifier, or a tagging id!" - ) + raise ValueError("Please supply an entity or connection identifier, or a tagging id!") campaign = self._campaign_check(campaign) endpoint = "tags/{}/taggings" @@ -391,9 +379,7 @@ def remove_tagging( f"campaigns/{campaign}/{endpoint.format(tag_id)}/{tagging_id}" ) - def upsert_connection( - self, identifiers, tag_data=None, campaign=None, reactivate=True - ): + def upsert_connection(self, identifiers, tag_data=None, campaign=None, reactivate=True): """ Load or update a connection record in Action Builder between two existing entity records. Only one connection record is allowed per pair of entities, so if the connection already @@ -478,9 +464,7 @@ def deactivate_connection( # Check that either connection or second entity identifier are provided if {connection_identifier, to_identifier} == {None}: - raise ValueError( - "Must provide a connection ID or an ID for the second entity" - ) + raise ValueError("Must provide a connection ID or an ID for the second entity") campaign = self._campaign_check(campaign) diff --git a/test/test_action_builder/test_action_builder.py b/test/test_action_builder/test_action_builder.py index 783a0e95ba..da2fb2a102 100644 --- a/test/test_action_builder/test_action_builder.py +++ b/test/test_action_builder/test_action_builder.py @@ -203,15 +203,11 @@ def setUp(self, m): self.fake_update_person = { k: v for k, v in self.fake_insert_person.items() if k != "entity_type" } - self.fake_update_person["identifier"] = [ - f"action_builder:{self.fake_entity_id}" - ] + self.fake_update_person["identifier"] = [f"action_builder:{self.fake_entity_id}"] self.fake_tag_id = "fake_tag_id" self.fake_tagging_id = "fake_tagging_id" - self.fake_remove_tag_resp = { - "message": "Tag has been removed from Taggable Logbook" - } + self.fake_remove_tag_resp = {"message": "Tag has been removed from Taggable Logbook"} # self.fake_connection = {"person_id": "fake-entity-id-2"} self.fake_connection = { @@ -225,9 +221,7 @@ def test_get_page(self, m): f"{self.api_url}/tags?page=2&per_page=2", text=json.dumps(self.fake_tags_list_2), ) - self.assertEqual( - self.bldr._get_page(self.campaign, "tags", 2, 2), self.fake_tags_list_2 - ) + self.assertEqual(self.bldr._get_page(self.campaign, "tags", 2, 2), self.fake_tags_list_2) @requests_mock.Mocker() def test_get_all_records(self, m): @@ -262,9 +256,7 @@ def test_get_campaign_tags(self, m): f"{self.api_url}/tags?page=3&per_page=25", text=json.dumps({"_embedded": {"osdi:tags": []}}), ) - assert_matching_tables( - self.bldr.get_campaign_tags(), Table(self.fake_tags_list) - ) + assert_matching_tables(self.bldr.get_campaign_tags(), Table(self.fake_tags_list)) @requests_mock.Mocker() def test_get_tag_by_name(self, m): @@ -286,9 +278,7 @@ def prepare_dict_key_intersection(self, dict1, dict2): # keys whose values are not lists (i.e. nested). common_keys = { - key - for key, value in dict1.items() - if key in dict2 and not isinstance(value, list) + key for key, value in dict1.items() if key in dict2 and not isinstance(value, list) } dict1_comp = {key: value for key, value in dict1.items() if key in common_keys} @@ -303,9 +293,7 @@ def test_upsert_entity(self, m): # Flatten and remove items added for spreadable arguments upsert_person = self.fake_upsert_person["person"] - upsert_response = self.bldr._upsert_entity( - self.fake_upsert_person, self.campaign - ) + upsert_response = self.bldr._upsert_entity(self.fake_upsert_person, self.campaign) person_comp, upsert_response_comp = self.prepare_dict_key_intersection( upsert_person, upsert_response @@ -362,9 +350,7 @@ def test_remove_entity_record_from_campaign(self, m): json="{'message': 'Entity has been removed from the campaign'}", ) - remove_response = self.bldr.remove_entity_record_from_campaign( - self.fake_entity_id - ) + remove_response = self.bldr.remove_entity_record_from_campaign(self.fake_entity_id) self.assertEqual( remove_response, @@ -421,9 +407,7 @@ def test_upsert_connection(self, m): f"{self.api_url}/people/{self.fake_entity_id}/connections", json=self.connect_callback, ) - connect_response = self.bldr.upsert_connection( - [self.fake_entity_id, "fake-entity-id-2"] - ) + connect_response = self.bldr.upsert_connection([self.fake_entity_id, "fake-entity-id-2"]) self.assertEqual( connect_response, {