diff --git a/src/postgrest/src/postgrest/_async/request_builder.py b/src/postgrest/src/postgrest/_async/request_builder.py index 3c996922..66749e99 100644 --- a/src/postgrest/src/postgrest/_async/request_builder.py +++ b/src/postgrest/src/postgrest/_async/request_builder.py @@ -104,23 +104,20 @@ class AsyncMaybeSingleRequestBuilder: def __init__(self, request: ReqConfig): self.request = request + @staticmethod + def _is_zero_rows_error(error: APIError) -> bool: + return any( + field is not None and "0 rows" in field + for field in (error.details, error.message, error.hint) + ) + async def execute(self) -> Optional[SingleAPIResponse]: - r = None try: - r = await AsyncSingleRequestBuilder(self.request).execute() - except APIError as e: - if e.details and "The result contains 0 rows" in e.details: + return await AsyncSingleRequestBuilder(self.request).execute() + except APIError as error: + if self._is_zero_rows_error(error): return None - if not r: - raise APIError( - { - "message": "Missing response", - "code": "204", - "hint": "Please check traceback of the code", - "details": "Postgrest couldn't retrieve response, please check traceback of the code. Please create an issue in `supabase-community/postgrest-py` if needed.", - } - ) - return r + raise class AsyncFilterRequestBuilder( diff --git a/src/postgrest/src/postgrest/_sync/request_builder.py b/src/postgrest/src/postgrest/_sync/request_builder.py index a5340403..733b55ad 100644 --- a/src/postgrest/src/postgrest/_sync/request_builder.py +++ b/src/postgrest/src/postgrest/_sync/request_builder.py @@ -104,23 +104,20 @@ class SyncMaybeSingleRequestBuilder: def __init__(self, request: ReqConfig): self.request = request + @staticmethod + def _is_zero_rows_error(error: APIError) -> bool: + return any( + field is not None and "0 rows" in field + for field in (error.details, error.message, error.hint) + ) + def execute(self) -> Optional[SingleAPIResponse]: - r = None try: - r = SyncSingleRequestBuilder(self.request).execute() - except APIError as e: - if e.details and "The result contains 0 rows" in e.details: + return SyncSingleRequestBuilder(self.request).execute() + except APIError as error: + if self._is_zero_rows_error(error): return None - if not r: - raise APIError( - { - "message": "Missing response", - "code": "204", - "hint": "Please check traceback of the code", - "details": "Postgrest couldn't retrieve response, please check traceback of the code. Please create an issue in `supabase-community/postgrest-py` if needed.", - } - ) - return r + raise class SyncFilterRequestBuilder( diff --git a/src/postgrest/tests/_async/test_client.py b/src/postgrest/tests/_async/test_client.py index 18a95627..757fbb2c 100644 --- a/src/postgrest/tests/_async/test_client.py +++ b/src/postgrest/tests/_async/test_client.py @@ -141,11 +141,18 @@ async def test_response_status_code_outside_ok(postgrest_client: AsyncPostgrestC @pytest.mark.asyncio -async def test_response_maybe_single(postgrest_client: AsyncPostgrestClient): +async def test_response_maybe_single_returns_none_on_zero_rows( + postgrest_client: AsyncPostgrestClient, +): with patch( "postgrest._async.request_builder.AsyncSingleRequestBuilder.execute", side_effect=APIError( - {"message": "mock error", "code": "400", "hint": "mock", "details": "mock"} + { + "message": "JSON object requested, multiple (or no) rows returned", + "code": "PGRST116", + "hint": None, + "details": "The result contains 0 rows", + } ), ): client = ( @@ -155,12 +162,31 @@ async def test_response_maybe_single(postgrest_client: AsyncPostgrestClient): assert ( client.request.headers.get("Accept") == "application/vnd.pgrst.object+json" ) + assert await client.execute() is None + + +@pytest.mark.asyncio +async def test_response_maybe_single_raises_non_zero_rows_error( + postgrest_client: AsyncPostgrestClient, +): + with patch( + "postgrest._async.request_builder.AsyncSingleRequestBuilder.execute", + side_effect=APIError( + { + "message": "JSON object requested, multiple (or no) rows returned", + "code": "PGRST116", + "hint": None, + "details": "The result contains 2 rows", + } + ), + ): + client = ( + postgrest_client.from_("test").select("a", "b").eq("c", "d").maybe_single() + ) with pytest.raises(APIError) as exc_info: await client.execute() - assert isinstance(exc_info, pytest.ExceptionInfo) - exc_response = exc_info.value.json() - assert isinstance(exc_response.get("message"), str) - assert "code" in exc_response and int(exc_response["code"]) == 204 + assert exc_info.value.code == "PGRST116" + assert exc_info.value.details == "The result contains 2 rows" # https://github.com/supabase/postgrest-py/issues/595 diff --git a/src/postgrest/tests/_sync/test_client.py b/src/postgrest/tests/_sync/test_client.py index 7f7c7610..382e13f6 100644 --- a/src/postgrest/tests/_sync/test_client.py +++ b/src/postgrest/tests/_sync/test_client.py @@ -136,11 +136,18 @@ def test_response_status_code_outside_ok(postgrest_client: SyncPostgrestClient): assert exc_response["errors"][0].get("code") == 400 -def test_response_maybe_single(postgrest_client: SyncPostgrestClient): +def test_response_maybe_single_returns_none_on_zero_rows( + postgrest_client: SyncPostgrestClient, +): with patch( "postgrest._sync.request_builder.SyncSingleRequestBuilder.execute", side_effect=APIError( - {"message": "mock error", "code": "400", "hint": "mock", "details": "mock"} + { + "message": "JSON object requested, multiple (or no) rows returned", + "code": "PGRST116", + "hint": None, + "details": "The result contains 0 rows", + } ), ): client = ( @@ -150,12 +157,30 @@ def test_response_maybe_single(postgrest_client: SyncPostgrestClient): assert ( client.request.headers.get("Accept") == "application/vnd.pgrst.object+json" ) + assert client.execute() is None + + +def test_response_maybe_single_raises_non_zero_rows_error( + postgrest_client: SyncPostgrestClient, +): + with patch( + "postgrest._sync.request_builder.SyncSingleRequestBuilder.execute", + side_effect=APIError( + { + "message": "JSON object requested, multiple (or no) rows returned", + "code": "PGRST116", + "hint": None, + "details": "The result contains 2 rows", + } + ), + ): + client = ( + postgrest_client.from_("test").select("a", "b").eq("c", "d").maybe_single() + ) with pytest.raises(APIError) as exc_info: client.execute() - assert isinstance(exc_info, pytest.ExceptionInfo) - exc_response = exc_info.value.json() - assert isinstance(exc_response.get("message"), str) - assert "code" in exc_response and int(exc_response["code"]) == 204 + assert exc_info.value.code == "PGRST116" + assert exc_info.value.details == "The result contains 2 rows" # https://github.com/supabase/postgrest-py/issues/595