|
6 | 6 | from rest_framework.views import set_rollback, exception_handler |
7 | 7 |
|
8 | 8 |
|
| 9 | +def error_response(code: str, message: str, details=None, status_code: int = 400) -> Response: |
| 10 | + """ |
| 11 | + Create a standardized error response. |
| 12 | +
|
| 13 | + Format: |
| 14 | + { |
| 15 | + "error": { |
| 16 | + "code": "ERROR_CODE", |
| 17 | + "message": "Human readable message", |
| 18 | + "details": {} // optional |
| 19 | + } |
| 20 | + } |
| 21 | + """ |
| 22 | + error_body = { |
| 23 | + "error": { |
| 24 | + "code": code, |
| 25 | + "message": message, |
| 26 | + } |
| 27 | + } |
| 28 | + if details is not None: |
| 29 | + error_body["error"]["details"] = details |
| 30 | + return Response(error_body, status=status_code) |
| 31 | + |
| 32 | + |
9 | 33 | class HealthcheckException(APIException): |
10 | 34 | """Exception class used for when the application's health check fails""" |
11 | 35 | pass |
@@ -36,24 +60,33 @@ def custom_exception_handler(exc, context): |
36 | 60 | # give more context on the error since DRF masks it as Not Found |
37 | 61 | if isinstance(exc, Http404): |
38 | 62 | set_rollback() |
39 | | - return Response(str(exc), status=status.HTTP_404_NOT_FOUND) |
| 63 | + return error_response('NOT_FOUND', str(exc), status_code=status.HTTP_404_NOT_FOUND) |
40 | 64 | # Convert Django ValidationError to DRF 400 response |
41 | 65 | if isinstance(exc, ValidationError): |
42 | 66 | set_rollback() |
43 | 67 | if hasattr(exc, 'message_dict'): |
44 | | - return Response(exc.message_dict, status=status.HTTP_400_BAD_REQUEST) |
| 68 | + return error_response( |
| 69 | + 'VALIDATION_ERROR', 'Validation failed', |
| 70 | + exc.message_dict, status.HTTP_400_BAD_REQUEST) |
45 | 71 | elif hasattr(exc, 'messages'): |
46 | | - return Response({'non_field_errors': exc.messages}, status=status.HTTP_400_BAD_REQUEST) |
47 | | - return Response({'non_field_errors': [str(exc)]}, status=status.HTTP_400_BAD_REQUEST) |
| 72 | + return error_response( |
| 73 | + 'VALIDATION_ERROR', 'Validation failed', |
| 74 | + {'non_field_errors': exc.messages}, |
| 75 | + status.HTTP_400_BAD_REQUEST) |
| 76 | + return error_response( |
| 77 | + 'VALIDATION_ERROR', str(exc), |
| 78 | + status_code=status.HTTP_400_BAD_REQUEST) |
48 | 79 | # Call REST framework's default exception handler after specific 404 handling, |
49 | 80 | # to get the standard error response. |
50 | 81 | response = exception_handler(exc, context) |
51 | 82 | # No response means DRF couldn't handle it, output a generic 500 in a JSON format |
52 | 83 | if response is None: |
53 | 84 | logging.exception('Uncaught Exception', exc_info=exc) |
54 | 85 | set_rollback() |
55 | | - return Response({'detail': 'Server Error'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) |
| 86 | + return error_response( |
| 87 | + 'INTERNAL_ERROR', 'An internal error occurred', |
| 88 | + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) |
56 | 89 | # log a few different types of exception instead of using APIException |
57 | 90 | if isinstance(exc, (DryccException, ServiceUnavailable, HealthcheckException)): |
58 | | - logging.exception(exc.__cause__, exc_info=exc) |
| 91 | + logging.exception(str(exc), exc_info=True) |
59 | 92 | return response |
0 commit comments