Implement task API with input validation; implement error handling; refactoring
This commit is contained in:
parent
19d264a03b
commit
50aff05614
24 changed files with 612 additions and 131 deletions
73
tofu_api/common/rest/error_handler.py
Normal file
73
tofu_api/common/rest/error_handler.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import logging
|
||||
from typing import Union
|
||||
|
||||
from flask import Flask, Response, jsonify
|
||||
from werkzeug.exceptions import HTTPException
|
||||
from werkzeug.http import HTTP_STATUS_CODES
|
||||
|
||||
from tofu_api.common import string_utils
|
||||
from tofu_api.common.exceptions import AppException
|
||||
from .exceptions import InternalServerError
|
||||
|
||||
T_Response = Union[Response, tuple[Response, int]]
|
||||
|
||||
|
||||
class RestApiErrorHandler:
|
||||
"""
|
||||
Error handler class for REST API errors.
|
||||
"""
|
||||
|
||||
# Dependencies
|
||||
logger: logging.Logger
|
||||
|
||||
# Options
|
||||
debug_mode: bool = False
|
||||
|
||||
# Lookup table for HTTP status codes to API error codes
|
||||
_http_to_api_error_codes: dict[int, str]
|
||||
|
||||
def __init__(self, *, debug_mode: bool = False):
|
||||
self.logger = logging.getLogger(type(self).__name__)
|
||||
self.debug_mode = debug_mode
|
||||
|
||||
# Generate lookup table for HTTP status codes
|
||||
self._http_to_api_error_codes = {
|
||||
http_status: string_utils.str_to_snake_case(name)
|
||||
for http_status, name in HTTP_STATUS_CODES.items()
|
||||
}
|
||||
|
||||
def register_error_handlers(self, app: Flask) -> None:
|
||||
"""
|
||||
Registers error handlers for different types of exceptions to the app.
|
||||
"""
|
||||
app.register_error_handler(AppException, self.handle_app_exception)
|
||||
app.register_error_handler(HTTPException, self.handle_http_exception)
|
||||
app.register_error_handler(Exception, self.handle_generic_exception)
|
||||
|
||||
@staticmethod
|
||||
def handle_app_exception(exception: AppException) -> T_Response:
|
||||
"""
|
||||
Handles exceptions of type `AppException` that were not handled by any more specific handler.
|
||||
"""
|
||||
return jsonify(exception.to_dict()), exception.status_code
|
||||
|
||||
def handle_http_exception(self, exception: HTTPException) -> T_Response:
|
||||
"""
|
||||
Handles exceptions of type `HTTPException`, i.e. any werkzeug HTTP exceptions.
|
||||
"""
|
||||
if exception.code >= 500:
|
||||
self.logger.exception('HTTP exception with status code %s: %s', exception.code, type(exception).__name__)
|
||||
|
||||
return jsonify({
|
||||
'code': self._http_to_api_error_codes.get(exception.code, 'unknown_http_error'),
|
||||
'message': exception.description,
|
||||
}), exception.code
|
||||
|
||||
def handle_generic_exception(self, exception: Exception) -> T_Response:
|
||||
"""
|
||||
Fallback handler for any exceptions not handled by any other handler.
|
||||
"""
|
||||
self.logger.exception('Uncaught exception: %s', type(exception).__name__)
|
||||
|
||||
wrapped_exception = InternalServerError('There was an uncaught error on the server.', inner_exception=exception)
|
||||
return jsonify(wrapped_exception.to_dict(debug=self.debug_mode)), wrapped_exception.status_code
|
||||
Loading…
Add table
Add a link
Reference in a new issue