From 4fad0a536c9cd6afd26bc7f0c7e3b2eafee04283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Riku=20Kestila=CC=88?= Date: Thu, 7 Dec 2023 15:58:42 +0200 Subject: [PATCH] feat: add function that performs delete/cancel request to Ahjo --- .../applications/services/ahjo_integration.py | 96 ++++++++---- .../applications/tests/test_ahjo_requests.py | 147 +++++++++++++----- 2 files changed, 175 insertions(+), 68 deletions(-) diff --git a/backend/benefit/applications/services/ahjo_integration.py b/backend/benefit/applications/services/ahjo_integration.py index ad8debc1b7..103b74580c 100644 --- a/backend/benefit/applications/services/ahjo_integration.py +++ b/backend/benefit/applications/services/ahjo_integration.py @@ -16,8 +16,12 @@ from django.db.models import QuerySet from django.urls import reverse -from applications.enums import AhjoStatus as AhjoStatusEnum, ApplicationStatus -from applications.models import AhjoSetting, AhjoStatus, Application +from applications.enums import ( + AhjoRequestType, + AhjoStatus as AhjoStatusEnum, + ApplicationStatus, +) +from applications.models import AhjoSetting, AhjoStatus, Application, Attachment from applications.services.ahjo_authentication import AhjoConnector from applications.services.ahjo_payload import prepare_open_case_payload from applications.services.applications_csv_report import ApplicationsCsvService @@ -383,9 +387,14 @@ def get_token() -> str: return -def prepare_headers(access_token: str, application_uuid: uuid) -> dict: +def prepare_headers( + access_token: str, application: Application, request_type: AhjoRequestType +) -> dict: """Prepare the headers for the Ahjo request.""" - url = reverse("ahjo_callback_url", kwargs={"uuid": str(application_uuid)}) + url = reverse( + "ahjo_callback_url", + kwargs={"request_type": request_type, "uuid": str(application.id)}, + ) return { "Authorization": f"Bearer {access_token}", @@ -394,7 +403,7 @@ def prepare_headers(access_token: str, application_uuid: uuid) -> dict: } -def get_application_for_ahjo(id: uuid) -> Optional[Application]: +def get_application_for_ahjo(id: uuid.UUID) -> Optional[Application]: """Get the first accepted application.""" application = ( Application.objects.filter(pk=id, status=ApplicationStatus.ACCEPTED) @@ -411,52 +420,87 @@ def get_application_for_ahjo(id: uuid) -> Optional[Application]: return application -def create_status_for_application(application: Application): +def create_status_for_application(application: Application, status: AhjoStatusEnum): """Create a new AhjoStatus for the application.""" - AhjoStatus.objects.create( - application=application, status=AhjoStatusEnum.REQUEST_TO_OPEN_CASE_SENT - ) + AhjoStatus.objects.create(application=application, status=status) -def do_ahjo_request_with_json_payload( - url: str, headers: dict, data: dict, application: Application, timeout: int = 10 +def send_request_to_ahjo( + request_type: AhjoRequestType, + headers: dict, + application: Application, + data: dict = {}, + timeout: int = 10, ): + """Send a request to Ahjo.""" headers["Content-Type"] = "application/json" - json_data = json.dumps(data) + url_base = f"{settings.AHJO_REST_API_URL}/cases" + + if request_type == AhjoRequestType.OPEN_CASE: + method = "POST" + status = AhjoStatusEnum.REQUEST_TO_OPEN_CASE_SENT + api_url = url_base + data = json.dumps(data) + elif request_type == AhjoRequestType.DELETE_APPLICATION: + method = "DELETE" + status = AhjoStatusEnum.DELETE_REQUEST_SENT + api_url = f"{url_base}/{application.ahjo_case_id}" try: - response = requests.post( - f"{url}/cases", headers=headers, timeout=timeout, data=json_data + response = requests.request( + method, api_url, headers=headers, timeout=timeout, data=data ) response.raise_for_status() if response.ok: - create_status_for_application(application) + create_status_for_application(application, status) LOGGER.info( - f"Open case for application {application.id} Request to Ahjo was successful." + f"Request for application {application.id} to Ahjo was successful." ) except requests.exceptions.HTTPError as e: - # Handle the HTTP error - LOGGER.error(f"HTTP error occurred while sending request to Ahjo: {e}") + LOGGER.error( + f"HTTP error occurred while sending a {request_type} request to Ahjo: {e}" + ) + raise except requests.exceptions.RequestException as e: - # Handle the network error - LOGGER.error(f"Network error occurred while sending request to Ahjo: {e}") + LOGGER.error( + f"Network error occurred while sending a {request_type} request to Ahjo: {e}" + ) + raise except Exception as e: - # Handle any other error - LOGGER.error(f"Error occurred while sending request to Ahjo: {e}") + LOGGER.error( + f"Error occurred while sending request a {request_type} to Ahjo: {e}" + ) + raise -def open_case_in_ahjo(application_id: uuid): +def open_case_in_ahjo(application_id: uuid.UUID): """Open a case in Ahjo.""" try: application = get_application_for_ahjo(application_id) - ahjo_api_url = settings.AHJO_REST_API_URL ahjo_token = get_token() - headers = prepare_headers(ahjo_token.access_token, application.id) + headers = prepare_headers( + ahjo_token.access_token, application.id, AhjoRequestType.OPEN_CASE + ) data = prepare_open_case_payload(application) - do_ahjo_request_with_json_payload(ahjo_api_url, headers, data, application) + send_request_to_ahjo(AhjoRequestType.OPEN_CASE, headers, data, application) + except ObjectDoesNotExist as e: + LOGGER.error(f"Object not found: {e}") + except ImproperlyConfigured as e: + LOGGER.error(f"Improperly configured: {e}") + + +def delete_application_in_ahjo(application_id: uuid.UUID): + """Delete/cancel an application in Ahjo.""" + try: + application = get_application_for_ahjo(application_id) + ahjo_token = get_token() + headers = prepare_headers( + ahjo_token.access_token, application.id, AhjoRequestType.DELETE_APPLICATION + ) + send_request_to_ahjo(AhjoRequestType.DELETE_APPLICATION, headers, application) except ObjectDoesNotExist as e: LOGGER.error(f"Object not found: {e}") except ImproperlyConfigured as e: diff --git a/backend/benefit/applications/tests/test_ahjo_requests.py b/backend/benefit/applications/tests/test_ahjo_requests.py index 4fea054f87..a9c9801cf0 100644 --- a/backend/benefit/applications/tests/test_ahjo_requests.py +++ b/backend/benefit/applications/tests/test_ahjo_requests.py @@ -1,25 +1,40 @@ -import uuid from unittest.mock import patch +import pytest import requests import requests_mock +from django.conf import settings from django.urls import reverse -from applications.enums import AhjoStatus -from applications.services.ahjo_integration import ( - do_ahjo_request_with_json_payload, - prepare_headers, -) +from applications.enums import AhjoRequestType, AhjoStatus +from applications.services.ahjo_integration import prepare_headers, send_request_to_ahjo + +@pytest.fixture +def application_with_ahjo_case_id(decided_application): + decided_application.ahjo_case_id = "12345" + decided_application.save() + return decided_application -def test_prepare_headers(settings): + +@pytest.mark.parametrize( + "request_type", + [ + (AhjoRequestType.OPEN_CASE,), + (AhjoRequestType.DELETE_APPLICATION,), + ], +) +def test_prepare_headers(settings, request_type, decided_application): settings.API_BASE_URL = "http://test.com" access_token = "test_token" - application_uuid = uuid.uuid4() - headers = prepare_headers(access_token, application_uuid) + url = reverse( + "ahjo_callback_url", + kwargs={"request_type": request_type, "uuid": decided_application.id}, + ) + + headers = prepare_headers(access_token, decided_application, request_type) - url = reverse("ahjo_callback_url", kwargs={"uuid": str(application_uuid)}) expected_headers = { "Authorization": f"Bearer {access_token}", "Accept": "application/hal+json", @@ -29,61 +44,109 @@ def test_prepare_headers(settings): assert headers == expected_headers -def test_do_ahjo_request_with_json_payload_success( - decided_application, ahjo_open_case_payload +@pytest.mark.parametrize( + "request_type, request_url, ahjo_status", + [ + ( + AhjoRequestType.OPEN_CASE, + f"{settings.AHJO_REST_API_URL}/cases", + AhjoStatus.REQUEST_TO_OPEN_CASE_SENT, + ), + ( + AhjoRequestType.DELETE_APPLICATION, + f"{settings.AHJO_REST_API_URL}/cases/12345", + AhjoStatus.DELETE_REQUEST_SENT, + ), + ], +) +def test_send_request_to_ahjo( + request_type, + request_url, + ahjo_status, + application_with_ahjo_case_id, + ahjo_open_case_payload, ): - url = "http://test.com" headers = {"Authorization": "Bearer test"} with requests_mock.Mocker() as m: - m.post(f"{url}/cases", text="data") - do_ahjo_request_with_json_payload( - url, headers, ahjo_open_case_payload, decided_application + if request_type == AhjoRequestType.OPEN_CASE: + m.post(request_url, text="data") + elif request_type == AhjoRequestType.DELETE_APPLICATION: + m.delete(request_url) + send_request_to_ahjo( + request_type, headers, application_with_ahjo_case_id, ahjo_open_case_payload ) - decided_application.refresh_from_db() - assert ( - decided_application.ahjo_status.latest().status - == AhjoStatus.REQUEST_TO_OPEN_CASE_SENT - ) + assert m.called + + application_with_ahjo_case_id.refresh_from_db() + assert application_with_ahjo_case_id.ahjo_status.latest().status == ahjo_status @patch("applications.services.ahjo_integration.LOGGER") -def test_http_error(mock_logger, decided_application, ahjo_open_case_payload): - url = "http://mockedurl.com" +def test_http_error(mock_logger, application_with_ahjo_case_id): headers = {} - data = ahjo_open_case_payload - application = decided_application - with requests_mock.Mocker() as m: - m.post(f"{url}/cases", status_code=400) - do_ahjo_request_with_json_payload(url, headers, data, application) + with requests_mock.Mocker() as m, pytest.raises(requests.exceptions.HTTPError): + m.post(f"{settings.AHJO_REST_API_URL}/cases", status_code=400) + send_request_to_ahjo( + AhjoRequestType.OPEN_CASE, headers, application_with_ahjo_case_id + ) + assert m.called + mock_logger.error.assert_called() + + with requests_mock.Mocker() as m, pytest.raises(requests.exceptions.HTTPError): + m.delete(f"{settings.AHJO_REST_API_URL}/cases/12345", status_code=400) + send_request_to_ahjo( + AhjoRequestType.DELETE_APPLICATION, headers, application_with_ahjo_case_id + ) + + assert m.called mock_logger.error.assert_called() @patch("applications.services.ahjo_integration.LOGGER") -def test_network_error(mock_logger, decided_application, ahjo_open_case_payload): - url = "http://mockedurl.com" +def test_network_error(mock_logger, application_with_ahjo_case_id): headers = {} - data = ahjo_open_case_payload - application = decided_application + exception = requests.exceptions.ConnectionError - with requests_mock.Mocker() as m: - m.post(f"{url}/cases", exc=requests.exceptions.ConnectionError) - do_ahjo_request_with_json_payload(url, headers, data, application) + with requests_mock.Mocker() as m, pytest.raises(exception): + m.post(f"{settings.AHJO_REST_API_URL}/cases", exc=exception) + send_request_to_ahjo( + AhjoRequestType.OPEN_CASE, headers, application_with_ahjo_case_id + ) + + assert m.called + mock_logger.error.assert_called() + + with requests_mock.Mocker() as m, pytest.raises(exception): + m.delete(f"{settings.AHJO_REST_API_URL}/cases/12345", exc=exception) + send_request_to_ahjo( + AhjoRequestType.DELETE_APPLICATION, headers, application_with_ahjo_case_id + ) + assert m.called mock_logger.error.assert_called() @patch("applications.services.ahjo_integration.LOGGER") -def test_other_exception(mock_logger, decided_application, ahjo_open_case_payload): - url = "http://mockedurl.com" +def test_other_error(mock_logger, application_with_ahjo_case_id): headers = {} - data = ahjo_open_case_payload - application = decided_application - with requests_mock.Mocker() as m: - m.post(f"{url}/cases", exc=Exception("Some error")) - do_ahjo_request_with_json_payload(url, headers, data, application) + with requests_mock.Mocker() as m, pytest.raises(Exception): + m.post(f"{settings.AHJO_REST_API_URL}/cases", exc=Exception) + send_request_to_ahjo( + AhjoRequestType.OPEN_CASE, headers, application_with_ahjo_case_id + ) + + assert m.called + mock_logger.error.assert_called() + + with requests_mock.Mocker() as m, pytest.raises(Exception): + m.delete(f"{settings.AHJO_REST_API_URL}/cases/12345", exc=Exception) + send_request_to_ahjo( + AhjoRequestType.DELETE_APPLICATION, headers, application_with_ahjo_case_id + ) + assert m.called mock_logger.error.assert_called()