Skip to content

Commit 5578ea2

Browse files
grdsdevclaude
andcommitted
feat: add region as forceFunctionRegion query parameter
- Updated FunctionsClient (both async and sync) to add region as both x-region header and forceFunctionRegion query param - Used urllib.parse.urlencode to properly construct query parameters - Added comprehensive tests to verify both header and query parameter functionality - Updated existing tests to check for both region mechanisms - Maintains backward compatibility with existing x-region header Ported from supabase/functions-js#100 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 82f60b9 commit 5578ea2

File tree

4 files changed

+28
-6
lines changed

4 files changed

+28
-6
lines changed

src/functions/src/supabase_functions/_async/functions_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Any, Dict, Literal, Optional, Union
2+
from urllib.parse import urlencode
23
from warnings import warn
34

45
from httpx import AsyncClient, HTTPError, Response
@@ -123,6 +124,8 @@ async def invoke(
123124
headers = self.headers
124125
body = None
125126
response_type = "text/plain"
127+
url = f"{self.url}/{function_name}"
128+
126129
if invoke_options is not None:
127130
headers.update(invoke_options.get("headers", {}))
128131
response_type = invoke_options.get("responseType", "text/plain")
@@ -135,6 +138,8 @@ async def invoke(
135138

136139
if region.value != "any":
137140
headers["x-region"] = region.value
141+
# Add region as query parameter
142+
url = f"{url}?{urlencode({'forceFunctionRegion': region.value})}"
138143

139144
body = invoke_options.get("body")
140145
if isinstance(body, str):
@@ -143,7 +148,7 @@ async def invoke(
143148
headers["Content-Type"] = "application/json"
144149

145150
response = await self._request(
146-
"POST", f"{self.url}/{function_name}", headers=headers, json=body
151+
"POST", url, headers=headers, json=body
147152
)
148153
is_relay_error = response.headers.get("x-relay-header")
149154

src/functions/src/supabase_functions/_sync/functions_client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import Any, Dict, Literal, Optional, Union
2+
from urllib.parse import urlencode
23
from warnings import warn
34

45
from httpx import Client, HTTPError, Response
@@ -123,6 +124,8 @@ def invoke(
123124
headers = self.headers
124125
body = None
125126
response_type = "text/plain"
127+
url = f"{self.url}/{function_name}"
128+
126129
if invoke_options is not None:
127130
headers.update(invoke_options.get("headers", {}))
128131
response_type = invoke_options.get("responseType", "text/plain")
@@ -135,6 +138,8 @@ def invoke(
135138

136139
if region.value != "any":
137140
headers["x-region"] = region.value
141+
# Add region as query parameter
142+
url = f"{url}?{urlencode({'forceFunctionRegion': region.value})}"
138143

139144
body = invoke_options.get("body")
140145
if isinstance(body, str):
@@ -143,7 +148,7 @@ def invoke(
143148
headers["Content-Type"] = "application/json"
144149

145150
response = self._request(
146-
"POST", f"{self.url}/{function_name}", headers=headers, json=body
151+
"POST", url, headers=headers, json=body
147152
)
148153
is_relay_error = response.headers.get("x-relay-header")
149154

src/functions/tests/_async/test_function_client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,11 @@ async def test_invoke_with_region(client: AsyncFunctionsClient):
100100

101101
await client.invoke("test-function", {"region": FunctionRegion("us-east-1")})
102102

103-
_, kwargs = mock_request.call_args
103+
args, kwargs = mock_request.call_args
104+
# Check that x-region header is present
104105
assert kwargs["headers"]["x-region"] == "us-east-1"
106+
# Check that the URL contains the forceFunctionRegion query parameter
107+
assert "forceFunctionRegion=us-east-1" in args[1]
105108

106109

107110
async def test_invoke_with_region_string(client: AsyncFunctionsClient):
@@ -118,8 +121,11 @@ async def test_invoke_with_region_string(client: AsyncFunctionsClient):
118121
with pytest.warns(UserWarning, match=r"Use FunctionRegion\(us-east-1\)"):
119122
await client.invoke("test-function", {"region": "us-east-1"})
120123

121-
_, kwargs = mock_request.call_args
124+
args, kwargs = mock_request.call_args
125+
# Check that x-region header is present
122126
assert kwargs["headers"]["x-region"] == "us-east-1"
127+
# Check that the URL contains the forceFunctionRegion query parameter
128+
assert "forceFunctionRegion=us-east-1" in args[1]
123129

124130

125131
async def test_invoke_with_http_error(client: AsyncFunctionsClient):

src/functions/tests/_sync/test_function_client.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,11 @@ def test_invoke_with_region(client: SyncFunctionsClient):
9494

9595
client.invoke("test-function", {"region": FunctionRegion("us-east-1")})
9696

97-
_, kwargs = mock_request.call_args
97+
args, kwargs = mock_request.call_args
98+
# Check that x-region header is present
9899
assert kwargs["headers"]["x-region"] == "us-east-1"
100+
# Check that the URL contains the forceFunctionRegion query parameter
101+
assert "forceFunctionRegion=us-east-1" in args[1]
99102

100103

101104
def test_invoke_with_region_string(client: SyncFunctionsClient):
@@ -110,8 +113,11 @@ def test_invoke_with_region_string(client: SyncFunctionsClient):
110113
with pytest.warns(UserWarning, match=r"Use FunctionRegion\(us-east-1\)"):
111114
client.invoke("test-function", {"region": "us-east-1"})
112115

113-
_, kwargs = mock_request.call_args
116+
args, kwargs = mock_request.call_args
117+
# Check that x-region header is present
114118
assert kwargs["headers"]["x-region"] == "us-east-1"
119+
# Check that the URL contains the forceFunctionRegion query parameter
120+
assert "forceFunctionRegion=us-east-1" in args[1]
115121

116122

117123
def test_invoke_with_http_error(client: SyncFunctionsClient):

0 commit comments

Comments
 (0)