from typing import List, Union # noqa: UP035 – Python <3.10 compatibility from requests import RequestException, Response REQUEST_ERROR_STATUS_CODE = 503 REQUEST_ERROR_MESSAGE = "Request failed" TOKEN_ERROR_GUIDANCE = "See our requirements for JSON Web Tokens \ at https://docs.notifications.service.gov.uk/rest-api.html#authorisation-header" TOKEN_ERROR_DEFAULT_ERROR_MESSAGE = "Invalid token: " + TOKEN_ERROR_GUIDANCE class TokenError(Exception): def __init__(self, message=None, token=None): self.message = ( message + ". " + TOKEN_ERROR_GUIDANCE if message else TOKEN_ERROR_DEFAULT_ERROR_MESSAGE ) self.token = token super().__init__(self.message, token) class TokenExpiredError(TokenError): pass class TokenAlgorithmError(TokenError): def __init__(self): super().__init__("Invalid token: algorithm used is not HS256") class TokenDecodeError(TokenError): def __init__(self, message=None): super().__init__(message or "Invalid token: signature") class TokenIssuerError(TokenDecodeError): def __init__(self): super().__init__("Invalid token: iss field not provided") class TokenIssuedAtError(TokenDecodeError): def __init__(self): super().__init__("Invalid token: iat field not provided") class APIError(Exception): def __init__(self, response: Response = None, message: str = None): self.response = response self._message = message super().__init__(response, message) def __str__(self): return f"{self.status_code} - {self.message}" @property def message( self, ) -> Union[str, List[dict]]: # noqa: UP006, UP007 – Python <3.10 compatibility try: json_resp = self.response.json() # type: ignore return json_resp.get("message", json_resp.get("errors")) except (TypeError, ValueError, AttributeError, KeyError): return self._message or REQUEST_ERROR_MESSAGE @property def status_code(self) -> int: try: return self.response.status_code # type: ignore except AttributeError: return REQUEST_ERROR_STATUS_CODE class HTTPError(APIError): @staticmethod def create(e: RequestException) -> "HTTPError": error = HTTPError(e.response) if error.status_code == 503: error = HTTP503Error(e.response) return error class HTTP503Error(HTTPError): """Specific instance of HTTPError for 503 errors Used for detecting whether failed requests should be retried. """ class InvalidResponse(APIError): pass