Skip to content

Commit

Permalink
feat: Add more information to APIClientError
Browse files Browse the repository at this point in the history
  • Loading branch information
Vojta Valter committed Jan 17, 2025
1 parent 8a94d6f commit 53d0781
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 9 deletions.
18 changes: 11 additions & 7 deletions rossum_api/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
10 changes: 8 additions & 2 deletions tests/test_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 53d0781

Please sign in to comment.