From 0e1eaa05f4dfa433de4f554ffbc20ae13c39c1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Tue, 26 Nov 2024 14:55:20 +0200 Subject: [PATCH] feat: signer data from Ahjo --- .../jobs/daily/daily_application_jobs.py | 1 + .../management/commands/get_signer.py | 9 ++ .../applications/services/ahjo/enums.py | 5 +- .../services/ahjo/request_handler.py | 19 +++- .../services/ahjo/setting_response_handler.py | 25 ++++- .../applications/services/ahjo_client.py | 7 +- .../benefit/applications/tests/conftest.py | 37 +++++++ .../tests/test_ahjo_request_handler.py | 21 ++++ .../applications/tests/test_ahjo_requests.py | 9 +- .../tests/test_ahjo_response_handler.py | 100 +++++++++++++----- 10 files changed, 194 insertions(+), 39 deletions(-) create mode 100644 backend/benefit/applications/management/commands/get_signer.py create mode 100644 backend/benefit/applications/tests/test_ahjo_request_handler.py diff --git a/backend/benefit/applications/jobs/daily/daily_application_jobs.py b/backend/benefit/applications/jobs/daily/daily_application_jobs.py index f63b8e12e4..68d0840c43 100755 --- a/backend/benefit/applications/jobs/daily/daily_application_jobs.py +++ b/backend/benefit/applications/jobs/daily/daily_application_jobs.py @@ -17,4 +17,5 @@ def execute(self): call_command("delete_applications", keep=180, status="draft") call_command("check_drafts_to_delete", notify=14, keep=180) call_command("get_decision_maker") + call_command("get_signer") call_command("check_and_notify_ending_benefits", notify=30) diff --git a/backend/benefit/applications/management/commands/get_signer.py b/backend/benefit/applications/management/commands/get_signer.py new file mode 100644 index 0000000000..e740b3ab43 --- /dev/null +++ b/backend/benefit/applications/management/commands/get_signer.py @@ -0,0 +1,9 @@ +from applications.enums import AhjoRequestType +from applications.management.commands.ahjo_base_command import AhjoRequestBaseClass + + +class Command(AhjoRequestBaseClass): + help = ( + "Get the decision maker from Ahjo and store it in the database in Ahjo settings" + ) + request_type = AhjoRequestType.GET_SIGNER diff --git a/backend/benefit/applications/services/ahjo/enums.py b/backend/benefit/applications/services/ahjo/enums.py index d7597fa5e4..14f56428c4 100644 --- a/backend/benefit/applications/services/ahjo/enums.py +++ b/backend/benefit/applications/services/ahjo/enums.py @@ -7,11 +7,12 @@ class AhjoSettingName(Enum): Enum representing different Ahjo setting names. The @unique decorator ensures that no two enum members have the same value. - Using auto() generates unique, incrementing values, but we'll use string values - to maintain readability and compatibility with existing database entries. """ DECISION_MAKER = "ahjo_decision_maker" + DECISION_MAKER_ORG_ID = "ahjo_org_identifier" + SIGNER = "ahjo_signer" + SIGNER_ORG_IDS = "ahjo_signer_org_ids" def __str__(self): """ diff --git a/backend/benefit/applications/services/ahjo/request_handler.py b/backend/benefit/applications/services/ahjo/request_handler.py index 76e94f6159..efa38e0cfd 100644 --- a/backend/benefit/applications/services/ahjo/request_handler.py +++ b/backend/benefit/applications/services/ahjo/request_handler.py @@ -8,6 +8,7 @@ AhjoApiClient, AhjoDecisionMakerRequest, AhjoRequest, + AhjoSignerRequest, ) @@ -18,11 +19,21 @@ def __init__(self, ahjo_token: AhjoToken, ahjo_request_type: AhjoRequest): def handle_request_without_application(self): if self.ahjo_request_type == AhjoRequestType.GET_DECISION_MAKER: - self.get_decision_maker_from_ahjo() + self.get_setting_from_ahjo( + request_class=AhjoDecisionMakerRequest, + setting_name=AhjoSettingName.DECISION_MAKER, + ) + if self.ahjo_request_type == AhjoRequestType.GET_SIGNER: + self.get_setting_from_ahjo( + request_class=AhjoSignerRequest, + setting_name=AhjoSettingName.SIGNER, + ) - def get_decision_maker_from_ahjo(self) -> Union[List, None]: - ahjo_client = AhjoApiClient(self.ahjo_token, AhjoDecisionMakerRequest()) + def get_setting_from_ahjo( + self, request_class: AhjoRequest, setting_name: AhjoSettingName + ) -> Union[List, None]: + ahjo_client = AhjoApiClient(self.ahjo_token, request_class()) _, result = ahjo_client.send_request_to_ahjo() AhjoResponseHandler.handle_ahjo_query_response( - setting_name=AhjoSettingName.DECISION_MAKER, data=result + setting_name=setting_name, data=result ) diff --git a/backend/benefit/applications/services/ahjo/setting_response_handler.py b/backend/benefit/applications/services/ahjo/setting_response_handler.py index 479f0f7ef6..eb095d066d 100644 --- a/backend/benefit/applications/services/ahjo/setting_response_handler.py +++ b/backend/benefit/applications/services/ahjo/setting_response_handler.py @@ -36,8 +36,10 @@ def handle_ahjo_query_response( raise ValidationError( f"Invalid response format for setting {setting_name}: expected dictionary" ) - - filtered_data = AhjoResponseHandler.filter_decision_makers(data) + if setting_name == AhjoSettingName.DECISION_MAKER: + filtered_data = AhjoResponseHandler.filter_decision_makers(data) + if setting_name == AhjoSettingName.SIGNER: + filtered_data = AhjoResponseHandler.filter_signers(data) if not filtered_data: LOGGER.warning("No valid decision makers found in response") @@ -129,3 +131,22 @@ def _save_ahjo_setting( raise ValidationError( f"Failed to save setting {setting_name} to database: {str(e)}" ) + + @staticmethod + def filter_signers(data: Dict) -> List[Dict[str, str]]: + """ + Filter the signers Name and ID from the Ahjo response. + + Args: + data: Response data dictionary + + Returns: + List of filtered signer dictionaries + + Raises: + ValidationError: If required fields are missing + """ + result = [] + for item in data["agentList"]: + result.append({"ID": item["ID"], "Name": item["Name"]}) + return result diff --git a/backend/benefit/applications/services/ahjo_client.py b/backend/benefit/applications/services/ahjo_client.py index 40a996cfa0..7c2b0a1cf3 100644 --- a/backend/benefit/applications/services/ahjo_client.py +++ b/backend/benefit/applications/services/ahjo_client.py @@ -10,6 +10,7 @@ from applications.enums import AhjoRequestType, AhjoStatus as AhjoStatusEnum from applications.models import AhjoSetting, AhjoStatus, Application +from applications.services.ahjo.enums import AhjoSettingName from applications.services.ahjo.exceptions import ( AhjoApiClientException, InvalidAhjoTokenException, @@ -151,7 +152,9 @@ class AhjoDecisionMakerRequest(AhjoRequest): @staticmethod def org_identifier() -> str: try: - return AhjoSetting.objects.get(name="ahjo_org_identifier").data["id"] + return AhjoSetting.objects.get( + name=AhjoSettingName.DECISION_MAKER_ORG_ID + ).data["id"] except AhjoSetting.DoesNotExist: raise MissingOrganizationIdentifier( "No organization identifier found in the database." @@ -170,7 +173,7 @@ class AhjoSignerRequest(AhjoRequest): @staticmethod def org_identifier() -> str: try: - setting = AhjoSetting.objects.get(name="ahjo_signer_org_ids") + setting = AhjoSetting.objects.get(name=AhjoSettingName.SIGNER_ORG_IDS) if not setting.data: raise ValueError("Signer organization identifier list is empty") diff --git a/backend/benefit/applications/tests/conftest.py b/backend/benefit/applications/tests/conftest.py index e2eb726932..a716fbd8da 100755 --- a/backend/benefit/applications/tests/conftest.py +++ b/backend/benefit/applications/tests/conftest.py @@ -1145,6 +1145,43 @@ def signer_settings(fake_signers): ) +@pytest.fixture +def signer_response(): + return { + "agentList": [ + { + "agentId": "kissa213", + "links": [], + "ID": "kissa213", + "Name": "Testaaja, Tiina", + "Title": None, + "Role": "decisionMaker", + "Email": None, + }, + { + "agentId": "koira123", + "links": [], + "ID": "koira123", + "Name": "Testaaja, Timo", + "Title": None, + "Role": "decisionMaker", + "Email": None, + }, + { + "agentId": "kala123", + "links": [], + "ID": "kala123", + "Name": "Testaaja, Teppo", + "Title": None, + "Role": "decisionMaker", + "Email": None, + }, + ], + "count": 3, + "links": [], + } + + @pytest.fixture def non_expired_token(): return AhjoToken( diff --git a/backend/benefit/applications/tests/test_ahjo_request_handler.py b/backend/benefit/applications/tests/test_ahjo_request_handler.py new file mode 100644 index 0000000000..86f8fb9b48 --- /dev/null +++ b/backend/benefit/applications/tests/test_ahjo_request_handler.py @@ -0,0 +1,21 @@ +import pytest + +from applications.enums import AhjoRequestType +from applications.services.ahjo.request_handler import AhjoRequestHandler + + +@pytest.mark.parametrize( + "request_type", + [ + (AhjoRequestType.GET_DECISION_MAKER), + (AhjoRequestType.GET_SIGNER), + ], +) +def test_init_sets_token_and_request_type(request_type, non_expired_token): + """ + Test that the constructor correctly sets token and request type + """ + request_handler = AhjoRequestHandler(non_expired_token, request_type) + + assert request_handler.ahjo_token == non_expired_token + assert request_handler.ahjo_request_type == request_type diff --git a/backend/benefit/applications/tests/test_ahjo_requests.py b/backend/benefit/applications/tests/test_ahjo_requests.py index b5fbe870af..7767741025 100644 --- a/backend/benefit/applications/tests/test_ahjo_requests.py +++ b/backend/benefit/applications/tests/test_ahjo_requests.py @@ -9,6 +9,7 @@ from applications.enums import AhjoRequestType, AhjoStatus as AhjoStatusEnum from applications.models import AhjoSetting, AhjoStatus +from applications.services.ahjo.enums import AhjoSettingName from applications.services.ahjo.exceptions import ( AhjoApiClientException, InvalidAhjoTokenException, @@ -67,8 +68,12 @@ def test_ahjo_requests_without_application( settings, non_expired_token, ): - AhjoSetting.objects.create(name="ahjo_org_identifier", data={"id": "1234567-8"}) - AhjoSetting.objects.create(name="ahjo_signer_org_ids", data=["1234567", "7654321"]) + AhjoSetting.objects.create( + name=AhjoSettingName.DECISION_MAKER_ORG_ID, data={"id": "1234567-8"} + ) + AhjoSetting.objects.create( + name=AhjoSettingName.SIGNER_ORG_IDS, data=["1234567", "7654321"] + ) settings.API_BASE_URL = "http://test.com" request_instance = ahjo_request_class() diff --git a/backend/benefit/applications/tests/test_ahjo_response_handler.py b/backend/benefit/applications/tests/test_ahjo_response_handler.py index 089e2f14fb..f4cd357efe 100644 --- a/backend/benefit/applications/tests/test_ahjo_response_handler.py +++ b/backend/benefit/applications/tests/test_ahjo_response_handler.py @@ -26,43 +26,81 @@ def test_ahjo_response_handler_filter_decision_makers(decisionmaker_response): ] +def test_ahjo_response_handler_filter_signers(signer_response): + result = AhjoResponseHandler.filter_signers(signer_response) + assert len(result) == 3 + assert result == [ + { + "Name": signer_response["agentList"][0]["Name"], + "ID": signer_response["agentList"][0]["ID"], + }, + { + "Name": signer_response["agentList"][1]["Name"], + "ID": signer_response["agentList"][1]["ID"], + }, + { + "Name": signer_response["agentList"][2]["Name"], + "ID": signer_response["agentList"][2]["ID"], + }, + ] + + @pytest.mark.parametrize( - "setting_name, test_data", + "setting_name", [ - (AhjoSettingName.DECISION_MAKER, {"Name": "Test Org", "ID": "ORG001"}), + (AhjoSettingName.DECISION_MAKER), + (AhjoSettingName.SIGNER), ], ) @patch("applications.models.AhjoSetting.objects.update_or_create") def test_handle_ahjo_query_response_successful( - mock_update_or_create, decisionmaker_response, setting_name, test_data + mock_update_or_create, decisionmaker_response, setting_name, signer_response ): - """Test successful handling of decision maker response""" - mock_response = decisionmaker_response[1] - - AhjoResponseHandler.handle_ahjo_query_response(setting_name, mock_response) + """Test successful handling of setting response""" + if setting_name == AhjoSettingName.DECISION_MAKER: + mock_response = decisionmaker_response[1] + data = [ + { + "Name": decisionmaker_response[1]["decisionMakers"][0]["Organization"][ + "Name" + ], + "ID": decisionmaker_response[1]["decisionMakers"][0]["Organization"][ + "ID" + ], + }, + { + "Name": decisionmaker_response[1]["decisionMakers"][1]["Organization"][ + "Name" + ], + "ID": decisionmaker_response[1]["decisionMakers"][1]["Organization"][ + "ID" + ], + }, + ] + if setting_name == AhjoSettingName.SIGNER: + mock_response = signer_response + data = [ + { + "Name": signer_response["agentList"][0]["Name"], + "ID": signer_response["agentList"][0]["ID"], + }, + { + "Name": signer_response["agentList"][1]["Name"], + "ID": signer_response["agentList"][1]["ID"], + }, + { + "Name": signer_response["agentList"][2]["Name"], + "ID": signer_response["agentList"][2]["ID"], + }, + ] + + AhjoResponseHandler.handle_ahjo_query_response( + setting_name=setting_name, data=mock_response + ) mock_update_or_create.assert_called_once_with( name=setting_name, - defaults={ - "data": [ - { - "Name": decisionmaker_response[1]["decisionMakers"][0][ - "Organization" - ]["Name"], - "ID": decisionmaker_response[1]["decisionMakers"][0][ - "Organization" - ]["ID"], - }, - { - "Name": decisionmaker_response[1]["decisionMakers"][1][ - "Organization" - ]["Name"], - "ID": decisionmaker_response[1]["decisionMakers"][1][ - "Organization" - ]["ID"], - }, - ] - }, + defaults={"data": data}, ) @@ -88,6 +126,7 @@ def test_filter_decision_makers_missing_decisionmakers_key(): "setting_name, test_data", [ (AhjoSettingName.DECISION_MAKER, {"Name": "Test Org", "ID": "ORG001"}), + (AhjoSettingName.SIGNER, [{"Name": "Test Signer", "ID": "SIGN001"}]), ], ) @patch("applications.models.AhjoSetting.objects.update_or_create") @@ -105,6 +144,13 @@ def test_save_ahjo_setting(mock_update_or_create, setting_name, test_data): "setting_name, test_data", [ (AhjoSettingName.DECISION_MAKER, {"Name": "Test Org", "ID": "ORG001"}), + ( + AhjoSettingName.SIGNER, + [ + {"Name": "Test Signer", "ID": "SIGN001"}, + {"Name": "Test Signer 2", "ID": "SIGN002"}, + ], + ), ], ) def test_save_ahjo_settings_database_error(setting_name, test_data):