Skip to content

Commit ec8887a

Browse files
authored
Merge pull request #260 from maykinmedia/feature/229-continue-deleting-if-a-case-fails
[#229] Continue deleting if a case fails
2 parents 21eae7d + da9459c commit ec8887a

File tree

7 files changed

+80
-78
lines changed

7 files changed

+80
-78
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class DeletionProcessingError(Exception):
2+
pass

backend/src/openarchiefbeheer/destruction/models.py

+28-16
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ def has_short_review_process(self) -> bool:
180180
[zaaktype in config.zaaktypes_short_process for zaaktype in zaaktypes_urls]
181181
)
182182

183+
def has_failures(self) -> bool:
184+
return any(
185+
[
186+
status == InternalStatus.failed
187+
for status in self.items.values_list("processing_status", flat=True)
188+
]
189+
)
190+
183191

184192
class DestructionListItem(models.Model):
185193
destruction_list = models.ForeignKey(
@@ -254,27 +262,31 @@ def set_processing_status(self, status: InternalStatus) -> None:
254262
self.processing_status = status
255263
self.save()
256264

257-
def process_deletion(self) -> None:
258-
from .utils import mark_as_failed_on_error
265+
def _delete_zaak(self):
266+
try:
267+
zaak = Zaak.objects.get(url=self.zaak)
268+
except ObjectDoesNotExist as exc:
269+
logger.error(
270+
"Could not find zaak with URL %s. Aborting deletion.", self.zaak
271+
)
272+
raise exc
259273

260-
self.processing_status = InternalStatus.processing
261-
self.save()
274+
store = ResultStore(store=self)
275+
store.clear_traceback()
262276

263-
with mark_as_failed_on_error(self):
264-
try:
265-
zaak = Zaak.objects.get(url=self.zaak)
266-
except ObjectDoesNotExist as exc:
267-
logger.error(
268-
"Could not find zaak with URL %s. Aborting deletion.", self.zaak
269-
)
270-
raise exc
277+
delete_zaak_and_related_objects(zaak=zaak, result_store=store)
271278

272-
store = ResultStore(store=self)
273-
store.clear_traceback()
279+
zaak.delete()
274280

275-
delete_zaak_and_related_objects(zaak=zaak, result_store=store)
281+
def process_deletion(self) -> None:
282+
self.processing_status = InternalStatus.processing
283+
self.save()
276284

277-
zaak.delete()
285+
try:
286+
self._delete_zaak()
287+
except Exception:
288+
self.set_processing_status(InternalStatus.failed)
289+
return
278290

279291
self.processing_status = InternalStatus.succeeded
280292
self.save()

backend/src/openarchiefbeheer/destruction/tasks.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from openarchiefbeheer.celery import app
88

99
from .constants import InternalStatus, ListItemStatus, ListStatus
10+
from .exceptions import DeletionProcessingError
1011
from .models import DestructionList, DestructionListItem, ReviewResponse
1112
from .signals import deletion_failure
1213
from .utils import notify_assignees_successful_deletion
@@ -66,11 +67,11 @@ def delete_destruction_list(destruction_list: DestructionList) -> None:
6667
chunk_tasks = delete_destruction_list_item.chunks(
6768
items_pks, settings.ZAKEN_CHUNK_SIZE
6869
)
69-
notify_task = complete_and_notify.si(destruction_list.pk)
70+
complete_and_notify_task = complete_and_notify.si(destruction_list.pk)
7071

7172
task_chain = chain(
7273
chunk_tasks.group(),
73-
notify_task,
74+
complete_and_notify_task,
7475
link_error=handle_processing_error.si(destruction_list.pk),
7576
)
7677
task_chain.delay()
@@ -99,6 +100,9 @@ def delete_destruction_list_item(pk: int) -> None:
99100
@app.task
100101
def complete_and_notify(pk: int) -> None:
101102
destruction_list = DestructionList.objects.get(pk=pk)
103+
if destruction_list.has_failures():
104+
raise DeletionProcessingError()
105+
102106
destruction_list.processing_status = InternalStatus.succeeded
103107
destruction_list.save()
104108

backend/src/openarchiefbeheer/destruction/tests/test_models.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,7 @@ def test_process_deletion_zaak_not_found(self):
8484
zaak="http://zaken.nl/api/v1/zaken/111-111-111",
8585
)
8686

87-
with self.assertRaises(ObjectDoesNotExist):
88-
item.process_deletion()
87+
item.process_deletion()
8988

9089
item.refresh_from_db()
9190

backend/src/openarchiefbeheer/destruction/tests/test_signals.py

-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ def test_failure_during_deletion_sends_signal(self):
5555
body_error_during_deletion="ERROR AAAh!",
5656
),
5757
),
58-
self.assertRaises(Exception),
5958
):
6059
delete_destruction_list(destruction_list)
6160

backend/src/openarchiefbeheer/destruction/tests/test_tasks.py

+43-47
Original file line numberDiff line numberDiff line change
@@ -241,53 +241,6 @@ def test_process_list(self):
241241
).exists()
242242
)
243243

