From e16d7750c9f1e754a3e14a496380de0b014fffe1 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Mon, 29 Jul 2024 16:04:13 -0500 Subject: Provide a generalised way to handle errors and exceptions. --- uploader/monadic_requests.py | 48 +++++++++++++++++++++++++++++ uploader/oauth2/views.py | 12 +++----- uploader/templates/unhandled_exception.html | 25 ++++++++------- 3 files changed, 67 insertions(+), 18 deletions(-) (limited to 'uploader') diff --git a/uploader/monadic_requests.py b/uploader/monadic_requests.py index f0a60f4..aa34951 100644 --- a/uploader/monadic_requests.py +++ b/uploader/monadic_requests.py @@ -1,9 +1,57 @@ """Wrap requests functions with monads.""" +import traceback +from typing import Union, Optional, Callable + import requests +from requests.models import Response from pymonad.either import Left, Right, Either +from flask import ( + flash, + request, + redirect, + render_template, + current_app as app, + escape as flask_escape) +# HTML Status codes indicating a successful request. SUCCESS_CODES = (200, 201, 202, 203, 204, 205, 206, 207, 208, 226) +# Possible error(s) that can be encontered while attempting to do a request. +PossibleError = Union[Response, Exception] + + +def make_error_handler( + redirect_to: Optional[Response] = None, + cleanup_thunk: Callable = lambda *args: None +) -> Callable[[PossibleError], Response]: + """ + Build a function to gracefully handle errors encountered while doing + requests. + + :rtype: Callable + """ + redirect_to = redirect_to or redirect(request.url) + def __handler__(resp_or_exc: PossibleError) -> Response: + cleanup_thunk() + if issubclass(type(resp_or_exc), Exception): + # Is an exception! + return render_template( + "unhandled_exception.html", + trace=traceback.format_exception(resp_or_exc)) + if isinstance(resp_or_exc, Response): + flash("The authorisation server responded with " + f"({flask_escape(resp_or_exc.status_code)}, " + f"{flask_escape(resp_or_exc.reason)}) for the request to " + f"'{flask_escape(resp_or_exc.request.url)}'", + "alert-danger") + return redirect_to + + flash("Unspecified error!", "alert-danger") + app.logger.debug("Error (%s): %s", type(resp_or_exc), resp_or_exc) + return redirect_to + return __handler__ + + def get(url, params=None, **kwargs) -> Either: """ A wrapper around `requests.get` function. diff --git a/uploader/oauth2/views.py b/uploader/oauth2/views.py index d196e22..40211c8 100644 --- a/uploader/oauth2/views.py +++ b/uploader/oauth2/views.py @@ -15,6 +15,7 @@ from flask import ( from uploader import session from uploader import monadic_requests as mrequests +from uploader.monadic_requests import make_error_handler from . import jwks from .client import ( @@ -118,12 +119,6 @@ def logout(): flash("Successfully logged out.", "alert-success") return redirect("/") - def __handle_failure__(_failure): - app.logger.debug("There was a failure logging out of the system", - exc_info=True, stack_info=True) - __unset_session__(session.session_info()) - return redirect("/") - if user_logged_in(): return session.user_token().then( lambda _tok: mrequests.post( @@ -133,5 +128,8 @@ def logout(): "client_id": oauth2_clientid(), "client_secret": oauth2_clientsecret() })).either( - __handle_failure__, + make_error_handler( + redirect_to=redirect("/"), + cleanup_thunk=lambda: __unset_session__( + session.session_info())), lambda res: __unset_session__(session.session_info())) diff --git a/uploader/templates/unhandled_exception.html b/uploader/templates/unhandled_exception.html index 6e6a051..d6087cb 100644 --- a/uploader/templates/unhandled_exception.html +++ b/uploader/templates/unhandled_exception.html @@ -1,4 +1,5 @@ {%extends "base.html"%} +{%from "flash_messages.html" import flash_all_messages%} {%block title%}System Error{%endblock%} @@ -7,15 +8,17 @@ {%endblock%} {%block contents%} -

- An error has occured, and your request has been aborted. Please notify the - administrator to try and get this sorted. -

-

- Provide the following information to help the administrator figure out and fix - the issue:
-



- {{trace}} -

-

+
+ {{flash_all_messages()}} +

Exception!

+ +

An error has occured, and your request has been aborted. Please notify the + administrator to try and get this fixed.

+

The system has failed with the following error:

+
+
+
+    {{'\n'.join(trace).strip()}}
+  
+
{%endblock%} -- cgit v1.2.3