PyFlowery/pyflowery/rest_adapter.py

110 lines
5.5 KiB
Python
Raw Normal View History

"""This module contains the RestAdapter class, which is used to make requests to the Flowery API.""" ""
2024-11-15 09:51:11 -05:00
from asyncio import sleep as asleep
2024-09-15 23:12:30 -04:00
from json import JSONDecodeError
import aiohttp
from pyflowery.exceptions import (
ClientError,
InternalServerError,
RetryLimitExceeded,
TooManyRequests,
)
2024-09-15 23:12:30 -04:00
from pyflowery.models import FloweryAPIConfig, Result
class RestAdapter:
"""Constructor for RestAdapter
Args:
config (FloweryAPIConfig): Configuration object for the FloweryAPI class
Raises:
ValueError: Raised when the keyword arguments passed to the class constructor conflict.
"""
def __init__(self, config=FloweryAPIConfig) -> None:
2024-09-15 23:12:30 -04:00
self._url = "https://api.flowery.pw/v1"
self.config = config
2024-09-15 23:12:30 -04:00
2024-11-15 09:51:11 -05:00
async def _do(self, http_method: str, endpoint: str, params: dict | None = None, timeout: float = 60) -> Result | None:
2024-09-15 23:12:30 -04:00
"""Internal method to make a request to the Flowery API. You shouldn't use this directly.
Args:
http_method (str): The [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) to use
endpoint (str): The endpoint to make the request to.
params (dict): Python dictionary of query parameters to send with the request.
timeout (float): Number of seconds to wait for the request to complete.
Raises:
TooManyRequests: Raised when the Flowery API returns a 429 status code
ClientError: Raised when the Flowery API returns a 4xx status code
InternalServerError: Raised when the Flowery API returns a 5xx status code
RetryLimitExceeded: Raised when the retry limit defined in the `FloweryAPIConfig` class (default 3) is exceeded
2024-09-15 23:12:30 -04:00
Returns:
Result: A Result object containing the status code, message, and data from the request.
"""
full_url = self._url + endpoint
headers = {
"User-Agent": self.config.prepended_user_agent,
2024-09-15 23:12:30 -04:00
}
sanitized_params = {k: str(v) if isinstance(v, bool) else v for k, v in params.items()} if params else None
retry_counter = 0
2024-09-15 23:12:30 -04:00
async with aiohttp.ClientSession() as session:
while retry_counter < self.config.retry_limit:
self.config.logger.debug("Making %s request to %s with headers %s and params %s", http_method, full_url, headers, sanitized_params)
2024-11-15 09:51:11 -05:00
async with session.request(method=http_method, url=full_url, params=sanitized_params, headers=headers, timeout=timeout) as response: # type: ignore
try:
data = await response.json()
except (JSONDecodeError, aiohttp.ContentTypeError):
data = await response.read()
2024-09-15 23:12:30 -04:00
result = Result(
2024-11-15 09:51:11 -05:00
success=response.status < 300,
status_code=response.status,
2024-11-15 09:51:11 -05:00
message=response.reason or "Unknown error",
data=data,
)
self.config.logger.debug("Received response: %s %s", response.status, response.reason)
try:
if result.status_code == 429:
2024-11-15 09:51:11 -05:00
raise TooManyRequests(f"{result.message} - {result.data!r}")
2024-09-17 23:31:50 -04:00
if 400 <= result.status_code < 500:
2024-11-15 09:51:11 -05:00
raise ClientError(f"{result.status_code} - {result.message} - {result.data!r}")
2024-09-17 23:31:50 -04:00
if 500 <= result.status_code < 600:
2024-11-15 09:51:11 -05:00
raise InternalServerError(f"{result.status_code} - {result.message} - {result.data!r}")
except (TooManyRequests, InternalServerError) as e:
if retry_counter < self.config.retry_limit:
interval = self.config.interval * retry_counter
self.config.logger.error("%s - retrying in %s seconds", e, interval, exc_info=True)
retry_counter += 1
await asleep(interval)
continue
raise RetryLimitExceeded(message=f"Request failed more than {self.config.retry_limit} times, not retrying") from e
return result
2024-11-15 09:51:11 -05:00
return None
2024-09-15 23:12:30 -04:00
2024-11-15 09:51:11 -05:00
async def get(self, endpoint: str, params: dict | None = None, timeout: float = 60) -> Result | None:
2024-09-15 23:12:30 -04:00
"""Make a GET request to the Flowery API. You should almost never have to use this directly.
If you need to use this method because an endpoint is missing, please open an issue on the [CoastalCommits repository](https://www.coastalcommits.com/cswimr/PyFlowery/issues).
2024-09-15 23:12:30 -04:00
Args:
endpoint (str): The endpoint to make the request to.
params (dict): Python dictionary of query parameters to send with the request.
timeout (float): Number of seconds to wait for the request to complete.
Raises:
TooManyRequests: Raised when the Flowery API returns a 429 status code
ClientError: Raised when the Flowery API returns a 4xx status code
InternalServerError: Raised when the Flowery API returns a 5xx status code
RetryLimitExceeded: Raised when the retry limit defined in the `FloweryAPIConfig` class (default 3) is exceeded
2024-09-15 23:12:30 -04:00
Returns:
Result: A Result object containing the status code, message, and data from the request.
"""
return await self._do(http_method="GET", endpoint=endpoint, params=params, timeout=timeout)