"""Define package errors.""" from __future__ import annotations from typing import Any from aiohttp import ClientResponse from aiohttp.client_exceptions import ClientError from yarl import URL class RainMachineError(Exception): """Define a base error.""" pass class RequestError(RainMachineError): """Define an error related to invalid requests.""" pass class TokenExpiredError(RequestError): """Define an error for expired access tokens that can't be refreshed.""" pass class UnknownAPICallError(RequestError): """Define an error for an unknown API call.""" pass class UnvalidatedEmailError(RequestError): """Define an error for unvalidated email addresses.""" pass LOCAL_ERROR_CODE_EXCEPTION_MAPPING = { 2: TokenExpiredError, 13: UnknownAPICallError, } REMOTE_ERROR_CODE_EXCEPTION_MAPPING = { 1: UnvalidatedEmailError("The email has not been validated"), } def _raise_local_api_error(url: URL, error_code: int, message: str) -> None: """Raise the appropriate error for a remote error code. Args: url: The URL that raised the exception: error_code: The RainMachine error code. message: The RainMachine error message. Raises: exc: A RequestError subclass. """ exc: RequestError try: exc_obj = LOCAL_ERROR_CODE_EXCEPTION_MAPPING[error_code] exc = exc_obj(message) except KeyError: exc = RequestError( f"Unknown error returned for {url}: {error_code} -> {message}" ) raise exc def _raise_remote_api_error(url: URL, error_code: int) -> None: """Raise the appropriate error for a remote error code. Args: url: The URL that raised the exception: error_code: The RainMachine error code. Raises: exc: A RequestError subclass. """ exc: RequestError try: exc = REMOTE_ERROR_CODE_EXCEPTION_MAPPING[error_code] except KeyError: exc = RequestError(f"Unknown error code returned for {url}: {error_code}") raise exc def raise_for_error(resp: ClientResponse, data: dict[str, Any] | None) -> None: """Raise an error from the remote API if necessary. Args: resp: The aiohttp ClientResponse that generated the exception. data: An optional API response payload. Raises: RequestError: Raised when any error occurs. """ if data: if data.get("errorType") and data["errorType"] > 0: # RainMachine's remote cloud uses "errorType" to show errors, so if we find # that, we assume we need to raise a remote error: _raise_remote_api_error(resp.url, data["errorType"]) if data.get("statusCode") and data["statusCode"] != 200: # RainMachine's local cloud uses "statusCode" to show errors, so if we find # that, we assume we need to raise a local error: _raise_local_api_error(resp.url, data["statusCode"], data["message"]) else: try: resp.raise_for_status() except ClientError as err: raise RequestError(f"Error while requesting {resp.url}: {err}") from err