Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion src/py/mat3ra/api_client/utils/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
import urllib.parse


def _extract_server_message(response: requests.Response) -> str:
"""Extract human-readable message from a JSEND-formatted error response body."""
try:
body = response.json()
return body.get("data", {}).get("message") or body.get("message") or ""
except Exception:
return ""


class BaseConnection(object):
"""
Base connection class to inherit from. This class should not be instantiated directly.
Expand Down Expand Up @@ -32,7 +41,14 @@ def request(self, method, url, params=None, data=None, headers=None):
params (dict): URL parameters to append to the URL.
"""
self.response = self.session.request(method=method.lower(), url=url, params=params, data=data, headers=headers)
self.response.raise_for_status()
try:
self.response.raise_for_status()
except requests.HTTPError:
status_code = self.response.status_code
server_message = _extract_server_message(self.response)
detail = server_message or "HTTP Error"
message = f"Error {status_code}: {detail}."
raise requests.HTTPError(message, response=self.response) from None

def get_response(self):
"""
Expand Down
32 changes: 25 additions & 7 deletions tests/py/unit/test_httpBase.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
from unittest import mock

from mat3ra.api_client.utils.http import Connection
Expand All @@ -7,11 +8,10 @@
API_VERSION_1 = "2018-10-1"
API_VERSION_2 = "2018-10-2"
HTTP_STATUS_UNAUTHORIZED = 401
HTTP_REASON_UNAUTHORIZED = "Unauthorized"
HTTP_STATUS_UNKNOWN = 418
EMPTY_CONTENT = ""
TEST_ENTITY_ID = "28FMvD5knJZZx452H"
EMPTY_USERNAME = ""
EMPTY_PASSWORD = ""
SERVER_MESSAGE = "Custom server error message"
SERVER_ERROR_RESPONSE = json.dumps({"message": SERVER_MESSAGE})


class HTTPBaseUnitTest(EndpointBaseUnitTest):
Expand All @@ -36,8 +36,26 @@ def test_preamble_version(self):

@mock.patch("requests.sessions.Session.request")
def test_raise_http_error(self, mock_request):
mock_request.return_value = self.mock_response(EMPTY_CONTENT, HTTP_STATUS_UNAUTHORIZED,
reason=HTTP_REASON_UNAUTHORIZED)
mock_request.return_value = self.mock_response(EMPTY_CONTENT, HTTP_STATUS_UNAUTHORIZED)
with self.assertRaises(HTTPError):
conn = Connection(self.host, self.port, version=API_VERSION_1, secure=True)
conn.request("POST", "login", data={"username": EMPTY_USERNAME, "password": EMPTY_PASSWORD})
conn.request("POST", "login")

@mock.patch("requests.sessions.Session.request")
def test_http_error_message_with_server_message(self, mock_request):
mock_request.return_value = self.mock_response(SERVER_ERROR_RESPONSE, HTTP_STATUS_UNAUTHORIZED)
with self.assertRaises(HTTPError) as ctx:
conn = Connection(self.host, self.port, version=API_VERSION_1, secure=True)
conn.request("POST", "login")
self.assertIn("Error 401", str(ctx.exception))
self.assertIn(SERVER_MESSAGE, str(ctx.exception))

@mock.patch("requests.sessions.Session.request")
def test_http_error_message_without_server_message(self, mock_request):
mock_request.return_value = self.mock_response(EMPTY_CONTENT, HTTP_STATUS_UNKNOWN)
with self.assertRaises(HTTPError) as ctx:
conn = Connection(self.host, self.port, version=API_VERSION_1, secure=True)
conn.request("GET", "materials")
self.assertIn("Error 418", str(ctx.exception))
self.assertIn("HTTP Error", str(ctx.exception))

Loading