Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ async def _request(
method, str(url), json=json, headers=headers, params=params
)
)
# Relay failures set this header even on non-2xx responses.
if response.headers.get("x-relay-error") == "true":
raise FunctionsRelayError(
response.json().get("error")
or "Relay Error invoking the Edge Function",
response.status_code,
)
try:
response.raise_for_status()
except HTTPError as exc:
Expand Down Expand Up @@ -165,10 +172,6 @@ async def invoke(
response = await self._request(
"POST", [function_name], headers=headers, json=body, params=params
)
is_relay_error = response.headers.get("x-relay-header")

if is_relay_error and is_relay_error == "true":
raise FunctionsRelayError(response.json().get("error"))

if response_type == "json":
data = response.json()
Expand Down
11 changes: 7 additions & 4 deletions src/functions/src/supabase_functions/_sync/functions_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ def _request(
method, str(url), json=json, headers=headers, params=params
)
)
# Relay failures set this header even on non-2xx responses.
if response.headers.get("x-relay-error") == "true":
raise FunctionsRelayError(
response.json().get("error")
or "Relay Error invoking the Edge Function",
response.status_code,
)
try:
response.raise_for_status()
except HTTPError as exc:
Expand Down Expand Up @@ -165,10 +172,6 @@ def invoke(
response = self._request(
"POST", [function_name], headers=headers, json=body, params=params
)
is_relay_error = response.headers.get("x-relay-header")

if is_relay_error and is_relay_error == "true":
raise FunctionsRelayError(response.json().get("error"))

if response_type == "json":
data = response.json()
Expand Down
26 changes: 25 additions & 1 deletion src/functions/tests/_async/test_function_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,9 @@ async def test_invoke_with_http_error(client: AsyncFunctionsClient) -> None:
async def test_invoke_with_relay_error(client: AsyncFunctionsClient) -> None:
mock_response = Mock(spec=Response)
mock_response.json.return_value = {"error": "Relay error message"}
mock_response.status_code = 200
mock_response.raise_for_status = Mock()
mock_response.headers = {"x-relay-header": "true"}
mock_response.headers = {"x-relay-error": "true"}

with patch.object(
client._client, "request", new_callable=AsyncMock
Expand All @@ -167,6 +168,29 @@ async def test_invoke_with_relay_error(client: AsyncFunctionsClient) -> None:
await client.invoke("test-function")


async def test_invoke_relay_error_on_non_2xx_status(
client: AsyncFunctionsClient,
) -> None:
mock_response = Mock(spec=Response)
mock_response.json.return_value = {"error": "Relay error message"}
mock_response.status_code = 546
mock_response.raise_for_status.side_effect = HTTPError("HTTP Error")
mock_response.headers = {"x-relay-error": "true"}

with patch.object(
client._client, "request", new_callable=AsyncMock
) as mock_request:
mock_request.return_value = mock_response

with pytest.raises(
FunctionsRelayError, match="Relay error message"
) as exc_info:
await client.invoke("test-function")

assert not isinstance(exc_info.value, FunctionsHttpError)
assert exc_info.value.status == 546


async def test_invoke_invalid_function_name(client: AsyncFunctionsClient) -> None:
with pytest.raises(ValueError, match="function_name must a valid string value."):
await client.invoke("")
Expand Down
22 changes: 21 additions & 1 deletion src/functions/tests/_sync/test_function_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,9 @@ def test_invoke_with_http_error(client: SyncFunctionsClient) -> None:
def test_invoke_with_relay_error(client: SyncFunctionsClient) -> None:
mock_response = Mock(spec=Response)
mock_response.json.return_value = {"error": "Relay error message"}
mock_response.status_code = 200
mock_response.raise_for_status = Mock()
mock_response.headers = {"x-relay-header": "true"}
mock_response.headers = {"x-relay-error": "true"}

with patch.object(client._client, "request", new_callable=Mock) as mock_request:
mock_request.return_value = mock_response
Expand All @@ -155,6 +156,25 @@ def test_invoke_with_relay_error(client: SyncFunctionsClient) -> None:
client.invoke("test-function")


def test_invoke_relay_error_on_non_2xx_status(client: SyncFunctionsClient) -> None:
mock_response = Mock(spec=Response)
mock_response.json.return_value = {"error": "Relay error message"}
mock_response.status_code = 546
mock_response.raise_for_status.side_effect = HTTPError("HTTP Error")
mock_response.headers = {"x-relay-error": "true"}

with patch.object(client._client, "request", new_callable=Mock) as mock_request:
mock_request.return_value = mock_response

with pytest.raises(
FunctionsRelayError, match="Relay error message"
) as exc_info:
client.invoke("test-function")

assert not isinstance(exc_info.value, FunctionsHttpError)
assert exc_info.value.status == 546


def test_invoke_invalid_function_name(client: SyncFunctionsClient) -> None:
with pytest.raises(ValueError, match="function_name must a valid string value."):
client.invoke("")
Expand Down
Loading