Skip to content

Commit 96467ed

Browse files
Oscar Evertssonewanharris
authored andcommitted
feat: support for providing a client wide request timeout
1 parent 5e61ff0 commit 96467ed

7 files changed

Lines changed: 65 additions & 2 deletions

File tree

openfga_sdk/client/configuration.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def __init__(
3030
authorization_model_id=None,
3131
ssl_ca_cert=None,
3232
api_url=None, # TODO: restructure when removing api_scheme/api_host
33+
timeout_millisec: int | None = None,
3334
):
3435
super().__init__(
3536
api_scheme,
@@ -39,6 +40,7 @@ def __init__(
3940
retry_params,
4041
ssl_ca_cert=ssl_ca_cert,
4142
api_url=api_url,
43+
timeout_millisec=timeout_millisec,
4244
)
4345
self._authorization_model_id = authorization_model_id
4446

openfga_sdk/configuration.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ class Configuration:
169169
:param ssl_ca_cert: str - the path to a file of concatenated CA certificates
170170
in PEM format
171171
:param api_url: str - the URL of the FGA server
172+
:param timeout_millisec: int | None - the default timeout in milliseconds for requests
172173
"""
173174

174175
_default = None
@@ -206,6 +207,7 @@ def __init__(
206207
]
207208
| None
208209
) = None,
210+
timeout_millisec: int | None = None,
209211
):
210212
"""Constructor"""
211213
self._url = api_url
@@ -218,6 +220,8 @@ def __init__(
218220
else:
219221
# use the default parameters
220222
self._retry_params = RetryParams()
223+
224+
self._timeout_millisec = timeout_millisec or 5000 * 60
221225
"""Default Base url
222226
"""
223227
self.server_index = 0
@@ -647,6 +651,18 @@ def is_valid(self):
647651
if self._credentials is not None:
648652
self._credentials.validate_credentials_config()
649653

654+
if self._timeout_millisec is not None:
655+
if not isinstance(self._timeout_millisec, int):
656+
raise FgaValidationException(
657+
f"timeout_millisec unexpected type {self._timeout_millisec}"
658+
)
659+
660+
ten_minutes = 10000 * 60
661+
if self._timeout_millisec < 0 or self._timeout_millisec > ten_minutes:
662+
raise FgaValidationException(
663+
f"timeout_millisec not within reasonable range (0,60000), {self._timeout_millisec}"
664+
)
665+
650666
@property
651667
def api_scheme(self):
652668
"""Return connection is https or http."""
@@ -718,6 +734,20 @@ def retry_params(self, value):
718734
"""
719735
self._retry_params = value
720736

737+
@property
738+
def timeout_millisec(self):
739+
"""
740+
Return timeout milliseconds
741+
"""
742+
return self._timeout_millisec
743+
744+
@timeout_millisec.setter
745+
def timeout_millisec(self, value):
746+
"""
747+
Update timeout milliseconds
748+
"""
749+
self._timeout_millisec = value
750+
721751
@property
722752
def disabled_client_side_validations(self):
723753
"""Return disable_client_side_validations."""

openfga_sdk/rest.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None):
7272

7373
self.proxy = configuration.proxy
7474
self.proxy_headers = configuration.proxy_headers
75+
self._timeout_millisec = configuration.timeout_millisec
7576

