# -*- coding: utf-8 -*-
"""
Module for all error handling.
All error handling is defined here as well as
made sure, we log them into the app.logger facility.
"""
import typing
from flask import Blueprint, render_template, current_app, request, make_response
from werkzeug.exceptions import HTTPException
from werkzeug.wrappers.response import Response
from sqlalchemy.exc import SQLAlchemyError, DBAPIError
from ResearchNotes.database import db
bp = Blueprint("errors", __name__)
[docs]@bp.app_errorhandler(400)
def badrequest_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response:
"""
Error handler for bad request.
Provide an error handler for code 400 HTTPExceptions (Bad request)
Parameters
----------
error : TYPE
DESCRIPTION.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.warning(f"{str(error)}")
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response("<h1>Sorry, bad HTTP request.</h1>", 400)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("400.html"), 400
[docs]@bp.app_errorhandler(403)
def forbidden_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response:
"""
Error for not authorized for certain action.
Parameters
----------
error : str
Error message from function calling the handler.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.warning(f"{str(error)}")
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response(
"<h1>Sorry, you do not have the right to access this resource.</h1>"
+ "This might either come from your user role or the group you are in.",
403,
)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("403.html"), 403
[docs]@bp.app_errorhandler(404)
def not_found_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response:
"""
Errorhandler for page not found.
Parameters
----------
error : str
Error message from function calling the handler.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.warning(f"{str(error)}")
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response(
"<h1>Sorry, the resource your were looking for was not found.</h1>", 404
)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("404.html"), 404
[docs]@bp.app_errorhandler(413)
def too_large(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int]:
"""
Error handler for file to large (does not work well with dropzone.js).
Parameters
----------
error : str
Error message from function calling the handler.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.warning(f"{str(error)}")
return "File is too large", 413
[docs]@bp.app_errorhandler(418)
def i_am_a_teapot(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response:
"""
Errorhandler for "I am a teapot" status.
Parameters
----------
error : str
Error message from function calling the handler.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.warning(f"{str(error)}")
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response("<h1>Sorry, I am a terapot</h1>", 418)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("418.html"), 418
[docs]@bp.app_errorhandler(500)
def internal_error(error: typing.Union[str, HTTPException]) -> typing.Tuple[str, int] | Response:
"""
Errorhandler for internal errors.
Tries to rollback database, if possible.
Parameters
----------
error : str
Error message from function calling the handler.
Returns
-------
str
View to error page
int
HTTP error code.
"""
current_app.logger.error("Real 500 error")
current_app.logger.error(f"{str(error)}")
try:
db.session.rollback()
except (SQLAlchemyError, DBAPIError) as dberror:
current_app.logger.error(f"Database error : {str(dberror)}")
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response(
"<h1> Upsi, An unexpected error has occurred</h1>\nThis should not"
+ " happen. Something went wrong. Sorry.\n<br>\nPlease, tell the admin "
+ "that we can fix it.",
500,
)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("500.html"), 500
[docs]@bp.app_errorhandler(Exception)
def handle_exception(
error: typing.Union[str, Exception]
) -> typing.Union[HTTPException, typing.Tuple[str, int]] | Response:
"""
Errorhandler for all non HTTPExceptions.
Parameters
----------
error : Exception
All exceptions go here and if they are not of the type HTTPExceptions
will be handed here.
Returns
-------
View
Returns view to our internal error page.
"""
# pass through HTTP errors
if isinstance(error, HTTPException):
return error
# now you're handling non-HTTP exceptions only
current_app.logger.error("Something bad happen. Exception raised")
current_app.logger.exception(error)
debug = None
if current_app.debug:
debug = error
if "HX-Request" in request.headers:
current_app.logger.info("Error came from Htmx request")
response = make_response(
"<h1> Upsi, An unexpected error has occurred</h1>\nThis should not"
+ " happen. Something went wrong. Sorry.\n<br>\nPlease, tell the admin "
+ f"that we can fix it. <p> {debug} </p>",
500,
)
# response.headers["HX-Reswap"] = ""
response.headers["HX-Retarget"] = "#main"
return response
return render_template("500.html", debug=debug), 500