# Path: esteid/exceptions.py
# class ActionInProgress(EsteidError):
#     """
#     Raised when a request to the service is still in pending status,
#     or when it is necessary to move on to the next step while passing some data on to the user
#     (like after successful auth, we initiate a signing request and display verification code)
#     """
# 
#     status = HTTPStatus.ACCEPTED
# 
#     default_message = _("Operation is in progress.")
# 
#     def __init__(self, message=None, data: dict = None):
#         super().__init__(message)
#         self.data = data or {}
# 
# class BadRequest(EsteidError):
#     pass
# 
# class EsteidError(Exception):
#     """
#     A generic Esteid error.
# 
#     Provides an interface for displaying a translated message to the user.
# 
#     Note: by default, any message passed as a first positional argument is ignored, because it only
#     serves for better logging. The User Interface should display a verbose error message
#     in case of a user-caused error so that user can take action accordingly. Errors caused by service malfunction
#     are likely not informative to a user without malicious intent.
# 
#     If the default message contains {key} placeholders, the exception is supposed to be
#     raised with kwargs, these will be passed on to <translated default message>.format(**kwargs).
#     """
# 
#     status = HTTPStatus.INTERNAL_SERVER_ERROR
# 
#     default_message = _("Something went wrong")
# 
#     kwargs: dict
# 
#     def __init__(self, message=None, **kwargs):
#         super().__init__(message)
#         self.kwargs = kwargs
# 
#     def get_message(self):
#         return str(self.default_message).format(**self.kwargs)
# 
#     def get_user_error(self):
#         return {
#             "error": self.__class__.__name__,  # error code that can be bound to JS translations
#             # TODO review errors and add translations
#             "message": self.get_message(),
#         }
# 
# class InvalidCredentials(EsteidError):
#     pass
# 
# class OfflineError(UpstreamServiceError):
#     default_message = _("The {service} service is unavailable at the moment. Please try again later.")
# 
# class SessionDoesNotExist(EsteidError):
#     pass
# 
# class UnsupportedClientImplementation(UpstreamServiceError):
#     pass
# 
# class UpstreamServiceError(EsteidError):
#     """A failure reported by upstream service which is not caused by user.
# 
#     Takes a `service` kwarg, e.g. Mobile ID / Smart ID
#     """
# 
#     status = HTTPStatus.SERVICE_UNAVAILABLE
# 
#     default_message = _("The {service} service reported a failure.")
# 
#     def __init__(self, message=None, *, service, **kwargs):
#         super().__init__(message)
#         self.kwargs = {"service": service, **kwargs}


import logging
import requests
from typing import Union
from uuid import UUID
from .exceptions import (
    ActionInProgress,
    BadRequest,
    EsteidError,
    InvalidCredentials,
    OfflineError,
    SessionDoesNotExist,
    UnsupportedClientImplementation,
    UpstreamServiceError,
)
            raise OfflineError(service=self.NAME) from e

        except requests.HTTPError as e:
            status_code = e.response.status_code
            if status_code == 401:
                logger.exception("API authentication failure at %s: %s", req.url, e.response.text)
                raise InvalidCredentials from e

            if status_code == 400:
                logger.exception("Bad Request to %s. Response:\n%s", req.url, e.response.text)
                raise BadRequest from e

            if status_code == 480:
                logger.exception("Service reported Unsupported Client at %s. Response:\n%s", req.url, e.response.text)
                raise UnsupportedClientImplementation(service=self.NAME) from e

            # 580 System is under maintenance, retry later.
            # see https://github.com/SK-EID/smart-id-documentation#413-http-status-code-usage
            # (Note: Though not documented, Mobile ID also returns this occasionally.)
            if status_code == 580:
                logger.exception("Service %s on maintenance.", self.NAME)
                raise OfflineError("Maintenance", service=self.NAME) from e

            # Raise proxy errors as OfflineError
            if status_code in [502, 503, 504]:
                logger.exception("Service %s not available (HTTP %s).", req.url, status_code)
                raise OfflineError(f"Proxy error: {status_code}", service=self.NAME) from e

            # UpstreamServiceError for everything else. The caller can use `exc.__cause__` to get the original HTTPError
            # Do not log these errors here, because they can be related to user input, depending on the endpoint.

