Skip to content

Commit

Permalink
feat: add retry support for api calls
Browse files Browse the repository at this point in the history
  • Loading branch information
koushik-kiran-kumar committed Jun 25, 2024
1 parent 4c8a1d7 commit 950e2ab
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 54 deletions.
82 changes: 40 additions & 42 deletions docs/examples/client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,46 @@ additional requests made by using the client automatically.
The `verify_ssl` parameter is ignored when connecting over HTTP.

Retry Requests
^^^^^^^^^^^^^

Initial client connection and all requests are retried if they are failed with a :class:`HTTP 5XX` error and the :class:`retry` parameter is set to :class:`True`. The default retry options are set as following:
- retry = True
- max_retries = 5
- retry_interval = 5 (in seconds)

To override the default retry options used by all library methods, provide them during client instantiation.

.. code-block:: python
from swimlane import Swimlane
swimlane = Swimlane(
'192.168.1.1',
'username',
'password',
retry=True,
max_retries=3,
retry_interval=10 # in seconds
)
The :meth:`swimlane.Swimlane.request` method can also accept the optional retry parameters that will override the
global defaults for the single request.

.. code-block:: python
from swimlane import Swimlane
swimlane = Swimlane('192.168.1.1', 'username', 'password')
# Potentially long delay before starting response with 10 minute timeout
response = swimlane.request(
'post',
'some/endpoint',
...,
retry=True,
max_retries=3,
retry_interval=10 # in seconds
)
Resource Caching
^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -270,48 +310,6 @@ disabled by setting `verify_server_version=False`.
'password',
verify_server_version=False
)
Retry Requests
^^^^^^^^^^^^^

Initial client connection and all requests are retried if they are failed with a :class:`HTTP 5XX` error and the :class:`retry` parameter is set to :class:`True`. The default retry options are set as following:
- retry = True
- max_retries = 5
- retry_interval = 5 (in seconds)

To override the default retry options used by all library methods, provide them during client instantiation.

.. code-block:: python
from swimlane import Swimlane
swimlane = Swimlane(
'192.168.1.1',
'username',
'password',
retry=True,
max_retries=3,
retry_interval=10 # in seconds
)
The :meth:`swimlane.Swimlane.request` method can also accept the optional retry parameters that will override the
global defaults for the single request.

.. code-block:: python
from swimlane import Swimlane
swimlane = Swimlane('192.168.1.1', 'username', 'password')
# Potentially long delay before starting response with 10 minute timeout
response = swimlane.request(
'post',
'some/endpoint',
...,
retry=True,
max_retries=3,
retry_interval=10 # in seconds
)
Available Adapters
------------------

Expand Down
60 changes: 48 additions & 12 deletions swimlane/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import jwt
import pendulum
import requests
import time
from pyuri import URI
from requests.compat import json
from requests.packages import urllib3
from requests.structures import CaseInsensitiveDict
from requests.exceptions import ConnectionError
from six.moves.urllib.parse import urljoin

from swimlane.core.adapters import GroupAdapter, UserAdapter, AppAdapter, HelperAdapter
Expand Down Expand Up @@ -46,6 +48,9 @@ class Swimlane(object):
caching. Disabled by default
access_token (str): Authentication token, used in lieu of a username and password
write_to_read_only (bool): Enable the ability to write to Read-only fields
retry (bool): Retry request when error code is >= 500
max_retries (int): Maximum number of retry attempts
retry_interval (int): Time interval (in seconds) between two retry attempts
Attributes:
host (pyuri.URI): Full RFC-1738 URL pointing to Swimlane host
Expand Down Expand Up @@ -92,7 +97,10 @@ def __init__(
verify_server_version=True,
resource_cache_size=0,
access_token=None,
write_to_read_only=False
write_to_read_only: bool=False,
retry: bool=True,
max_retries: int=5,
retry_interval: int=5
):
self.__verify_auth_params(username, password, access_token)

Expand All @@ -111,6 +119,10 @@ def __init__(

self._session = WrappedSession()
self._session.verify = verify_ssl

self.retry = retry
self.max_retries = max_retries
self.retry_interval = retry_interval

if username is not None and password is not None:
self._session.auth = SwimlaneJwtAuth(
Expand Down Expand Up @@ -215,17 +227,41 @@ def request(self, method, api_endpoint, **kwargs):
kwargs['headers'] = headers

kwargs['data'] = json.dumps(json_data, sort_keys=True, separators=(',', ':'))

response = self._session.request(method, urljoin(str(self.host) + self._api_root, api_endpoint), **kwargs)

# Roll 400 errors up into SwimlaneHTTP400Errors with specific Swimlane error code support
try:
response.raise_for_status()
except requests.HTTPError as error:
if error.response.status_code == 400:
raise SwimlaneHTTP400Error(error)
else:
raise error

# Retry logic
req_retry = kwargs.pop('retry', self.retry)

req_max_retries = kwargs.pop('max_retries', self.max_retries)
if not isinstance(req_max_retries, int):
raise TypeError('max_retries should be an integer')
if req_max_retries <= 0:
raise ValueError('max_retries should be a positive integer')

req_retry_interval = kwargs.pop('retry_interval', self.retry_interval)
if not isinstance(req_retry_interval, int):
raise TypeError('retry_interval should be an integer')
if req_retry_interval <= 0:
raise ValueError('retry_interval should be a positive integer')

while not req_max_retries<0:
response = self._session.request(method, urljoin(str(self.host) + self._api_root, api_endpoint), **kwargs)

# Roll 400 errors up into SwimlaneHTTP400Errors with specific Swimlane error code support
try:
response.raise_for_status()
# Exit loop on successful request
req_max_retries = -1
except requests.HTTPError as error:
if error.response.status_code == 400:
raise SwimlaneHTTP400Error(error)
else:
if req_retry and req_max_retries>0 and error.response.status_code>=500:
req_max_retries -= 1
time.sleep(req_retry_interval)
continue
elif req_max_retries == 0:
raise ConnectionError(f'Max retries exceeded. Caused by ({error})')
raise error

return response

Expand Down
12 changes: 12 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from swimlane import Swimlane


app = Swimlane(
host='https://swimlane-staging.swimlane.io/account',
username='admin1',
password='P00lParty!',
max_retries=5,
retry_interval=1
)

print(app.apps.list())

0 comments on commit 950e2ab

Please sign in to comment.