diff --git a/src/objecttypes/conf/api.py b/src/objecttypes/conf/api.py index 286b0dbe..60dddd8d 100644 --- a/src/objecttypes/conf/api.py +++ b/src/objecttypes/conf/api.py @@ -19,6 +19,7 @@ "DEFAULT_VERSION": "v2", # NOT to be confused with API_VERSION - it's the major version part "ALLOWED_VERSIONS": ("v2",), "VERSION_PARAM": "version", + "EXCEPTION_HANDLER": "objecttypes.utils.views.exception_handler", # test "TEST_REQUEST_DEFAULT_FORMAT": "json", } diff --git a/src/objecttypes/utils/views.py b/src/objecttypes/utils/views.py index 1830e37c..dd65a4ff 100644 --- a/src/objecttypes/utils/views.py +++ b/src/objecttypes/utils/views.py @@ -1,8 +1,20 @@ from django import http from django.template import TemplateDoesNotExist, loader +from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import requires_csrf_token from django.views.defaults import ERROR_500_TEMPLATE_NAME +import structlog +from open_api_framework.conf.utils import config +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import exception_handler as drf_exception_handler + +logger = structlog.stdlib.get_logger(__name__) + +DEFAULT_CODE = "invalid" +DEFAULT_DETAIL = _("Invalid input.") + @requires_csrf_token def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): @@ -23,3 +35,38 @@ def server_error(request, template_name=ERROR_500_TEMPLATE_NAME): ) context = {"request": request} return http.HttpResponseServerError(template.render(context)) + + +def exception_handler(exc, context): + """ + Transform 5xx errors into DSO-compliant shape. + """ + response = drf_exception_handler(exc, context) + if not response: + if config("DEBUG", default=False): + return None + + data = { + "code": "error", + "title": "Internal Server Error", + "status": status.HTTP_500_INTERNAL_SERVER_ERROR, + "detail": _("A server error has occurred."), + } + event = "api.uncaught_exception" + + response = Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data=data) + logger.exception(event, exc_info=exc) + + return response + + # exception logger event + logger.exception( + "api.handled_exception", + title=getattr(exc, "default_detail", DEFAULT_DETAIL).strip("'"), + code=getattr(exc, "default_code", DEFAULT_CODE), + status=getattr(response, "status_code", status.HTTP_400_BAD_REQUEST), + data=getattr(response, "data", {}), + exc_info=False, + ) + + return response