-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CASMCMS-9225: Create generic endpoint classes
- Loading branch information
1 parent
e7b2128
commit 90aa877
Showing
8 changed files
with
476 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# | ||
# 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 .base_endpoint import BaseEndpoint | ||
from .base_generic_endpoint import BaseGenericEndpoint, RequestErrorHandler | ||
from .base_raw_endpoint import BaseRawEndpoint | ||
from .defs import JsonData, RequestData, RequestsMethod | ||
from .exceptions import ApiResponseError | ||
from .response_data import ResponseData |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# | ||
# 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 | ||
|
||
import requests | ||
|
||
from .base_generic_endpoint import BaseGenericEndpoint | ||
from .defs import JsonData | ||
from .response_data import ResponseData | ||
|
||
|
||
class BaseEndpoint(BaseGenericEndpoint[JsonData], ABC): | ||
""" | ||
This base class provides generic access to an API where the only part of the response | ||
that is returned is the body. | ||
""" | ||
|
||
@classmethod | ||
def format_response(cls, response: requests.Response) -> JsonData: | ||
return ResponseData.from_response(response).body |
119 changes: 119 additions & 0 deletions
119
src/bos/common/clients/endpoints/base_generic_endpoint.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
# | ||
# 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, abstractmethod | ||
import logging | ||
from typing import Generic, TypeVar | ||
|
||
import requests | ||
|
||
from .defs import RequestData, RequestsMethod | ||
from .exceptions import ApiResponseError | ||
from .request_error_handler import BaseRequestErrorHandler, RequestErrorHandler | ||
|
||
LOGGER = logging.getLogger(__name__) | ||
|
||
RequestReturnT = TypeVar('RequestReturnT') | ||
|
||
|
||
class BaseGenericEndpoint(ABC, Generic[RequestReturnT]): | ||
""" | ||
This base class provides generic access to an API endpoint. | ||
RequestReturnT represents the type of data this API will return. | ||
Most often this will be the Json data from the response body, but in some | ||
cases (like with BSS), we are after something else. | ||
Exceptions are handled by a separate class, since different API clients | ||
may want to handle these differently. | ||
""" | ||
BASE_ENDPOINT: str = '' | ||
ENDPOINT: str = '' | ||
error_handler: BaseRequestErrorHandler = RequestErrorHandler | ||
|
||
def __init__(self, session: requests.Session): | ||
super().__init__() | ||
self.session = session | ||
|
||
@classmethod | ||
@abstractmethod | ||
def format_response(cls, response: requests.Response) -> RequestReturnT: | ||
... | ||
|
||
@classmethod | ||
def base_url(cls) -> str: | ||
return f"{cls.BASE_ENDPOINT}/{cls.ENDPOINT}" | ||
|
||
@classmethod | ||
def url(cls, uri: str) -> str: | ||
base_url = cls.base_url() | ||
if not uri: | ||
return base_url | ||
if uri[0] == '/' or base_url[-1] == '/': | ||
return f"{base_url}{uri}" | ||
return f"{base_url}/{uri}" | ||
|
||
def request(self, | ||
method: RequestsMethod, | ||
/, | ||
*, | ||
uri: str = "", | ||
**kwargs) -> RequestReturnT: | ||
url = self.url(uri) | ||
LOGGER.debug("%s %s (kwargs=%s)", method.__name__.upper(), url, kwargs) | ||
try: | ||
return self._request(method, url, **kwargs) | ||
except Exception as err: | ||
self.error_handler.handle_exception( | ||
err, | ||
RequestData(method_name=method.__name__.upper(), | ||
url=url, | ||
request_options=kwargs)) | ||
|
||
@classmethod | ||
def _request(cls, method: RequestsMethod, url: str, /, | ||
**kwargs) -> RequestReturnT: | ||
"""Make API request""" | ||
with method(url, **kwargs) as response: | ||
if not response.ok: | ||
raise ApiResponseError(response=response) | ||
return cls.format_response(response) | ||
|
||
def delete(self, **kwargs) -> RequestReturnT: | ||
"""Delete request""" | ||
return self.request(self.session.delete, **kwargs) | ||
|
||
def get(self, **kwargs) -> RequestReturnT: | ||
"""Get request""" | ||
return self.request(self.session.get, **kwargs) | ||
|
||
def patch(self, **kwargs) -> RequestReturnT: | ||
"""Patch request""" | ||
return self.request(self.session.patch, **kwargs) | ||
|
||
def post(self, **kwargs) -> RequestReturnT: | ||
"""Post request""" | ||
return self.request(self.session.post, **kwargs) | ||
|
||
def put(self, **kwargs) -> RequestReturnT: | ||
"""Put request""" | ||
return self.request(self.session.put, **kwargs) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# | ||
# 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 | ||
|
||
import requests | ||
|
||
from .base_generic_endpoint import BaseGenericEndpoint | ||
from .response_data import ResponseData | ||
|
||
|
||
class BaseRawEndpoint(BaseGenericEndpoint[ResponseData], ABC): | ||
""" | ||
This base class provides generic access to an API. | ||
In this case, an assortment of response data is returned up, rather than | ||
just the response body. | ||
""" | ||
|
||
@classmethod | ||
def format_response(cls, response: requests.Response) -> ResponseData: | ||
return ResponseData.from_response(response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# | ||
# 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 typing import Any, Callable, ContextManager, NamedTuple | ||
|
||
import requests | ||
|
||
type JsonData = bool | str | None | int | float | list[JsonData] | dict[str, JsonData] | ||
type JsonDict = dict[str, JsonData] | ||
type JsonList = list[JsonData] | ||
|
||
type RequestsMethod = Callable[..., ContextManager[requests.Response]] | ||
|
||
class RequestData(NamedTuple): | ||
""" | ||
This class encapsulates data about an API request. | ||
It is passed into the exception handler, so that it is able to | ||
include information about the request in its logic and error messages. | ||
""" | ||
method_name: str | ||
url: str | ||
request_options: dict[str, Any] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# | ||
# 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. | ||
# | ||
import requests | ||
|
||
from .response_data import ResponseData | ||
|
||
|
||
class ApiResponseError(Exception): | ||
"""Raised when API response has non-ok status""" | ||
|
||
def __init__(self, *args, response: requests.Response, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.response_data = ResponseData.from_response(response) |
Oops, something went wrong.