import time from typing import List, Union, Dict import requests from apollo_utils.service.exceptions import APIUnavailable, APIInvalidResponse class BaseApiClient: service = None logger = None _loaded_attrs = None _loaded_default_attrs = None _schema = None _host = None _auth_name = None _apikey = None _retry_count = None _retry_delay = None _timeout = None def __init__(self): self._session = None self._base_url = f"{self._schema}://{self._host}" def _get_header(self): return {self._auth_name: self._apikey} @staticmethod def _add_header(session: requests.Session, header: Dict = None): return session.headers.update(header) @property def session(self): if not self._session: self._session = requests.Session() self._add_header(self._session, self._get_header()) return self._session def _send_request(self, request: requests.Request, get_json: bool = False) -> Union[List[dict], dict]: request_method = request.method retry_counter = 0 result = None request_headers = request.headers while True: try: session = self.session if request_headers: self._add_header(session, request_headers) response = session.send(session.prepare_request(request), timeout=self._timeout) except (requests.exceptions.ConnectionError, requests.Timeout) as exc: if retry_counter >= self._retry_count: if self.logger: self.logger.info( f"Got Connection error {exc} on request to {request.url} requested by: {self.service.value}. \n" ) raise APIUnavailable(extra="Connection error", service=self.service.value) retry_counter += 1 delay = self._retry_delay * retry_counter if self.logger: self.logger.info( f"[{retry_counter}/{self._retry_count}] Got exception {exc} on request to {request.url} \n" f"Requested by: {self.service.value}. \n" f"After delay {delay} seconds. \n" ) if self._retry_delay: time.sleep(self._retry_delay * retry_counter) continue if request_method == "GET" or get_json: try: result = response.json() except ValueError as exc: if self.logger: self.logger.info( f"Got exception {exc} on request to {request.url} requested by: {self.service.value}. \n" f"Original response: {result} \n" ) raise APIInvalidResponse(service=self.service.value) if response.status_code >= 400: if self.logger: self.logger.info( f"Got APIInvalidResponse on request to {request.url} requested by: {self.service.value}. \n" f"Original response: {result} \n" f"Status code: {response.status_code}" ) raise APIInvalidResponse( service=self.service.value, original_response=result, status_code=response.status_code ) return result def _prepare_url(self, url: str): return f"{self._base_url}/{url}" def send_request( self, url: str, method: str = "GET", data=None, headers: Dict = None, get_json: bool = False, **params ): url = self._prepare_url(url) request = requests.Request(method, url, params=params, headers=headers, json=data if data else None) return self._send_request(request, get_json)