Generalize provider error handling to processes (#1495)

This allows triggering errors from a processor with specific
http status code, ogc exception code and a custom message.

This is very useful the processor realizes that the input parameters
don't make sense or are not allowed, in which case it can supply a
descriptive error message and an http status code different from 500.
This commit is contained in:
Bernhard Mallinger
2024-01-15 19:54:00 +01:00
committed by GitHub
parent af5dbb8350
commit 1da681bf1b
4 changed files with 62 additions and 19 deletions
+2 -3
View File
@@ -3381,10 +3381,9 @@ class API:
headers['Location'] = f'{self.base_url}/jobs/{job_id}'
except ProcessorExecuteError as err:
LOGGER.error(err)
msg = 'Processing error'
return self.get_exception(
HTTPStatus.INTERNAL_SERVER_ERROR, headers,
request.format, 'NoApplicableCode', msg)
err.http_status_code, headers,
request.format, err.ogc_exception_code, err.message)
response = {}
if status == JobStatus.failed:
+53
View File
@@ -0,0 +1,53 @@
# =================================================================
#
# Authors: Bernhard Mallinger <bernhard.mallinger@eox.at>
#
# Copyright (c) 2024 Bernhard Mallinger
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# =================================================================
from http import HTTPStatus
class GenericError(Exception):
"""Exception class where error codes and messages
can be defined in custom error subclasses, so custom
providers and processes can raise appropriate errors.
"""
ogc_exception_code = 'NoApplicableCode'
http_status_code = HTTPStatus.INTERNAL_SERVER_ERROR
default_msg = 'Unknown error'
def __init__(self, msg=None, *args, user_msg=None) -> None:
# if only a user_msg is provided, use it as msg
if user_msg and not msg:
msg = user_msg
super().__init__(msg, *args)
self.user_msg = user_msg
@property
def message(self):
return self.user_msg if self.user_msg else self.default_msg
+4 -2
View File
@@ -30,6 +30,8 @@
import logging
from typing import Any, Tuple
from pygeoapi.error import GenericError
LOGGER = logging.getLogger(__name__)
@@ -64,14 +66,14 @@ class BaseProcessor:
return f'<BaseProcessor> {self.name}'
class ProcessorGenericError(Exception):
class ProcessorGenericError(GenericError):
"""processor generic error"""
pass
class ProcessorExecuteError(ProcessorGenericError):
"""query / backend error"""
pass
default_msg = "generic error (check logs)"
class JobError(Exception):
+3 -14
View File
@@ -32,6 +32,8 @@ import logging
from enum import Enum
from http import HTTPStatus
from pygeoapi.error import GenericError
LOGGER = logging.getLogger(__name__)
@@ -272,23 +274,10 @@ class BaseProvider:
return f'<BaseProvider> {self.type}'
class ProviderGenericError(Exception):
class ProviderGenericError(GenericError):
"""provider generic error"""
ogc_exception_code = 'NoApplicableCode'
http_status_code = HTTPStatus.INTERNAL_SERVER_ERROR
default_msg = 'generic error (check logs)'
def __init__(self, msg=None, *args, user_msg=None) -> None:
# if only a user_msg is provided, use it as msg
if user_msg and not msg:
msg = user_msg
super().__init__(msg, *args)
self.user_msg = user_msg
@property
def message(self):
return self.user_msg if self.user_msg else self.default_msg
class ProviderConnectionError(ProviderGenericError):
"""provider connection error"""