Skip to content

Commit

Permalink
CASMCMS-9225: Create generic API client class
Browse files Browse the repository at this point in the history
  • Loading branch information
mharding-hpe committed Dec 17, 2024
1 parent a92800f commit 0b9f2a9
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 10 deletions.
2 changes: 1 addition & 1 deletion constraints.txt.in
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ PyYAML>=6.0.1,<6.1
redis>=5.0,<5.1
requests>=2.28.2,<2.29
requests-oauthlib>=1.3.1,<1.4
requests-retry-session>=0.1,<0.2
requests-retry-session>=2.0,<2.1
retrying>=1.3.4,<1.4
rsa>=4.9,<4.10
s3transfer>=0.6.2,<0.7
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ python-dateutil
PyYAML
redis
requests
requests-retry-session
requests-retry-session>=2.0
urllib3

# The purpose of this file is to contain python runtime requirements
Expand Down
Empty file.
62 changes: 62 additions & 0 deletions src/bos/common/clients/api_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#
# MIT License
#
# (C) Copyright 2021-2024 Hewlett Packard Enterprise Development LP
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
from abc import ABC
from typing import Type, TypeVar

from bos.common.clients.endpoints import BaseGenericEndpoint
from bos.common.utils import RetrySessionManager

ClientEndpoint = TypeVar('ClientEndpoint', bound=BaseGenericEndpoint)


class APIClient(RetrySessionManager, ABC):
"""
As a subclass of RetrySessionManager, this class can be used as a context manager,
and will have a requests session available as self.requests_session
This context manager is used to provide API endpoints, via subclassing.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._endpoint_values: dict[Type[ClientEndpoint], ClientEndpoint] = {}

def get_endpoint(self,
endpoint_type: Type[ClientEndpoint]) -> ClientEndpoint:
"""
Endpoints are created only as needed, and passed the manager retry session.
"""
if endpoint_type not in self._endpoint_values:
self._endpoint_values[endpoint_type] = endpoint_type(
self.requests_session)
return self._endpoint_values[endpoint_type]

def __exit__(self, exc_type, exc_val, exc_tb) -> bool | None:
"""
The only cleanup we need to do when exiting the context manager is to clear out
our list of API clients. Our call to super().__exit__ will take care of closing
out the underlying request session.
"""
self._endpoint_values.clear()
return super().__exit__(exc_type, exc_val, exc_tb)
38 changes: 30 additions & 8 deletions src/bos/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

# Third party imports
from dateutil.parser import parse
from requests_retry_session import requests_retry_session as base_requests_retry_session
import requests_retry_session as rrs

PROTOCOL = 'http'
TIME_DURATION_PATTERN = re.compile(r"^(\d+?)(\D+?)$", re.M | re.S)
Expand Down Expand Up @@ -68,14 +68,36 @@ def duration_to_timedelta(timestamp: str):
return datetime.timedelta(seconds=seconds)


requests_retry_session = partial(base_requests_retry_session,
retries=10,
backoff_factor=0.5,
status_forcelist=(500, 502, 503, 504),
connect_timeout=3,
read_timeout=10,
DEFAULT_RETRY_ADAPTER_ARGS = rrs.RequestsRetryAdapterArgs(
retries=10,
backoff_factor=0.5,
status_forcelist=(500, 502, 503, 504),
connect_timeout=3,
read_timeout=10)

retry_session_manager = partial(rrs.retry_session_manager,
protocol=PROTOCOL,
**DEFAULT_RETRY_ADAPTER_ARGS)


class RetrySessionManager(rrs.RetrySessionManager):
"""
Just sets the default values we use for our requests sessions
"""

def __init__(self,
protocol: str = PROTOCOL,
**adapter_kwargs: Unpack[rrs.RequestsRetryAdapterArgs]):
for key, value in DEFAULT_RETRY_ADAPTER_ARGS.items():
if key not in adapter_kwargs:
adapter_kwargs[key] = value
super().__init__(protocol=protocol, **adapter_kwargs)


requests_retry_session = partial(rrs.requests_retry_session,
session=None,
protocol=PROTOCOL)
protocol=PROTOCOL,
**DEFAULT_RETRY_ADAPTER_ARGS)


def compact_response_text(response_text: str) -> str:
Expand Down

0 comments on commit 0b9f2a9

Please sign in to comment.