From 53d07810ccad959a3d110f622c8c46316a374a01 Mon Sep 17 00:00:00 2001 From: Vojta Valter Date: Fri, 17 Jan 2025 15:52:56 +0100 Subject: [PATCH] feat: Add more information to APIClientError --- rossum_api/api_client.py | 18 +++++++++++------- tests/test_api_client.py | 10 ++++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/rossum_api/api_client.py b/rossum_api/api_client.py index 3db1d6e..67fc776 100644 --- a/rossum_api/api_client.py +++ b/rossum_api/api_client.py @@ -30,12 +30,14 @@ class APIClientError(Exception): - def __init__(self, status_code, error): + def __init__(self, method, url, status_code, error): + self.method = method + self.url = url self.status_code = status_code self.error = error def __str__(self): - return f"HTTP {self.status_code}, content: {self.error}" + return f"[{self.method}] {self.url} - HTTP {self.status_code} - {self.error}" def authenticate_if_needed(method): @@ -375,7 +377,7 @@ async def _authenticate(self) -> None: build_full_login_url(self.base_url), data={"username": self.username, "password": self.password}, ) - await self._raise_for_status(response) + await self._raise_for_status(response, "POST") self.token = response.json()["key"] def _retrying(self): @@ -415,7 +417,7 @@ async def _request(self, method: str, url: str, *args, **kwargs) -> httpx.Respon async for attempt in self._retrying(): with attempt: response = await self.client.request(method, url, headers=headers, *args, **kwargs) - await self._raise_for_status(response) + await self._raise_for_status(response, method) return response @authenticate_generator_if_needed @@ -427,11 +429,11 @@ async def _stream(self, method: str, url: str, *args, **kwargs) -> AsyncIterator headers = kwargs.pop("headers", {}) headers["Authorization"] = f"token {self.token}" async with self.client.stream(method, url, headers=headers, *args, **kwargs) as response: - await self._raise_for_status(response) + await self._raise_for_status(response, method) async for chunk in response.aiter_bytes(): yield chunk - async def _raise_for_status(self, response: httpx.Response): + async def _raise_for_status(self, response: httpx.Response, method: str) -> None: """Raise an exception in case of HTTP error. Re-pack to our own exception class to shield users from the fact that we're using @@ -441,4 +443,6 @@ async def _raise_for_status(self, response: httpx.Response): response.raise_for_status() except httpx.HTTPStatusError as e: content = response.content if response.stream is None else await response.aread() - raise APIClientError(response.status_code, content.decode("utf-8")) from e + raise APIClientError( + method, response.url, response.status_code, content.decode("utf-8") + ) from e diff --git a/tests/test_api_client.py b/tests/test_api_client.py index ed54581..0e5511a 100644 --- a/tests/test_api_client.py +++ b/tests/test_api_client.py @@ -699,7 +699,10 @@ async def test_request_repacks_exception(client, httpx_mock): ) with pytest.raises(APIClientError) as err: await client._request("GET", "workspaces/123") - assert str(err.value) == 'HTTP 404, content: {"detail":"Not found."}' + assert str(err.value) == ( + "[GET] https://elis.rossum.ai/api/v1/workspaces/123 - " + 'HTTP 404 - {"detail":"Not found."}' + ) @pytest.mark.asyncio @@ -713,7 +716,10 @@ async def test_stream_repacks_exception(client, httpx_mock): with pytest.raises(APIClientError) as err: async for _w in client._stream("GET", "queues/123/export?format=csv&exported_at=invalid"): pass - assert str(err.value) == "HTTP 404, content: exported_at: Enter a valid date/time" + assert str(err.value) == ( + "[GET] https://elis.rossum.ai/api/v1/queues/123/export?format=csv&exported_at=invalid " + "- HTTP 404 - exported_at: Enter a valid date/time" + ) @pytest.mark.asyncio