From e22e671f103d1cbce296818819c912cdbca61478 Mon Sep 17 00:00:00 2001 From: bmccandless Date: Tue, 23 Jun 2020 17:14:42 -0700 Subject: [PATCH] Return 404 when the dataset does not exist (#1581) Also in this PR, restructure the exception handling. fixes #1566 --- server/app/app.py | 15 +++-- server/common/errors.py | 97 +++++++++++++---------------- server/data_common/matrix_loader.py | 3 +- 3 files changed, 57 insertions(+), 58 deletions(-) diff --git a/server/app/app.py b/server/app/app.py index 911ddf6e..a60ca03b 100644 --- a/server/app/app.py +++ b/server/app/app.py @@ -9,7 +9,7 @@ from server_timing import Timing as ServerTiming from http import HTTPStatus import server.common.rest as common_rest -from server.common.errors import DatasetAccessError +from server.common.errors import DatasetAccessError, RequestException from server.common.utils import path_join, Float32JSONEncoder from server.common.data_locator import DataLocator from server.common.health import health_check @@ -81,9 +81,9 @@ def dataset_index(url_dataroot=None, dataset=None): return render_template( "index.html", datasetTitle=dataset_title, SCRIPTS=scripts, INLINE_SCRIPTS=inline_scripts ) - except DatasetAccessError: + except DatasetAccessError as e: return common_rest.abort_and_log( - HTTPStatus.BAD_REQUEST, f"Invalid dataset {dataset}", loglevel=logging.INFO, include_exc_info=True + e.status_code, f"Invalid dataset {dataset}: {e.message}", loglevel=logging.INFO, include_exc_info=True ) @@ -94,6 +94,11 @@ def health(): return health_check(config) +@webbp.errorhandler(RequestException) +def handle_request_exception(error): + return common_rest.abort_and_log(error.status_code, error.message, loglevel=logging.INFO, include_exc_info=True) + + def get_data_adaptor(url_dataroot=None, dataset=None): config = current_app.app_config @@ -123,9 +128,9 @@ def rest_get_data_adaptor(func): try: with get_data_adaptor(self.url_dataroot, dataset) as data_adaptor: return func(self, data_adaptor) - except DatasetAccessError: + except DatasetAccessError as e: return common_rest.abort_and_log( - HTTPStatus.BAD_REQUEST, f"Invalid dataset {dataset}", loglevel=logging.INFO, include_exc_info=True + e.status_code, f"Invalid dataset {dataset}: {e.message}", loglevel=logging.INFO, include_exc_info=True ) return wrapped_function diff --git a/server/common/errors.py b/server/common/errors.py index 9b31ee6d..d1dda2cf 100644 --- a/server/common/errors.py +++ b/server/common/errors.py @@ -1,92 +1,85 @@ -class FilterError(Exception): - """ - Raised when filter is malformed - """ +from http import HTTPStatus + + +class RequestException(Exception): + """Baseclass for exceptions that can be raised from a request.""" + + # The default status code is 400 (Bad Request) + default_status_code = HTTPStatus.BAD_REQUEST + + def __init__(self, message, status_code=None): + Exception.__init__(self) + self.message = message + self.status_code = status_code or self.default_status_code + + +class FilterError(RequestException): + """Raised when filter is malformed""" pass -class JSONEncodingValueError(Exception): - """ - Raised when data cannot be encoded into json - """ +class JSONEncodingValueError(RequestException): + """Raised when data cannot be encoded into json""" pass -class MimeTypeError(Exception): - """ - Raised when incompatible MIME type selected - """ +class MimeTypeError(RequestException): + """Raised when incompatible MIME type selected""" pass -class PrepareError(Exception): - """ - Raised when data is misprepared - """ +class DatasetAccessError(RequestException): + """Raised when file loaded into a DataAdaptor is misformatted""" pass -class DatasetAccessError(Exception): - """ - Raised when file loaded into a DataAdaptor is misformatted - """ +class DisabledFeatureError(RequestException): + """Raised when an attempt to use a disabled feature occurs""" pass -class DisabledFeatureError(Exception): - """ - Raised when an attempt to use a disabled feature occurs - """ +class AnnotationsError(RequestException): + """Raised when an attempt to use the annotations feature fails""" pass -class AnnotationsError(Exception): - """ - Raised when an attempt to use the annotations feature fails - """ +class ComputeError(RequestException): + """Raised when an error occurs during a compute algorithm (such as diffexp)""" + + default_status_code = HTTPStatus.INTERNAL_SERVER_ERROR + + +class ExceedsLimitError(RequestException): + """Raised when an HTTP request exceeds a limit/quota""" + + pass + + +class ColorFormatException(RequestException): + """Raised when color helper functions encounter an unknown color format""" pass class OntologyLoadFailure(Exception): - """ - Raised when reading the ontology file fails - """ + """Raised when reading the ontology file fails""" pass class ConfigurationError(Exception): - """ - Raised when checking configuration errors - """ + """Raised when checking configuration errors""" pass -class ExceedsLimitError(Exception): - """ - Raised when an HTTP request exceeds a limit/quota - """ - - pass - - -class ComputeError(Exception): - """ - Raised when an error occurs during a compute algorithm (such as diffexp) - """ - - pass - - -class ColorFormatException(Exception): - """Raised when color helper functions encounter an unknown color format""" +class PrepareError(Exception): + """Raised when data is misprepared""" pass diff --git a/server/data_common/matrix_loader.py b/server/data_common/matrix_loader.py index fae95b5a..4f7bfaec 100644 --- a/server/data_common/matrix_loader.py +++ b/server/data_common/matrix_loader.py @@ -5,6 +5,7 @@ from server.data_common.rwlock import RWLock from server.common.errors import DatasetAccessError from server.common.data_locator import DataLocator from contextlib import contextmanager +from http import HTTPStatus class MatrixDataCacheItem(object): @@ -217,7 +218,7 @@ class MatrixDataLoader(object): region_name = None if app_config is None else app_config.data_locator__s3__region_name self.location = DataLocator(location, region_name=region_name) if not self.location.exists(): - raise DatasetAccessError("Dataset does not exist.") + raise DatasetAccessError("Dataset does not exist.", HTTPStatus.NOT_FOUND) # matrix_data_type is an enum value of type MatrixDataType self.matrix_data_type = matrix_data_type