diff --git a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/approve_serializers.py b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/approve_serializers.py index 0597774d74..d849acab16 100644 --- a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/approve_serializers.py +++ b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/approve_serializers.py @@ -10,9 +10,9 @@ from tilavarauspalvelu.enums import AccessType, ReservationStateChoice from tilavarauspalvelu.integrations.email.main import EmailService from tilavarauspalvelu.integrations.keyless_entry import PindoraClient -from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraClientError from tilavarauspalvelu.models import Reservation from utils.date_utils import local_datetime +from utils.external_service.errors import ExternalServiceError if TYPE_CHECKING: from tilavarauspalvelu.typing import ReservationApproveData @@ -59,7 +59,7 @@ def validate(self, data: ReservationApproveData) -> ReservationApproveData: def update(self, instance: Reservation, validated_data: ReservationApproveData) -> Reservation: if self.instance.access_type == AccessType.ACCESS_CODE and instance.recurring_reservation is None: # Allow activation in Pindora to fail, will be handled by a background task. - with suppress(PindoraClientError): + with suppress(ExternalServiceError): # If access code has not been generated (e.g. returned to handling after a deny and then approved), # create a new active access code in Pindora. if instance.access_code_generated_at is None: diff --git a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/confirm_serializers.py b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/confirm_serializers.py index 3d38522527..ee74c48a71 100644 --- a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/confirm_serializers.py +++ b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/confirm_serializers.py @@ -13,7 +13,6 @@ from tilavarauspalvelu.enums import AccessType, OrderStatus, PaymentType, ReservationStateChoice from tilavarauspalvelu.integrations.email.main import EmailService from tilavarauspalvelu.integrations.keyless_entry import PindoraClient -from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraClientError from tilavarauspalvelu.integrations.sentry import SentryLogger from tilavarauspalvelu.integrations.verkkokauppa.helpers import ( create_mock_verkkokauppa_order, @@ -23,6 +22,7 @@ from tilavarauspalvelu.integrations.verkkokauppa.verkkokauppa_api_client import VerkkokauppaAPIClient from tilavarauspalvelu.models import PaymentOrder, Reservation from utils.date_utils import local_datetime +from utils.external_service.errors import ExternalServiceError if TYPE_CHECKING: from tilavarauspalvelu.integrations.verkkokauppa.order.types import CreateOrderParams, Order @@ -90,7 +90,7 @@ def update(self, instance: Reservation, validated_data: ReservationConfirmData) if instance.state == ReservationStateChoice.CONFIRMED: if self.instance.access_type == AccessType.ACCESS_CODE and instance.recurring_reservation is None: # Allow activation in Pindora to fail, will be handled by a background task. - with suppress(PindoraClientError): + with suppress(ExternalServiceError): PindoraClient.activate_reservation_access_code(reservation=instance) instance.access_code_is_active = True instance.save(update_fields=["access_code_is_active"]) diff --git a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/staff_change_access_code_serializers.py b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/staff_change_access_code_serializers.py index 1ab5c7c143..fad9f83246 100644 --- a/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/staff_change_access_code_serializers.py +++ b/backend/tilavarauspalvelu/api/graphql/types/reservation/serializers/staff_change_access_code_serializers.py @@ -8,13 +8,15 @@ from tilavarauspalvelu.integrations.email.main import EmailService from tilavarauspalvelu.integrations.keyless_entry import PindoraClient -from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraClientError, PindoraNotFoundError +from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraNotFoundError from tilavarauspalvelu.models import Reservation __all__ = [ "StaffChangeReservationAccessCodeSerializer", ] +from utils.external_service.errors import ExternalServiceError + class StaffChangeReservationAccessCodeSerializer(NestingModelSerializer): """Change the access code of a reservation.""" @@ -51,7 +53,7 @@ def update(self, instance: Reservation, validated_data: dict[str, Any]) -> Reser instance.access_code_is_active = False else: if instance.access_code_should_be_active: - with suppress(PindoraClientError): + with suppress(ExternalServiceError): PindoraClient.activate_reservation_access_code(reservation=instance) instance.access_code_is_active = True EmailService.send_reservation_modified_access_code_email(reservation=instance) diff --git a/backend/tilavarauspalvelu/models/payment_order/actions.py b/backend/tilavarauspalvelu/models/payment_order/actions.py index e073f2bad5..5c922f2bfe 100644 --- a/backend/tilavarauspalvelu/models/payment_order/actions.py +++ b/backend/tilavarauspalvelu/models/payment_order/actions.py @@ -9,13 +9,13 @@ from tilavarauspalvelu.enums import AccessType, OrderStatus, ReservationStateChoice from tilavarauspalvelu.integrations.email.main import EmailService from tilavarauspalvelu.integrations.keyless_entry import PindoraClient -from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraClientError from tilavarauspalvelu.integrations.sentry import SentryLogger from tilavarauspalvelu.integrations.verkkokauppa.order.exceptions import CancelOrderError from tilavarauspalvelu.integrations.verkkokauppa.payment.exceptions import GetPaymentError from tilavarauspalvelu.integrations.verkkokauppa.payment.types import PaymentStatus from tilavarauspalvelu.integrations.verkkokauppa.verkkokauppa_api_client import VerkkokauppaAPIClient from utils.date_utils import local_datetime +from utils.external_service.errors import ExternalServiceError if TYPE_CHECKING: from tilavarauspalvelu.integrations.verkkokauppa.order.types import Order @@ -119,7 +119,7 @@ def update_order_status(self, new_status: OrderStatus, payment_id: str = "") -> if reservation.access_type == AccessType.ACCESS_CODE: # Allow activation in Pindora to fail, will be handled by a background task. - with suppress(PindoraClientError): + with suppress(ExternalServiceError): PindoraClient.activate_reservation_access_code(reservation=reservation) reservation.access_code_is_active = True update_fields.append("access_code_is_active") diff --git a/backend/tilavarauspalvelu/tasks.py b/backend/tilavarauspalvelu/tasks.py index 6100fccfc0..2f1af4ebff 100644 --- a/backend/tilavarauspalvelu/tasks.py +++ b/backend/tilavarauspalvelu/tasks.py @@ -30,7 +30,6 @@ Weekday, ) from tilavarauspalvelu.integrations.email.main import EmailService -from tilavarauspalvelu.integrations.keyless_entry.exceptions import PindoraClientError from tilavarauspalvelu.integrations.sentry import SentryLogger from tilavarauspalvelu.models import ( AffectingTimeSpan, @@ -747,7 +746,7 @@ def create_missing_pindora_reservations() -> None: for reservation in reservations: is_active = reservation.access_code_should_be_active - with suppress(PindoraClientError): + with suppress(ExternalServiceError): try: response = PindoraClient.create_reservation(reservation=reservation, is_active=is_active) @@ -789,7 +788,7 @@ def update_pindora_access_code_is_active() -> None: return for reservation in reservations: - with suppress(PindoraClientError): + with suppress(ExternalServiceError): if reservation.access_code_should_be_active: try: PindoraClient.activate_reservation_access_code(reservation=reservation) diff --git a/backend/utils/external_service/base_external_service_client.py b/backend/utils/external_service/base_external_service_client.py index 0597187056..287a901330 100644 --- a/backend/utils/external_service/base_external_service_client.py +++ b/backend/utils/external_service/base_external_service_client.py @@ -7,7 +7,11 @@ from requests import request from rest_framework.status import HTTP_500_INTERNAL_SERVER_ERROR -from utils.external_service.errors import ExternalServiceParseJSONError, ExternalServiceRequestError +from utils.external_service.errors import ( + ExternalServiceError, + ExternalServiceParseJSONError, + ExternalServiceRequestError, +) if TYPE_CHECKING: from requests import Response @@ -81,7 +85,11 @@ def handle_500_error(cls, response: Response) -> None: @classmethod def request(cls, method: Literal["get", "post", "put", "delete"], url: str, **kwargs: Any) -> Response: - return request(method, url, **kwargs, timeout=cls.REQUEST_TIMEOUT_SECONDS) + try: + return request(method, url, **kwargs, timeout=cls.REQUEST_TIMEOUT_SECONDS) + except Exception as err: + # Convert all exceptions to ExternalServiceError to allow easily suppressing them with + raise ExternalServiceError from err @classmethod @stamina.retry(on=Exception, attempts=3) # Likely retry should happen no matter the exception