Skip to content

Commit

Permalink
Hl 1283 ahjo errors (#3208)
Browse files Browse the repository at this point in the history
* feat: log validation errors returned by Ahjo

* feat: add error_from_ahjo to ahjo_status

* feat: save error received from Ahjo callback

* feat: errors from ahjo request to handler's JSON
  • Loading branch information
rikuke authored Aug 15, 2024
1 parent a46fb10 commit 2067d01
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 4 deletions.
3 changes: 3 additions & 0 deletions backend/benefit/applications/api/v1/ahjo_integration_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ def handle_failure_callback(
self, application: Application, callback_data: dict
) -> Response:
self._log_failure_details(application, callback_data)
latest_status = application.ahjo_status.latest()
latest_status.error_from_ahjo = callback_data.get("failureDetails", None)
latest_status.save()
return Response(
{"message": "Callback received but request was unsuccessful at AHJO"},
status=status.HTTP_200_OK,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class AhjoCallbackSerializer(serializers.Serializer):
caseId = serializers.CharField(required=False)
caseGuid = serializers.UUIDField(format="hex_verbose", required=False)
records = serializers.ListField(required=False)
failureDetails = serializers.ListField(required=False)

# You can add additional validation here if needed
def validate_message(self, message):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from rest_framework import serializers

from applications.models import AhjoStatus


class AhjoStatusSerializer(serializers.ModelSerializer):
class Meta:
model = AhjoStatus
fields = ["modified_at", "error_from_ahjo"]
16 changes: 16 additions & 0 deletions backend/benefit/applications/api/v1/serializers/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from rest_framework import serializers
from rest_framework.exceptions import PermissionDenied

from applications.api.v1.serializers.ahjo_status import AhjoStatusSerializer
from applications.api.v1.serializers.application_alteration import (
ApplicantApplicationAlterationSerializer,
HandlerApplicationAlterationSerializer,
Expand Down Expand Up @@ -1549,6 +1550,7 @@ class HandlerApplicationSerializer(BaseApplicationSerializer):
* latest_decision_comment
* handled_at
* paper_application_date
* ahjo_error
"""

# more status transitions
Expand Down Expand Up @@ -1612,6 +1614,19 @@ class HandlerApplicationSerializer(BaseApplicationSerializer):
),
)

ahjo_error = serializers.SerializerMethodField()

def get_latest_ahjo_error(self, obj) -> Union[Dict, None]:
"""Get the latest Ahjo error for the application"""
try:
status = obj.ahjo_status.latest()
except AhjoStatus.DoesNotExist:
return None
return AhjoStatusSerializer(status).data

def get_ahjo_error(self, obj):
return self.get_latest_ahjo_error(obj)

def get_changes(self, obj):
return {
"handler": get_application_change_history_made_by_handler(obj),
Expand Down Expand Up @@ -1663,6 +1678,7 @@ class Meta(BaseApplicationSerializer.Meta):
"decision_proposal_draft",
"handler",
"handled_by_ahjo_automation",
"ahjo_error",
]
read_only_fields = BaseApplicationSerializer.Meta.read_only_fields + [
"latest_decision_comment",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.11 on 2024-08-12 09:06

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("applications", "0077_alter_attachment_attachment_file"),
]

operations = [
migrations.AddField(
model_name="ahjostatus",
name="error_from_ahjo",
field=models.JSONField(null=True),
),
]
3 changes: 3 additions & 0 deletions backend/benefit/applications/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1162,6 +1162,9 @@ class AhjoStatus(TimeStampedModel):
related_name="ahjo_status",
on_delete=models.CASCADE,
)
error_from_ahjo = JSONField(
null=True,
)

def __str__(self):
return self.status
Expand Down
23 changes: 20 additions & 3 deletions backend/benefit/applications/services/ahjo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,7 @@ def send_request_to_ahjo(
f"Missing Ahjo case id for application {self._request.application.application_number}: {e}"
)
except requests.exceptions.HTTPError as e:
LOGGER.error(
f"A HTTP error occurred while sending {self._request} to Ahjo: {e}"
)
self.handle_http_error(e)
except requests.exceptions.RequestException as e:
LOGGER.error(
f"A network error occurred while sending {self._request} to Ahjo: {e}"
Expand All @@ -243,3 +241,22 @@ def send_request_to_ahjo(
f"An error occurred while sending {self._request} to Ahjo: {e}"
)
return None, None

def handle_http_error(self, e: requests.exceptions.HTTPError) -> Tuple[None, dict]:
"""Handle HTTP errors that occur when sending a request to Ahjo.
Also log any validation errors received from Ahjo.
"""
error_response = e.response
application_number = self._request.application.application_number
try:
error_json = error_response.json()
except json.JSONDecodeError:
error_json = None

error_message = f"A HTTP error occurred while sending {self._request} for application \
{application_number} to Ahjo: {e}"

if error_json:
error_message += f" Error message: {error_json}"

LOGGER.error(error_message)
34 changes: 33 additions & 1 deletion backend/benefit/applications/tests/test_ahjo_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,22 +561,48 @@ def test_subscribe_to_decisions_callback_success(
assert decided_application.ahjo_status.latest().status == status_after_callback


@pytest.mark.parametrize(
"request_type, last_ahjo_status, failure_details",
[
(
AhjoRequestType.OPEN_CASE,
AhjoStatusEnum.REQUEST_TO_OPEN_CASE_SENT,
[
{
"id": "INVALID_RECORD_TYPE",
"message": "Asiakirjan tyyppi ei ole sallittu.",
"context": "(Tähän tulisi tietoa virheen esiintymispaikasta jos mahdollista antaa.)",
}
],
),
],
)
@pytest.mark.django_db
def test_ahjo_open_case_callback_failure(
ahjo_client,
ahjo_user_token,
decided_application,
settings,
ahjo_callback_payload,
request_type,
last_ahjo_status,
failure_details,
):
ahjo_callback_payload.pop("caseId", None)
ahjo_callback_payload.pop("caseGuid", None)
ahjo_callback_payload["message"] = AhjoCallBackStatus.FAILURE

AhjoStatus.objects.create(
application=decided_application,
status=last_ahjo_status,
)

ahjo_callback_payload["failureDetails"] = failure_details

url = reverse(
"ahjo_callback_url",
kwargs={
"request_type": AhjoRequestType.OPEN_CASE,
"request_type": request_type,
"uuid": decided_application.id,
},
)
Expand All @@ -589,6 +615,12 @@ def test_ahjo_open_case_callback_failure(
"message": "Callback received but request was unsuccessful at AHJO"
}

decided_application.refresh_from_db()

latest_status = decided_application.ahjo_status.latest()
assert latest_status.status == last_ahjo_status
assert latest_status.error_from_ahjo == ahjo_callback_payload["failureDetails"]


@pytest.mark.parametrize(
"request_type",
Expand Down

0 comments on commit 2067d01

Please sign in to comment.