# Path: esteid/exceptions.py
# class ActionInProgress(EsteidError):
#     """
#     Raised when a request to the service is still in pending status,
#     or when it is necessary to move on to the next step while passing some data on to the user
#     (like after successful auth, we initiate a signing request and display verification code)
#     """
# 
#     status = HTTPStatus.ACCEPTED
# 
#     default_message = _("Operation is in progress.")
# 
#     def __init__(self, message=None, data: dict = None):
#         super().__init__(message)
#         self.data = data or {}
# 
# class BadRequest(EsteidError):
#     pass
# 
# class EsteidError(Exception):
#     """
#     A generic Esteid error.
# 
#     Provides an interface for displaying a translated message to the user.
# 
#     Note: by default, any message passed as a first positional argument is ignored, because it only
#     serves for better logging. The User Interface should display a verbose error message
#     in case of a user-caused error so that user can take action accordingly. Errors caused by service malfunction
#     are likely not informative to a user without malicious intent.
# 
#     If the default message contains {key} placeholders, the exception is supposed to be
#     raised with kwargs, these will be passed on to <translated default message>.format(**kwargs).
#     """
# 
#     status = HTTPStatus.INTERNAL_SERVER_ERROR
# 
#     default_message = _("Something went wrong")
# 
#     kwargs: dict
# 
#     def __init__(self, message=None, **kwargs):
#         super().__init__(message)
#         self.kwargs = kwargs
# 
#     def get_message(self):
#         return str(self.default_message).format(**self.kwargs)
# 
#     def get_user_error(self):
#         return {
#             "error": self.__class__.__name__,  # error code that can be bound to JS translations
#             # TODO review errors and add translations
#             "message": self.get_message(),
#         }
# 
# class InvalidCredentials(EsteidError):
#     pass
# 
# class OfflineError(UpstreamServiceError):
#     default_message = _("The {service} service is unavailable at the moment. Please try again later.")
# 
# class SessionDoesNotExist(EsteidError):
#     pass
# 
# class UnsupportedClientImplementation(UpstreamServiceError):
#     pass
# 
# class UpstreamServiceError(EsteidError):
#     """A failure reported by upstream service which is not caused by user.
# 
#     Takes a `service` kwarg, e.g. Mobile ID / Smart ID
#     """
# 
#     status = HTTPStatus.SERVICE_UNAVAILABLE
# 
#     default_message = _("The {service} service reported a failure.")
# 
#     def __init__(self, message=None, *, service, **kwargs):
#         super().__init__(message)
#         self.kwargs = {"service": service, **kwargs}


# Path: esteid/base_service.py
import logging
import requests
from typing import Union
from uuid import UUID
from .exceptions import (
    ActionInProgress,
    BadRequest,
    EsteidError,
    InvalidCredentials,
    OfflineError,
    SessionDoesNotExist,
    UnsupportedClientImplementation,
    UpstreamServiceError,
)

            raise OfflineError(service=self.NAME) from e

        except requests.HTTPError as e:
            status_code = e.response.status_code
            if status_code == 401:
                logger.exception("API authentication failure at %s: %s", req.url, e.response.text)
                raise InvalidCredentials from e

            if status_code == 400:
                logger.exception("Bad Request to %s. Response:\n%s", req.url, e.response.text)
                raise BadRequest from e

            if status_code == 480:
                logger.exception("Service reported Unsupported Client at %s. Response:\n%s", req.url, e.response.text)
                raise UnsupportedClientImplementation(service=self.NAME) from e

            # 580 System is under maintenance, retry later.
            # see https://github.com/SK-EID/smart-id-documentation#413-http-status-code-usage
            # (Note: Though not documented, Mobile ID also returns this occasionally.)
            if status_code == 580:
                logger.exception("Service %s on maintenance.", self.NAME)
                raise OfflineError("Maintenance", service=self.NAME) from e

            # Raise proxy errors as OfflineError
            if status_code in [502, 503, 504]:
                logger.exception("Service %s not available (HTTP %s).", req.url, status_code)
                raise OfflineError(f"Proxy error: {status_code}", service=self.NAME) from e

            # UpstreamServiceError for everything else. The caller can use `exc.__cause__` to get the original HTTPError
            # Do not log these errors here, because they can be related to user input, depending on the endpoint.

            raise UpstreamServiceError(f"Service {self.NAME} returned an unidentified error", service=self.NAME) from e