From 68473e7df3e27abaabc5ea0e111e54ddf9a9fd96 Mon Sep 17 00:00:00 2001 From: Joe Shimkus <35382397+jshimkus-rh@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:22:57 -0400 Subject: [PATCH] (chore) Add api fallback exception handler. (#430) Signed-off-by: Joe Shimkus --- src/aap_eda/api/exceptions.py | 20 ++++++++++ src/aap_eda/settings/default.py | 1 + .../integration/api/test_fallback_handler.py | 38 +++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/integration/api/test_fallback_handler.py diff --git a/src/aap_eda/api/exceptions.py b/src/aap_eda/api/exceptions.py index 497f84c62..bbe0195e8 100644 --- a/src/aap_eda/api/exceptions.py +++ b/src/aap_eda/api/exceptions.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from django.conf import settings from django.utils.translation import gettext_lazy as _ from rest_framework import status from rest_framework.exceptions import ( @@ -20,6 +21,8 @@ NotFound, PermissionDenied, ) +from rest_framework.response import Response +from rest_framework.views import exception_handler __all__ = ( "AuthenticationFailed", @@ -29,9 +32,26 @@ "Conflict", "Unprocessable", "PermissionDenied", + "api_fallback_handler", ) +def api_fallback_handler(exc, context): + response = exception_handler(exc, context) + if (response is None) and (not settings.DEBUG): + response = Response( + status=status.HTTP_500_INTERNAL_SERVER_ERROR, + data={ + "detail": ( + "Unexpected server error" + "; contact your system administrator" + ) + }, + ) + response["context"] = context + return response + + class BadRequest(APIException): status = status.HTTP_400_BAD_REQUEST default_code = "bad_request" diff --git a/src/aap_eda/settings/default.py b/src/aap_eda/settings/default.py index 92411d1ea..87b1eb307 100644 --- a/src/aap_eda/settings/default.py +++ b/src/aap_eda/settings/default.py @@ -238,6 +238,7 @@ def _get_secret_key() -> str: "aap_eda.api.permissions.RoleBasedPermission", ], "TEST_REQUEST_DEFAULT_FORMAT": "json", + "EXCEPTION_HANDLER": "aap_eda.api.exceptions.api_fallback_handler", } # --------------------------------------------------------- diff --git a/tests/integration/api/test_fallback_handler.py b/tests/integration/api/test_fallback_handler.py new file mode 100644 index 000000000..d1390f081 --- /dev/null +++ b/tests/integration/api/test_fallback_handler.py @@ -0,0 +1,38 @@ +from unittest import mock + +import pytest +from rest_framework import status +from rest_framework.test import APIClient + +from tests.integration.constants import api_url_v1 + + +class FallbackException(Exception): + pass + + +def raise_exception(self, request): + raise FallbackException + + +@pytest.mark.django_db +@mock.patch( + "aap_eda.api.views.project.ProjectViewSet.list", new=raise_exception +) +def test_debug_unexpected_exception(client: APIClient, settings): + settings.DEBUG = True + with pytest.raises(FallbackException): + client.get(f"{api_url_v1}/projects/") + + +@pytest.mark.django_db +@mock.patch( + "aap_eda.api.views.project.ProjectViewSet.list", new=raise_exception +) +def test_non_debug_unexpected_exception(client: APIClient, settings): + settings.DEBUG = False + response = client.get(f"{api_url_v1}/projects/") + assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR + + data = response.json() + assert data["detail"].startswith("Unexpected server error")