From d8e6694d8517968fe49270b79d0feb798a974efc Mon Sep 17 00:00:00 2001 From: Sidney Richards Date: Mon, 5 Aug 2024 12:20:52 +0200 Subject: [PATCH] [#2648] handle legacy case detail URLs with redirect The case detail URL now contains a reference to the ZGW api group to be used to fetch the case. It is quite likely there are various hard-coded URLs in the wild (especially in notification emails) that point to the old case detail URLs. This commit adds a redirect handler for the now-deprecated case detail URL and tries to handle the request gracefully, depending on the case. --- src/open_inwoner/cms/cases/urls.py | 11 ++++ src/open_inwoner/cms/cases/views/__init__.py | 2 + src/open_inwoner/cms/cases/views/status.py | 51 ++++++++++++++- .../tests/test_case_detail_redirects.py | 65 +++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/open_inwoner/openzaak/tests/test_case_detail_redirects.py diff --git a/src/open_inwoner/cms/cases/urls.py b/src/open_inwoner/cms/cases/urls.py index 8468b33370..5dc589b5dd 100644 --- a/src/open_inwoner/cms/cases/urls.py +++ b/src/open_inwoner/cms/cases/urls.py @@ -12,6 +12,7 @@ CaseDocumentUploadFormView, InnerCaseDetailView, InnerCaseListView, + LegacyCaseDetailHandler, OuterCaseDetailView, OuterCaseListView, ) @@ -61,4 +62,14 @@ ), path("content/", InnerCaseListView.as_view(), name="cases_content"), path("", OuterCaseListView.as_view(), name="index"), + # Legacy redirects for hard-coded case detail urls lacking a ZGW api group reference + # in the url (e.g. from old notification mails). This redirects those URLs + # to the new case detail URL with a reference to the only ZGW api group (if 1), + # or the case list page with an expiration notice (because we can't be sure which + # api group to use). + path( + "/status/", + LegacyCaseDetailHandler.as_view(), + name="legacy_case_detail", + ), ] diff --git a/src/open_inwoner/cms/cases/views/__init__.py b/src/open_inwoner/cms/cases/views/__init__.py index 29832011c4..88d7111bdb 100644 --- a/src/open_inwoner/cms/cases/views/__init__.py +++ b/src/open_inwoner/cms/cases/views/__init__.py @@ -4,6 +4,7 @@ CaseDocumentDownloadView, CaseDocumentUploadFormView, InnerCaseDetailView, + LegacyCaseDetailHandler, OuterCaseDetailView, ) @@ -14,5 +15,6 @@ "CaseDocumentDownloadView", "CaseDocumentUploadFormView", "InnerCaseDetailView", + "LegacyCaseDetailHandler", "OuterCaseDetailView", ] diff --git a/src/open_inwoner/cms/cases/views/status.py b/src/open_inwoner/cms/cases/views/status.py index 1414ee9f5c..1806b00e5e 100644 --- a/src/open_inwoner/cms/cases/views/status.py +++ b/src/open_inwoner/cms/cases/views/status.py @@ -7,7 +7,12 @@ from django.conf import settings from django.contrib import messages from django.core.exceptions import ObjectDoesNotExist, PermissionDenied -from django.http import Http404, StreamingHttpResponse +from django.http import ( + Http404, + HttpRequest, + HttpResponseRedirect, + StreamingHttpResponse, +) from django.urls import reverse from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ @@ -1029,3 +1034,47 @@ def get_context_data(self, **kwargs): "cases:case_detail_contact_form", kwargs=self.kwargs ) return context + + +class LegacyCaseDetailHandler(View): + """Redirect the legacy case detail to the current version with ZGW API group ref.""" + + def get( + self, + request: HttpRequest, + object_id: str, + ): + redirect_url = None + match ZGWApiGroupConfig.objects.count(): + case 1: + target_api_group = ZGWApiGroupConfig.objects.get() + redirect_url = reverse( + "cases:case_detail", + kwargs={ + "api_group_id": target_api_group.id, + "object_id": object_id, + }, + ) + case count if count > 1: + messages.add_message( + request, + messages.ERROR, + _( + "The link you clicked on has expired. Please find your case in the" + " list below." + ), + ) + logger.warning( + "Could not automatically handle legacy case detail URL due to multiple" + " ZGWApiGroupConfig objects" + ) + redirect_url = reverse("cases:index") + case 0: + # This is an invariant violation: there should always be at least + # one ZGWApiGroupConfig. + logger.error( + "Legacy redirect invoked without any configured API groups" + ) + raise Http404 + + return HttpResponseRedirect(redirect_url) diff --git a/src/open_inwoner/openzaak/tests/test_case_detail_redirects.py b/src/open_inwoner/openzaak/tests/test_case_detail_redirects.py new file mode 100644 index 0000000000..406fe8c2a9 --- /dev/null +++ b/src/open_inwoner/openzaak/tests/test_case_detail_redirects.py @@ -0,0 +1,65 @@ +from django.contrib.messages import get_messages +from django.test import TestCase, override_settings +from django.urls import reverse + +from open_inwoner.openzaak.tests.factories import ZGWApiGroupConfigFactory + + +@override_settings( + ROOT_URLCONF="open_inwoner.cms.tests.urls", +) +class LegacyCaseDetailUrlRedirectTest(TestCase): + def test_legacy_url_redirects_to_only_api_group_prefix(self): + sole_api_group = ZGWApiGroupConfigFactory() + + object_id = "test_object_id" + legacy_handler_url = reverse( + "cases:legacy_case_detail", + kwargs={"object_id": object_id}, + ) + target_url = reverse( + "cases:case_detail", + kwargs={"api_group_id": sole_api_group.id, "object_id": object_id}, + ) + + response = self.client.get(legacy_handler_url) + self.assertEqual(response.status_code, 302) + self.assertEqual(response["Location"], target_url) + + def test_legacy_url_redirects_to_case_list_when_multiple_api_groups(self): + ZGWApiGroupConfigFactory(), ZGWApiGroupConfigFactory() + + object_id = "test_object_id" + legacy_handler_url = reverse( + "cases:legacy_case_detail", + kwargs={"object_id": object_id}, + ) + target_url = reverse("cases:index") + + response = self.client.get(legacy_handler_url) + messages = [str(m) for m in get_messages(response.wsgi_request)] + + self.assertEqual(response.status_code, 302) + self.assertEqual(response["Location"], target_url) + self.assertEqual( + messages, + [ + "The link you clicked on has expired. Please find your case in the" + " list below." + ], + ) + + def test_legacy_url_redirect_returns_404_on_missing_api_groups(self): + object_id = "test_object_id" + legacy_handler_url = reverse( + "cases:legacy_case_detail", + kwargs={"object_id": object_id}, + ) + + legacy_handler_url = reverse( + "cases:legacy_case_detail", + kwargs={"object_id": object_id}, + ) + + response = self.client.get(legacy_handler_url) + self.assertEqual(response.status_code, 404)