Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(python-sdk): support for providing a client wide request timeout #403

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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 @@ -19,8 +19,9 @@ class ClientConfiguration(Configuration):
authorization_model_id=None,
ssl_ca_cert=None,
api_url=None, # TODO: restructure when removing api_scheme/api_host
timeout_millisec: int | None = None,
):
super().__init__(api_scheme, api_host, store_id, credentials, retry_params, ssl_ca_cert=ssl_ca_cert, api_url=api_url)
super().__init__(api_scheme, api_host, store_id, credentials, retry_params, ssl_ca_cert=ssl_ca_cert, api_url=api_url, timeout_millisec=timeout_millisec)
self._authorization_model_id = authorization_model_id

def is_valid(self):
Expand Down
32 changes: 32 additions & 0 deletions config/clients/python/template/src/configuration.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class Configuration:
:param ssl_ca_cert: str - the path to a file of concatenated CA certificates
in PEM format
:param api_url: str - the URL of the FGA server
:param timeout_millis: int | None - the default timeout in milliseconds for requests
"""

_default = None
Expand All @@ -163,7 +164,12 @@ class Configuration:
server_operation_index=None, server_operation_variables=None,
ssl_ca_cert=None,
api_url=None, # TODO: restructure when removing api_scheme/api_host


timeout_millisec: int | None = None,

telemetry: dict[TelemetryConfigurationType | str, TelemetryMetricsConfiguration | dict[TelemetryHistogram | TelemetryCounter, TelemetryMetricConfiguration | dict[TelemetryAttribute, bool] | None] | None] | None = None):

"""Constructor
"""
self._url = api_url
Expand All @@ -176,6 +182,8 @@ class Configuration:
else:
# use the default parameters
self._retry_params = RetryParams()

self._timeout_millisec = timeout_millisec or 5000 * 60
"""Default Base url
"""
self.server_index = 0
Expand Down Expand Up @@ -624,6 +632,16 @@ class Configuration:
if self._credentials is not None:
self._credentials.validate_credentials_config()

if self._timeout_millisec is not None:
if not isinstance(self._timeout_millisec, int):
raise FgaValidationException(f"timeout_millisec unexpected type {self._timeout_millisec}")

ten_minutes = 10000 * 60
if self._timeout_millisec < 0 or self._timeout_millisec > ten_minutes:
raise FgaValidationException(f"timeout_millisec not within reasonable range (0,60000), {self._timeout_millisec}")



@property
def api_scheme(self):
"""Return connection is https or http."""
Expand Down Expand Up @@ -695,6 +713,20 @@ class Configuration:
"""
self._retry_params = value

@property
def timeout_millisec(self):
"""
Return timeout milliseconds
"""
return self._timeout_millisec

@timeout_millisec.setter
def timeout_millisec(self, value):
"""
Update timeout milliseconds
"""
self._timeout_millisec = value

@property
def disabled_client_side_validations(self):
"""Return disable_client_side_validations."""
Expand Down
3 changes: 2 additions & 1 deletion config/clients/python/template/src/rest.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class RESTClientObject:

self.proxy = configuration.proxy
self.proxy_headers = configuration.proxy_headers
self._timeout_millisec = configuration.timeout_millisec

