Skip to content

Commit 2e1c162

Browse files
Maffoochmtesauro
andauthored
Notifications: Clean up duplicate system notification entries (#14488)
* Add unique constraint for system notifications and deduplicate existing entries * Handle multiple system notifications gracefully by logging a warning and deleting duplicates * Add unique constraint for notifications to prevent duplicates * Remove unique constraint for system notifications to simplify notification handling * Extra logging statement leads to assertion error --------- Co-authored-by: Matt Tesauro <mtesauro@gmail.com>
1 parent 77887b0 commit 2e1c162

4 files changed

Lines changed: 36 additions & 20 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0261_remove_url_insert_insert_remove_url_update_update_and_more
1+
0261_remove_url_insert_insert_remove_url_update_update_and_more

dojo/notifications/helper.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,25 @@ def __init__(
118118
def _get_notifications_object(self) -> Notifications:
119119
"""Set the system Notifications object on the class."""
120120
try:
121-
return Notifications.objects.get(user=None, template=False)
122-
except Exception:
123-
return Notifications()
121+
notifications, _ = Notifications.objects.get_or_create(
122+
user=None, product=None, template=False,
123+
)
124+
except Notifications.MultipleObjectsReturned:
125+
notifications = Notifications.objects.filter(
126+
user=None,
127+
product=None,
128+
template=False,
129+
).first()
130+
logger.warning(
131+
"Multiple system notifications objects found, using the first one with id %s. Cleaning up the duplicate...",
132+
notifications.id,
133+
)
134+
Notifications.objects.filter(
135+
user=None,
136+
product=None,
137+
template=False,
138+
).exclude(id=notifications.id).delete()
139+
return notifications
124140

125141
def _get_system_settings(self) -> System_Settings:
126142
"""Set the system settings object in the class."""

dojo/notifications/views.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import requests
44
from django.contrib import messages
55
from django.core.exceptions import PermissionDenied
6+
from django.db import transaction
67
from django.http import Http404, HttpRequest, HttpResponseRedirect
78
from django.shortcuts import get_object_or_404, render
89
from django.urls import reverse
@@ -19,11 +20,10 @@
1920

2021
class SystemNotificationsView(View):
2122
def get_notifications(self, request: HttpRequest):
22-
try:
23-
notifications = Notifications.objects.get(user=None, product__isnull=True, template=False)
24-
except Notifications.DoesNotExist:
25-
notifications = Notifications(user=None, template=False)
26-
23+
with transaction.atomic():
24+
notifications, _ = Notifications.objects.get_or_create(
25+
user=None, product=None, template=False,
26+
)
2727
return notifications
2828

2929
def check_user_permissions(self, request: HttpRequest):
@@ -97,10 +97,10 @@ def post(self, request: HttpRequest):
9797

9898
class PersonalNotificationsView(SystemNotificationsView):
9999
def get_notifications(self, request: HttpRequest):
100-
try:
101-
notifications = Notifications.objects.get(user=request.user, product__isnull=True)
102-
except Notifications.DoesNotExist:
103-
notifications = Notifications(user=request.user)
100+
with transaction.atomic():
101+
notifications, _ = Notifications.objects.get_or_create(
102+
user=request.user, product=None, template=False,
103+
)
104104
return notifications
105105

106106
def check_user_permissions(self, request: HttpRequest):
@@ -116,10 +116,10 @@ def set_breadcrumbs(self, request: HttpRequest):
116116

117117
class TemplateNotificationsView(SystemNotificationsView):
118118
def get_notifications(self, request: HttpRequest):
119-
try:
120-
notifications = Notifications.objects.get(template=True)
121-
except Notifications.DoesNotExist:
122-
notifications = Notifications(user=None, template=True)
119+
with transaction.atomic():
120+
notifications, _ = Notifications.objects.get_or_create(
121+
user=None, product=None, template=True,
122+
)
123123
return notifications
124124

125125
def get_scope(self):

unittests/test_notifications.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -578,23 +578,23 @@ def test_missing_system_webhook(self):
578578
with self.assertLogs("dojo.notifications.helper", level="INFO") as cm:
579579
manager = WebhookNotificationManger()
580580
manager.send_webhooks_notification(event="dummy")
581-
self.assertIn("URLs for Webhooks not configured: skipping system notification", cm.output[0])
581+
self.assertIn("URLs for Webhooks not configured: skipping system notification", cm.output[1])
582582

583583
def test_missing_personal_webhook(self):
584584
# test data contains 2 entries but we need to test missing definition
585585
Notification_Webhooks.objects.all().delete()
586586
with self.assertLogs("dojo.notifications.helper", level="INFO") as cm:
587587
manager = WebhookNotificationManger()
588588
manager.send_webhooks_notification(event="dummy", user=Dojo_User.objects.get(username="admin"))
589-
self.assertIn("URLs for Webhooks not configured for user '(admin)': skipping user notification", cm.output[0])
589+
self.assertIn("URLs for Webhooks not configured for user '(admin)': skipping user notification", cm.output[1])
590590

591591
def test_system_webhook_inactive(self):
592592
self.sys_wh.status = Notification_Webhooks.Status.STATUS_INACTIVE_PERMANENT
593593
self.sys_wh.save()
594594
with self.assertLogs("dojo.notifications.helper", level="INFO") as cm:
595595
manager = WebhookNotificationManger()
596596
manager.send_webhooks_notification(event="dummy")
597-
self.assertIn("URL for Webhook 'My webhook endpoint' is not active: Permanently inactive (inactive_permanent)", cm.output[0])
597+
self.assertIn("URL for Webhook 'My webhook endpoint' is not active: Permanently inactive (inactive_permanent)", cm.output[1])
598598

599599
def test_system_webhook_sucessful(self):
600600
with self.assertLogs("dojo.notifications.helper", level="DEBUG") as cm:

0 commit comments

Comments
 (0)