Skip to content

Commit

Permalink
Improve handling of openeAPI errors: show openEO error code and messa…
Browse files Browse the repository at this point in the history
…ge better
  • Loading branch information
soxofaan committed Oct 24, 2019
1 parent b27ba81 commit ee50d04
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
38 changes: 36 additions & 2 deletions openeo/rest/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ def url_join(root_url: str, path: str):
return urljoin(root_url.rstrip('/') + '/', path.lstrip('/'))


class OpenEoApiError(Exception):
"""
Error returned by OpenEO API according to https://open-eo.github.io/openeo-api/errors/
"""

def __init__(self, http_status_code: int = None,
code: str = 'unknown', message: str = 'unknown error', id: str = None, url: str = None):
self.http_status_code = http_status_code
self.code = code
self.message = message
self.id = id
self.url = url
super().__init__("[{s}] {c}: {m}".format(s=self.http_status_code, c=self.code, m=self.message))


class RestApiConnection:
"""Base connection class implementing generic REST API request functionality"""

Expand Down Expand Up @@ -59,10 +74,28 @@ def request(self, method: str, path: str, headers: dict = None, auth: AuthBase =
**kwargs
)
if check_status:
# TODO: raise a custom OpenEO branded exception?
resp.raise_for_status()
# TODO: option to specify the list/range of expected status codes?
if resp.status_code >= 400:
self._raise_api_error(resp)
return resp

def _raise_api_error(self, response: requests.Response):
"""Convert API error response to Python exception"""
try:
# Try parsing the error info according to spec and wrap it in an exception.
info = response.json()
exception = OpenEoApiError(
http_status_code=response.status_code,
code=info.get("code", "unknown"),
message=info.get("message", "unknown error"),
id=info.get("id"),
url=info.get("url"),
)
except Exception:
# When parsing went wrong: give minimal information.
exception = OpenEoApiError(http_status_code=response.status_code, message=response.text)
raise exception

def get(self, path, stream=False, auth: AuthBase = None, **kwargs) -> Response:
"""
Do GET request to REST API.
Expand Down Expand Up @@ -422,6 +455,7 @@ def parse_json_response(self, response: requests.Response):
self._handle_error_response(response)

def _handle_error_response(self, response):
# TODO replace this with `_raise_api_error`
if response.status_code == 502:
from requests.exceptions import ProxyError
raise ProxyError("The proxy returned an error, this could be due to a timeout.")
Expand Down
30 changes: 29 additions & 1 deletion tests/rest/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import requests_mock

from openeo.rest.auth.auth import NullAuth, BearerAuth
from openeo.rest.connection import Connection, RestApiConnection, connect
from openeo.rest.connection import Connection, RestApiConnection, connect, OpenEoApiError

API_URL = "https://oeo.net/"

Expand Down Expand Up @@ -72,6 +72,34 @@ def test_connect_with_session():
)


def test_api_error(requests_mock):
conn = Connection(API_URL)
requests_mock.get('https://oeo.net/collections/foobar', status_code=404, json={
"code": "CollectionNotFound", "message": "No such things as a collection 'foobar'", "id": "54321"
})
with pytest.raises(OpenEoApiError) as exc_info:
conn.describe_collection("foobar")
exc = exc_info.value
assert exc.http_status_code == 404
assert exc.code == "CollectionNotFound"
assert exc.message == "No such things as a collection 'foobar'"
assert exc.id == "54321"
assert exc.url is None


def test_api_error_non_json(requests_mock):
conn = Connection(API_URL)
requests_mock.get('https://oeo.net/collections/foobar', status_code=500, text="olapola")
with pytest.raises(OpenEoApiError) as exc_info:
conn.describe_collection("foobar")
exc = exc_info.value
assert exc.http_status_code == 500
assert exc.code == "unknown"
assert exc.message == "olapola"
assert exc.id is None
assert exc.url is None


def test_authenticate_basic(requests_mock):
conn = Connection(API_URL)

Expand Down

0 comments on commit ee50d04

Please sign in to comment.