7677
# https pool manager
7778
self.pool_manager = aiohttp.ClientSession(connector=connector, trust_env=True)
@@ -117,7 +118,7 @@ async def request(
117118

118119
post_params = post_params or {}
119120
headers = headers or {}
120-
timeout = _request_timeout or 5 * 60
121+
timeout = _request_timeout or self._timeout_millisec / 1000
121122

122123
if "Content-Type" not in headers:
123124
headers["Content-Type"] = "application/json"

openfga_sdk/sync/rest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def __init__(self, configuration, pools_size=4, maxsize=None):
8181
else:
8282
maxsize = 4
8383

84+
self._timeout_millisec = configuration.timeout_millisec
85+
8486
# https pool manager
8587
if configuration.proxy:
8688
self.pool_manager = urllib3.ProxyManager(
@@ -148,7 +150,7 @@ def request(
148150
post_params = post_params or {}
149151
headers = headers or {}
150152

151-
timeout = None
153+
timeout = urllib3.Timeout(total=self._timeout_millisec / 1000)
152154
if _request_timeout:
153155
if isinstance(_request_timeout, (float, int)):
154156
timeout = urllib3.Timeout(total=_request_timeout)

test/api/open_fga_api_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,17 @@ def test_url_with_scheme_and_host(self):
11481148
self.assertEqual(configuration.api_url, "http://localhost:8080")
11491149
configuration.is_valid() # Should not throw and complain about scheme being invalid
11501150

1151+
def test_timeout_millisec(self):
1152+
"""
1153+
Ensure that timeout_seconds is set and validated
1154+
"""
1155+
configuration = openfga_sdk.Configuration(
1156+
api_url="http://localhost:8080",
1157+
timeout_millisec=10000,
1158+
)
1159+
self.assertEqual(configuration.timeout_millisec, 10000)
1160+
configuration.is_valid()
1161+
11511162
async def test_bad_configuration_read_authorization_model(self):
11521163
"""
11531164
Test whether FgaValidationException is raised for API (reading authorization models)

test/configuration_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ def test_configuration_set_default(self, configuration):
136136
}
137137
default_config.ssl_ca_cert = "/path/to/ca_cert.pem"
138138
default_config.api_url = "https://fga.example/api"
139+
default_config.timeout_millisec = 10000
139140
Configuration.set_default(default_config)
140141

141142
assert Configuration._default.api_scheme == "https"
@@ -183,6 +184,7 @@ def test_configuration_set_default(self, configuration):
183184
}
184185
assert Configuration._default.ssl_ca_cert == "/path/to/ca_cert.pem"
185186
assert Configuration._default.api_url == "https://fga.example/api"
187+
assert Configuration._default.timeout_millisec == 10000
186188

187189
def test_configuration_get_default_copy(self, configuration):
188190
default_config = Configuration()
@@ -212,6 +214,7 @@ def test_configuration_get_default_copy(self, configuration):
212214
}
213215
default_config.ssl_ca_cert = "/path/to/ca_cert.pem"
214216
default_config.api_url = "https://fga.example/api"
217+
default_config.timeout_millisec = 10000
215218
Configuration.set_default(default_config)
216219

217220
copied_config = Configuration.get_default_copy()
@@ -229,6 +232,7 @@ def test_configuration_get_default_copy(self, configuration):
229232
assert copied_config.credentials._api_audience == "audience123"
230233
assert copied_config.credentials._api_issuer == "issuer123"
231234
assert copied_config.credentials._api_token == "token123"
235+
assert Configuration._default.timeout_millisec == 10000
232236

233237

234238
class TestConfigurationValidityChecks:
@@ -361,6 +365,7 @@ def test_configuration_deepcopy(self, configuration):
361365
},
362366
ssl_ca_cert="/path/to/ca_cert.pem",
363367
api_url="https://fga.example/api",
368+
timeout_millisec=10000,
364369
)
365370

366371
# Perform deep copy
@@ -394,3 +399,4 @@ def test_configuration_deepcopy(self, configuration):
394399
)
395400
assert copied_config.ssl_ca_cert == config.ssl_ca_cert
396401
assert copied_config.api_url == config.api_url
402+
assert copied_config.timeout_millisec == config.timeout_millisec

test/sync/open_fga_api_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,17 @@ def test_url_with_scheme_and_host(self):
11481148
self.assertEqual(configuration.api_url, "http://localhost:8080")
11491149
configuration.is_valid() # Should not throw and complain about scheme being invalid
11501150

1151+
def test_timeout_millisec(self):
1152+
"""
1153+
Ensure that timeout_millisec is set and validated
1154+
"""
1155+
configuration = Configuration(
1156+
api_url="http://localhost:8080",
1157+
timeout_millisec=10000,
1158+
)
1159+
self.assertEqual(configuration.timeout_millisec, 10000)
1160+
configuration.is_valid()
1161+
11511162
async def test_bad_configuration_read_authorization_model(self):
11521163
"""
11531164
Test whether FgaValidationException is raised for API (reading authorization models)

0 commit comments

Comments
 (0)