Skip to content

Commit 8b4b56c

Browse files
grdsdevclaudeo-santi
authored
feat: add region as forceFunctionRegion query parameter (#1236)
Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Leonardo Santiago <leonardo.ribeiro.santiago@gmail.com>
1 parent 82f60b9 commit 8b4b56c

File tree

4 files changed

+42
-14
lines changed

4 files changed

+42
-14
lines changed

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Any, Dict, Literal, Optional, Union
22
from warnings import warn
33

4-
from httpx import AsyncClient, HTTPError, Response
4+
from httpx import AsyncClient, HTTPError, Response, QueryParams
55

66
from ..errors import FunctionsHttpError, FunctionsRelayError
77
from ..utils import (
@@ -73,11 +73,16 @@ async def _request(
7373
url: str,
7474
headers: Optional[Dict[str, str]] = None,
7575
json: Optional[Dict[Any, Any]] = None,
76+
params: Optional[QueryParams] = None,
7677
) -> Response:
7778
response = (
78-
await self._client.request(method, url, data=json, headers=headers)
79+
await self._client.request(
80+
method, url, data=json, headers=headers, params=params
81+
)
7982
if isinstance(json, str)
80-
else await self._client.request(method, url, json=json, headers=headers)
83+
else await self._client.request(
84+
method, url, json=json, headers=headers, params=params
85+
)
8186
)
8287
try:
8388
response.raise_for_status()
@@ -121,8 +126,11 @@ async def invoke(
121126
if not is_valid_str_arg(function_name):
122127
raise ValueError("function_name must a valid string value.")
123128
headers = self.headers
129+
params = QueryParams()
124130
body = None
125131
response_type = "text/plain"
132+
url = f"{self.url}/{function_name}"
133+
126134
if invoke_options is not None:
127135
headers.update(invoke_options.get("headers", {}))
128136
response_type = invoke_options.get("responseType", "text/plain")
@@ -135,6 +143,8 @@ async def invoke(
135143

136144
if region.value != "any":
137145
headers["x-region"] = region.value
146+
# Add region as query parameter
147+
params = params.set("forceFunctionRegion", region.value)
138148

139149
body = invoke_options.get("body")
140150
if isinstance(body, str):
@@ -143,7 +153,7 @@ async def invoke(
143153
headers["Content-Type"] = "application/json"
144154

145155
response = await self._request(
146-
"POST", f"{self.url}/{function_name}", headers=headers, json=body
156+
"POST", url, headers=headers, json=body, params=params
147157
)
148158
is_relay_error = response.headers.get("x-relay-header")
149159

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import Any, Dict, Literal, Optional, Union
22
from warnings import warn
33

4-
from httpx import Client, HTTPError, Response
4+
from httpx import Client, HTTPError, Response, QueryParams
55

66
from ..errors import FunctionsHttpError, FunctionsRelayError
77
from ..utils import (
@@ -73,11 +73,14 @@ def _request(
7373
url: str,
7474
headers: Optional[Dict[str, str]] = None,
7575
json: Optional[Dict[Any, Any]] = None,
76+
params: Optional[QueryParams] = None,
7677
) -> Response:
7778
response = (
78-
self._client.request(method, url, data=json, headers=headers)
79+
self._client.request(method, url, data=json, headers=headers, params=params)
7980
if isinstance(json, str)
80-
else self._client.request(method, url, json=json, headers=headers)
81+
else self._client.request(
82+
method, url, json=json, headers=headers, params=params
83+
)
8184
)
8285
try:
8386
response.raise_for_status()
@@ -121,8 +124,11 @@ def invoke(
121124
if not is_valid_str_arg(function_name):
122125
raise ValueError("function_name must a valid string value.")
123126
headers = self.headers
127+
params = QueryParams()
124128
body = None
125129
response_type = "text/plain"
130+
url = f"{self.url}/{function_name}"
131+
126132
if invoke_options is not None:
127133
headers.update(invoke_options.get("headers", {}))
128134
response_type = invoke_options.get("responseType", "text/plain")
@@ -135,16 +141,16 @@ def invoke(
135141

136142
if region.value != "any":
137143
headers["x-region"] = region.value
144+
# Add region as query parameter
145+
params = params.set("forceFunctionRegion", region.value)
138146

139147
body = invoke_options.get("body")
140148
if isinstance(body, str):
141149
headers["Content-Type"] = "text/plain"
142150
elif isinstance(body, dict):
143151
headers["Content-Type"] = "application/json"
144152

145-
response = self._request(
146-
"POST", f"{self.url}/{function_name}", headers=headers, json=body
147-
)
153+
response = self._request("POST", url, headers=headers, json=body, params=params)
148154
is_relay_error = response.headers.get("x-relay-header")
149155

150156
if is_relay_error and is_relay_error == "true":

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 kwargs["params"]["forceFunctionRegion"] == "us-east-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 kwargs["params"]["forceFunctionRegion"] == "us-east-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 kwargs["params"]["forceFunctionRegion"] == "us-east-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 kwargs["params"]["forceFunctionRegion"] == "us-east-1"
115121

116122

117123
def test_invoke_with_http_error(client: SyncFunctionsClient):

0 commit comments

Comments
 (0)