244-
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
245-
def test_process_item_raises_error(self):
246-
destruction_list = DestructionListFactory.create(
247-
status=ListStatus.ready_to_delete
248-
)
249-
ZaakFactory.create(
250-
url="http://zaken.nl/api/v1/zaken/111-111-111",
251-
)
252-
item1 = DestructionListItemFactory.create(
253-
zaak="http://zaken.nl/api/v1/zaken/111-111-111",
254-
destruction_list=destruction_list,
255-
)
256-
ZaakFactory.create(
257-
url="http://zaken.nl/api/v1/zaken/222-222-222",
258-
)
259-
item2 = DestructionListItemFactory.create(
260-
zaak="http://zaken.nl/api/v1/zaken/222-222-222",
261-
destruction_list=destruction_list,
262-
)
263-
264-
with (
265-
patch(
266-
"openarchiefbeheer.destruction.models.delete_zaak_and_related_objects",
267-
side_effect=Exception,
268-
),
269-
self.assertRaises(Exception),
270-
):
271-
delete_destruction_list(destruction_list)
272-
273-
destruction_list.refresh_from_db()
274-
item1.refresh_from_db()
275-
item2.refresh_from_db()
276-
277-
self.assertEqual(destruction_list.processing_status, InternalStatus.failed)
278-
self.assertEqual(destruction_list.status, ListStatus.ready_to_delete)
279-
280-
# We don't know which item is processed first
281-
statuses = [item1.processing_status, item2.processing_status]
282-
self.assertIn(InternalStatus.failed, statuses)
283-
self.assertIn(InternalStatus.new, statuses)
284-
self.assertTrue(
285-
Zaak.objects.filter(url="http://zaken.nl/api/v1/zaken/111-111-111").exists()
286-
)
287-
self.assertTrue(
288-
Zaak.objects.filter(url="http://zaken.nl/api/v1/zaken/222-222-222").exists()
289-
)
290-
291244
@log_capture(level=logging.INFO)
292245
def test_item_skipped_if_already_succeeded(self, logs):
293246
item = DestructionListItemFactory.create(
@@ -369,3 +322,46 @@ def test_complete_and_notify(self):
369322

370323
self.assertEqual(list.status, ListStatus.deleted)
371324
self.assertEqual(list.processing_status, InternalStatus.succeeded)
325+
326+
@override_settings(CELERY_TASK_ALWAYS_EAGER=True)
327+
def test_other_items_processed_if_one_fails(self):
328+
destruction_list = DestructionListFactory.create(
329+
status=ListStatus.ready_to_delete
330+
)
331+
ZaakFactory.create(
332+
url="http://zaken.nl/api/v1/zaken/111-111-111",
333+
)
334+
item1 = DestructionListItemFactory.create(
335+
zaak="http://zaken.nl/api/v1/zaken/111-111-111",
336+
destruction_list=destruction_list,
337+
)
338+
ZaakFactory.create(
339+
url="http://zaken.nl/api/v1/zaken/222-222-222",
340+
)
341+
item2 = DestructionListItemFactory.create(
342+
zaak="http://zaken.nl/api/v1/zaken/222-222-222",
343+
destruction_list=destruction_list,
344+
)
345+
346+
def mock_exceptions(zaak, result_store):
347+
if zaak.url == item1.zaak:
348+
raise Exception("An errur occurred!")
349+
350+
with (
351+
patch(
352+
"openarchiefbeheer.destruction.models.delete_zaak_and_related_objects",
353+
side_effect=mock_exceptions,
354+
),
355+
):
356+
delete_destruction_list(destruction_list)
357+
358+
destruction_list.refresh_from_db()
359+
item1.refresh_from_db()
360+
item2.refresh_from_db()
361+
362+
self.assertEqual(destruction_list.processing_status, InternalStatus.failed)
363+
self.assertEqual(destruction_list.status, ListStatus.ready_to_delete)
364+
self.assertEqual(item1.processing_status, InternalStatus.failed)
365+
self.assertEqual(item2.processing_status, InternalStatus.succeeded)
366+
self.assertTrue(Zaak.objects.filter(url=item1.zaak).exists())
367+
self.assertFalse(Zaak.objects.filter(url=item2.zaak).exists())

backend/src/openarchiefbeheer/destruction/utils.py

-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from contextlib import contextmanager
21
from typing import Protocol
32

43
from django.conf import settings
@@ -118,15 +117,6 @@ class ObjectWithStatus(Protocol):
118117
def set_processing_status(self, status: InternalStatus) -> None: ...
119118

120119

121-
@contextmanager
122-
def mark_as_failed_on_error(object_with_status: ObjectWithStatus):
123-
try:
124-
yield
125-
except Exception as exc:
126-
object_with_status.set_processing_status(InternalStatus.failed)
127-
raise exc
128-
129-
130120
def process_new_assignees(
131121
destruction_list: DestructionList,
132122
assignees: list[dict],

0 commit comments

Comments
 (0)