Skip to content

Commit

Permalink
Generalize provider error handling to processes (#1495)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
totycro authored Jan 15, 2024
1 parent af5dbb8 commit 1da681b
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 19 deletions.
5 changes: 2 additions & 3 deletions pygeoapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3381,10 +3381,9 @@ def execute_process(self, request: Union[APIRequest, Any],
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:
Expand Down
53 changes: 53 additions & 0 deletions pygeoapi/error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# =================================================================
#
# Authors: Bernhard Mallinger <[email protected]>
#
# 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
6 changes: 4 additions & 2 deletions pygeoapi/process/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import logging
from typing import Any, Tuple

from pygeoapi.error import GenericError

LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -64,14 +66,14 @@ def __repr__(self):
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):
Expand Down
17 changes: 3 additions & 14 deletions pygeoapi/provider/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
from enum import Enum
from http import HTTPStatus

from pygeoapi.error import GenericError

LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -272,23 +274,10 @@ def __repr__(self):
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"""
Expand Down

0 comments on commit 1da681b

Please sign in to comment.