diff --git a/notion_client/client.py b/notion_client/client.py index 1677463f..e3e2d873 100644 --- a/notion_client/client.py +++ b/notion_client/client.py @@ -153,10 +153,20 @@ def _parse_response(self, response: Response) -> Any: try: body = error.response.json() code = body.get("code") + additional_data = body.get("additional_data") + request_id = body.get("request_id") except json.JSONDecodeError: code = None + additional_data = None + request_id = None if code and is_api_error_code(code): - raise APIResponseError(response, body["message"], code) + raise APIResponseError( + response, + body["message"], + code, + additional_data, + request_id, + ) raise HTTPResponseError(error.response) body = response.json() diff --git a/notion_client/errors.py b/notion_client/errors.py index 516d32f6..5b6168b6 100644 --- a/notion_client/errors.py +++ b/notion_client/errors.py @@ -4,7 +4,7 @@ """ from enum import Enum -from typing import Optional +from typing import Any, Dict, Optional import httpx @@ -95,12 +95,21 @@ class APIResponseError(HTTPResponseError): """An error raised by Notion API.""" code: APIErrorCode + additional_data: Optional[Dict[str, Any]] + request_id: Optional[str] def __init__( - self, response: httpx.Response, message: str, code: APIErrorCode + self, + response: httpx.Response, + message: str, + code: APIErrorCode, + additional_data: Optional[Dict[str, Any]] = None, + request_id: Optional[str] = None, ) -> None: super().__init__(response, message) self.code = code + self.additional_data = additional_data + self.request_id = request_id def is_api_error_code(code: str) -> bool: diff --git a/tests/test_errors.py b/tests/test_errors.py index f94d4eed..b2fc84c0 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -59,6 +59,44 @@ async def test_api_async_request_bad_request_error(async_client): await async_client.request(STATUS_PAGE_BAD_REQUEST, "GET") +@pytest.mark.vcr() +def test_api_response_error_request_id(client): + with pytest.raises(APIResponseError) as exc_info: + client.request("/invalid", "GET") + + error = exc_info.value + assert isinstance(error.request_id, str) + + +@pytest.mark.vcr() +async def test_async_api_response_error_request_id(async_client): + with pytest.raises(APIResponseError) as exc_info: + await async_client.request("/invalid", "GET") + + error = exc_info.value + assert isinstance(error.request_id, str) + + +@pytest.mark.vcr() +def test_api_response_error_additional_data(client): + with pytest.raises(APIResponseError) as exc_info: + client.request("/users", "GET", auth="invalid-token") + + error = exc_info.value + if error.additional_data is not None: + assert isinstance(error.additional_data, dict) + + +@pytest.mark.vcr() +async def test_async_api_response_error_additional_data(async_client): + with pytest.raises(APIResponseError) as exc_info: + await async_client.request("/users", "GET", auth="invalid-token") + + error = exc_info.value + if error.additional_data is not None: + assert isinstance(error.additional_data, dict) + + async def test_is_api_error_code(): error_code = "unauthorized" assert is_api_error_code(error_code)