Skip to content

Commit

Permalink
Add custom error exception_handler
Browse files Browse the repository at this point in the history
  • Loading branch information
k9845 committed Oct 30, 2023
1 parent f3f76b3 commit 58a9269
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
3 changes: 3 additions & 0 deletions main/error_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
TOKEN_INVALID = 4001
NOT_AUTHENTICATED = 4011
AUTHENTICATION_FAILED = 4012
22 changes: 22 additions & 0 deletions main/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from main import error_codes


error_code_map = {
'not_authenticated': error_codes.NOT_AUTHENTICATED,
'authentication_failed': error_codes.AUTHENTICATION_FAILED,
}


def map_error_codes(codes, default=None):
"""
Take in get_codes() value of drf exception
and return a corresponding internal error code.
"""

if isinstance(codes, str):
return error_code_map.get(codes, default)

if codes == {'non_field_errors': ['invalid']}:
return error_codes.TOKEN_INVALID

return default
71 changes: 69 additions & 2 deletions main/exception_handler.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import logging
import sentry_sdk
import logging

from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from rest_framework import status, exceptions
from django_read_only import DjangoReadOnlyError
from django.utils import timezone

from main.errors import map_error_codes

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -56,4 +58,69 @@ def custom_exception_handler(exc, context):
)
response = Response(response_data, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

# Empty the response body but keep the headers
response.data = {}

# Timestamp of exception
response.data['timestamp'] = timezone.now()

if isinstance(exc, (exceptions.NotAuthenticated,)):
response.status_code = status.HTTP_401_UNAUTHORIZED
elif hasattr(exc, 'status_code'):
response.status_code = exc.status_code

if hasattr(exc, 'code'):
# If the raised exception defines a code, send it as
# internal error code
response.data['error_code'] = exc.code
elif hasattr(exc, 'get_codes'):
# Otherwise, try to map the exception.get_codes() value to an
# internal error code.
# If no internal code available, return http status code as
# internal error code by default.
response.data['error_code'] = map_error_codes(
exc.get_codes(), response.status_code)
else:
response.data['error_code'] = response.status_code

# Error message can be defined by the exception as message
# or detail attributres
# Otherwise, it is simply the stringified exception.

errors = None
user_error = None

if hasattr(exc, 'message'):
errors = exc.message
elif hasattr(exc, 'detail'):
if isinstance(exc.detail, list):
errors = [str(error) for error in exc.detail]
else:
errors = exc.detail
elif hasattr(exc, 'default_detail'):
errors = exc.default_detail
elif response.status_code == 404:
errors = 'Resource not found'
else:
errors = str(exc)
user_error = standard_error_string

if hasattr(exc, 'user_message'):
user_error = exc.user_message

# Wrap up string error inside non-field-errors
if isinstance(errors, str):
errors = {
'non_field_errors': [errors],
}
elif isinstance(errors, list) and all([isinstance(error, str) for error in errors]):
errors = {
'non_field_errors': errors,
}

if user_error:
errors['internal_non_field_errors'] = errors.get('non_field_errors')
errors['non_field_errors'] = [user_error]

response.data['errors'] = errors
return response

0 comments on commit 58a9269

Please sign in to comment.