# https pool manager
self.pool_manager = aiohttp.ClientSession(
Expand Down Expand Up @@ -96,7 +97,7 @@ class RESTClientObject:

post_params = post_params or {}
headers = headers or {}
timeout = _request_timeout or 5 * 60
timeout = _request_timeout or self._timeout_millisec / 1000

if 'Content-Type' not in headers:
headers['Content-Type'] = 'application/json'
Expand Down
4 changes: 3 additions & 1 deletion config/clients/python/template/src/sync/rest.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class RESTClientObject:
else:
maxsize = 4

self._timeout_millisec = configuration.timeout_millisec

# https pool manager
if configuration.proxy:
self.pool_manager = urllib3.ProxyManager(
Expand Down Expand Up @@ -122,7 +124,7 @@ class RESTClientObject:
post_params = post_params or {}
headers = headers or {}

timeout = None
timeout = urllib3.Timeout(total=self._timeout_millisec / 1000)
if _request_timeout:
if isinstance(_request_timeout, (float, int)):
timeout = urllib3.Timeout(total=_request_timeout)
Expand Down
11 changes: 11 additions & 0 deletions config/clients/python/template/test/api_test.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,17 @@ class {{#operations}}Test{{classname}}(IsolatedAsyncioTestCase):
self.assertEqual(configuration.api_url, 'http://localhost:8080')
configuration.is_valid() # Should not throw and complain about scheme being invalid

def test_timeout_millisec(self):
"""
Ensure that timeout_seconds is set and validated
"""
configuration = {{packageName}}.Configuration(
api_url='http://localhost:8080',
timeout_millisec=10000,
)
self.assertEqual(configuration.timeout_millisec, 10000)
configuration.is_valid()

async def test_bad_configuration_read_authorization_model(self):
"""
Test whether FgaValidationException is raised for API (reading authorization models)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class TestConfigurationSetDefaultAndGetDefaultCopy:
}
default_config.ssl_ca_cert = "/path/to/ca_cert.pem"
default_config.api_url = "https://{{sampleApiDomain}}/api"
default_config.timeout_millisec = 10000
Configuration.set_default(default_config)

assert Configuration._default.api_scheme == "https"
Expand Down Expand Up @@ -172,6 +173,7 @@ class TestConfigurationSetDefaultAndGetDefaultCopy:
}
assert Configuration._default.ssl_ca_cert == "/path/to/ca_cert.pem"
assert Configuration._default.api_url == "https://{{sampleApiDomain}}/api"
assert Configuration._default.timeout_millisec == 10000

def test_configuration_get_default_copy(self, configuration):
default_config = Configuration()
Expand Down Expand Up @@ -201,6 +203,7 @@ class TestConfigurationSetDefaultAndGetDefaultCopy:
}
default_config.ssl_ca_cert = "/path/to/ca_cert.pem"
default_config.api_url = "https://{{sampleApiDomain}}/api"
default_config.timeout_millisec = 10000
Configuration.set_default(default_config)

copied_config = Configuration.get_default_copy()
Expand All @@ -218,6 +221,7 @@ class TestConfigurationSetDefaultAndGetDefaultCopy:
assert copied_config.credentials._api_audience == "audience123"
assert copied_config.credentials._api_issuer == "issuer123"
assert copied_config.credentials._api_token == "token123"
assert Configuration._default.timeout_millisec == 10000


class TestConfigurationValidityChecks:
Expand Down Expand Up @@ -350,6 +354,7 @@ class TestConfigurationMiscellaneous:
},
ssl_ca_cert="/path/to/ca_cert.pem",
api_url="https://{{sampleApiDomain}}/api",
timeout_millisec=10000,
)

# Perform deep copy
Expand Down Expand Up @@ -383,3 +388,4 @@ class TestConfigurationMiscellaneous:
)
assert copied_config.ssl_ca_cert == config.ssl_ca_cert
assert copied_config.api_url == config.api_url
assert copied_config.timeout_millisec == config.timeout_millisec
11 changes: 11 additions & 0 deletions config/clients/python/template/test/sync/api_test.py.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,17 @@ class TestOpenFgaApiSync(IsolatedAsyncioTestCase):
self.assertEqual(configuration.api_url, 'http://localhost:8080')
configuration.is_valid() # Should not throw and complain about scheme being invalid

def test_timeout_millisec(self):
"""
Ensure that timeout_millisec is set and validated
"""
configuration = Configuration(
api_url='http://localhost:8080',
timeout_millisec=10000,
)
self.assertEqual(configuration.timeout_millisec, 10000)
configuration.is_valid()

async def test_bad_configuration_read_authorization_model(self):
"""
Test whether FgaValidationException is raised for API (reading authorization models)
Expand Down
Loading