From cca3f5ff468a46a7e91db90d9d7c10de411fe7c5 Mon Sep 17 00:00:00 2001 From: Mika Hietanen Date: Fri, 23 Jan 2026 09:12:30 +0200 Subject: [PATCH] fix: treat LinkedEvents 404/410 as ObjectDoesNotExistError Event/place resolvers now use shared LinkedEvents fetch helper. 404 and 410 are converted to ObjectDoesNotExistError so they are not reported to Sentry as HTTPError. Refs: PT-1990 --- graphene_linked_events/schema.py | 17 +++--- .../tests/__snapshots__/test_api.ambr | 52 ++++++++++++++++++- graphene_linked_events/tests/test_api.py | 42 ++++++++++++++- graphene_linked_events/utils.py | 5 ++ 4 files changed, 102 insertions(+), 14 deletions(-) diff --git a/graphene_linked_events/schema.py b/graphene_linked_events/schema.py index 54d0a829..c6c90ec0 100644 --- a/graphene_linked_events/schema.py +++ b/graphene_linked_events/schema.py @@ -39,6 +39,7 @@ get_keyword_set_by_id, json2obj, json_object_hook, + retrieve_linked_events_data, ) from occurrences.event_api_services import prepare_published_event_data from occurrences.models import PalvelutarjotinEvent, VenueCustomData @@ -491,15 +492,11 @@ def _test_events_p_event_relations(events_json): @staticmethod def resolve_event(parent, info, **kwargs): - response = api_client.retrieve( - "event", - kwargs.pop("id"), - params=kwargs, - is_event_staff=getattr(info.context.user, "is_event_staff", False), + event_id = kwargs.pop("id") + is_event_staff = getattr(info.context.user, "is_event_staff", False) + return retrieve_linked_events_data( + "event", event_id, params=kwargs, is_event_staff=is_event_staff ) - response.raise_for_status() - obj = json2obj(format_response(response)) - return obj @staticmethod def resolve_events(parent, info, **kwargs): # noqa: C901 @@ -620,9 +617,7 @@ def resolve_place(parent, info, **kwargs): place_id = kwargs["id"] if not place_id: return None - response = api_client.retrieve("place", place_id) - response.raise_for_status() - return json2obj(format_response(response)) + return retrieve_linked_events_data("place", place_id) @staticmethod def resolve_places(parent, info, **kwargs): diff --git a/graphene_linked_events/tests/__snapshots__/test_api.ambr b/graphene_linked_events/tests/__snapshots__/test_api.ambr index 59a20e2e..24a9b661 100644 --- a/graphene_linked_events/tests/__snapshots__/test_api.ambr +++ b/graphene_linked_events/tests/__snapshots__/test_api.ambr @@ -394,6 +394,30 @@ }), }) # --- +# name: test_get_event_gone + dict({ + 'data': dict({ + 'event': None, + }), + 'errors': list([ + dict({ + 'extensions': dict({ + 'code': 'OBJECT_DOES_NOT_EXIST_ERROR', + }), + 'locations': list([ + dict({ + 'column': 3, + 'line': 3, + }), + ]), + 'message': 'The resource is no longer available in the API (gone).', + 'path': list([ + 'event', + ]), + }), + ]), + }) +# --- # name: test_get_event_not_found dict({ 'data': dict({ @@ -402,7 +426,7 @@ 'errors': list([ dict({ 'extensions': dict({ - 'code': 'GENERAL_ERROR', + 'code': 'OBJECT_DOES_NOT_EXIST_ERROR', }), 'locations': list([ dict({ @@ -410,7 +434,7 @@ 'line': 3, }), ]), - 'message': 'A mocked generic HTTP error', + 'message': 'Could not find the event from the API.', 'path': list([ 'event', ]), @@ -915,6 +939,30 @@ }), }) # --- +# name: test_get_place_gone + dict({ + 'data': dict({ + 'place': None, + }), + 'errors': list([ + dict({ + 'extensions': dict({ + 'code': 'OBJECT_DOES_NOT_EXIST_ERROR', + }), + 'locations': list([ + dict({ + 'column': 3, + 'line': 3, + }), + ]), + 'message': 'The resource is no longer available in the API (gone).', + 'path': list([ + 'place', + ]), + }), + ]), + }) +# --- # name: test_get_places dict({ 'data': dict({ diff --git a/graphene_linked_events/tests/test_api.py b/graphene_linked_events/tests/test_api.py index 9e7ddd34..6e06e595 100644 --- a/graphene_linked_events/tests/test_api.py +++ b/graphene_linked_events/tests/test_api.py @@ -740,6 +740,24 @@ def mock_data(*args, **kwargs): snapshot.assert_match(executed) +def test_get_event_gone(api_client, snapshot, monkeypatch): + """Test that 410 (Gone) status code is handled as ObjectDoesNotExistError.""" + + def _get_mock_function(event_data, keyword_data, status_code=410): + def mock_data(*args, **kwargs): + return MockResponse(status_code=status_code, json_data={}) + + return mock_data + + monkeypatch.setattr( + graphene_linked_events.rest_client.LinkedEventsApiClient, + "retrieve", + _get_mock_function(EVENT_DATA, KEYWORD_SET_DATA), + ) + executed = api_client.execute(GET_EVENT_QUERY) + snapshot.assert_match(executed) + + def test_get_places(api_client, snapshot, mock_get_places_data): executed = api_client.execute(GET_PLACES_QUERY) snapshot.assert_match(executed) @@ -750,6 +768,24 @@ def test_get_place(api_client, snapshot, mock_get_place_data): snapshot.assert_match(executed) +def test_get_place_gone(api_client, snapshot, monkeypatch): + """Test that 410 (Gone) status code is handled as ObjectDoesNotExistError for places.""" + + def _get_mock_function(place_data, status_code=410): + def mock_data(*args, **kwargs): + return MockResponse(status_code=status_code, json_data={}) + + return mock_data + + monkeypatch.setattr( + graphene_linked_events.rest_client.LinkedEventsApiClient, + "retrieve", + _get_mock_function({}), + ) + executed = api_client.execute(GET_PLACE_QUERY) + snapshot.assert_match(executed) + + def test_get_keywords(api_client, snapshot, mock_get_keywords_data): executed = api_client.execute(GET_KEYWORDS_QUERY) snapshot.assert_match(executed) @@ -2243,7 +2279,11 @@ def mock_retrieve_with_mixed_results( @pytest.mark.parametrize( "status_code,error_cls", - [(400, ApiBadRequestError), (404, ObjectDoesNotExistError)], + [ + (400, ApiBadRequestError), + (404, ObjectDoesNotExistError), + (410, ObjectDoesNotExistError), + ], ) def test_linkedevents_api_retrieve_errors(status_code, error_cls): with patch.object( diff --git a/graphene_linked_events/utils.py b/graphene_linked_events/utils.py index 98a353d6..2212fb68 100644 --- a/graphene_linked_events/utils.py +++ b/graphene_linked_events/utils.py @@ -57,6 +57,11 @@ def retrieve_linked_events_data( if response.status_code == 404: raise ObjectDoesNotExistError("Could not find the event from the API.") + if response.status_code == 410: + raise ObjectDoesNotExistError( + "The resource is no longer available in the API (gone)." + ) + # Raise any other errors response.raise_for_status()