Skip to content

Commit d6aa24b

Browse files
authored
Alter usage of message extends (#2567)
* Alter usage of message extends Instead of using the messages.add method directly, add message for the message notification backend using the storage adapter directly. This also removes the limitation on the storage backend that requires a request.user is authenticated, as we are likely going to be mostly adding messages through celery or other request-less mechanisms. * Fix tests * Make sure we can't add messages for anonymous users and fix tests
1 parent 38e73cd commit d6aa24b

File tree

3 files changed

+67
-14
lines changed

3 files changed

+67
-14
lines changed

readthedocs/notifications/backends.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Notification to message backends"""
22

33
from django.conf import settings
4+
from django.http import HttpRequest
45
from django.utils.module_loading import import_string
56
from messages_extends.constants import INFO_PERSISTENT
67
from messages_extends import add_message
@@ -67,13 +68,21 @@ class SiteBackend(Backend):
6768
name = 'site'
6869

6970
def send(self, notification):
70-
add_message(
71-
request=self.request,
71+
# Instead of calling the standard messages.add method, this instead
72+
# manipulates the storage directly. This is because we don't have a
73+
# request object and need to mock one out to fool the message storage
74+
# into saving a message for a separate user.
75+
cls_name = settings.MESSAGE_STORAGE
76+
cls = import_string(cls_name)
77+
req = HttpRequest()
78+
setattr(req, 'session', '')
79+
storage = cls(req)
80+
storage.add(
81+
level=LEVEL_MAPPING.get(notification.level, INFO_PERSISTENT),
7282
message=notification.render(
7383
backend_name=self.name,
7484
source_format=HTML
7585
),
76-
level=LEVEL_MAPPING.get(notification.level, INFO_PERSISTENT),
7786
extra_tags='',
7887
user=notification.user,
7988
)

readthedocs/notifications/storages.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ def _get(self, *args, **kwargs):
4545
return safe_messages, all_ret
4646

4747
def add(self, level, message, extra_tags='', *args, **kwargs):
48-
if self.request.user.is_authenticated():
48+
user = kwargs.get('user') or self.request.user
49+
if not user.is_anonymous():
4950
persist_messages = (PersistentMessage.objects
5051
.filter(message=message,
51-
user=self.request.user,
52+
user=user,
5253
read=False))
5354
if persist_messages.exists():
5455
return

readthedocs/rtd_tests/tests/test_notifications.py

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
import django_dynamic_fixture as fixture
55
from django.test import TestCase
66
from django.test.utils import override_settings
7-
from django.contrib.auth.models import User
7+
from django.contrib.auth.models import User, AnonymousUser
8+
from messages_extends.models import Message as PersistentMessage
89

910
from readthedocs.notifications import Notification
1011
from readthedocs.notifications.backends import EmailBackend, SiteBackend
12+
from readthedocs.notifications.constants import ERROR
1113
from readthedocs.builds.models import Build
1214

1315

@@ -22,6 +24,8 @@
2224
class NotificationTests(TestCase):
2325

2426
def test_notification_custom(self, send, render_to_string):
27+
render_to_string.return_value = 'Test'
28+
2529
class TestNotification(Notification):
2630
name = 'foo'
2731
subject = 'This is {{ foo.id }}'
@@ -54,23 +58,36 @@ class NotificationBackendTests(TestCase):
5458

5559
@mock.patch('readthedocs.notifications.backends.send_email')
5660
def test_email_backend(self, send_email, render_to_string):
61+
render_to_string.return_value = 'Test'
62+
5763
class TestNotification(Notification):
5864
name = 'foo'
5965
subject = 'This is {{ foo.id }}'
6066
context_object_name = 'foo'
67+
level = ERROR
6168

6269
build = fixture.get(Build)
6370
req = mock.MagicMock()
64-
notify = TestNotification(object=build, request=req)
71+
user = fixture.get(User)
72+
notify = TestNotification(object=build, request=req, user=user)
6573
backend = EmailBackend(request=req)
6674
backend.send(notify)
6775

76+
self.assertEqual(render_to_string.call_count, 1)
6877
send_email.assert_has_calls([
69-
mock.call(level=21, request=req, message=mock.ANY, extra_tags='')
78+
mock.call(
79+
request=mock.ANY,
80+
template='core/email/common.txt',
81+
context={'content': 'Test'},
82+
subject=u'This is 1',
83+
template_html='core/email/common.html',
84+
recipient=user.email,
85+
)
7086
])
7187

72-
@mock.patch('readthedocs.notifications.backends.add_message')
73-
def test_email_backend(self, add_message, render_to_string):
88+
def test_message_backend(self, render_to_string):
89+
render_to_string.return_value = 'Test'
90+
7491
class TestNotification(Notification):
7592
name = 'foo'
7693
subject = 'This is {{ foo.id }}'
@@ -83,7 +100,33 @@ class TestNotification(Notification):
83100
backend = SiteBackend(request=req)
84101
backend.send(notify)
85102

86-
add_message.assert_has_calls([
87-
mock.call(level=21, request=req, message=mock.ANY, extra_tags='',
88-
user=user)
89-
])
103+
self.assertEqual(render_to_string.call_count, 1)
104+
self.assertEqual(PersistentMessage.objects.count(), 1)
105+
106+
message = PersistentMessage.objects.first()
107+
self.assertEqual(message.user, user)
108+
109+
def test_message_anonymous_user(self, render_to_string):
110+
"""Anonymous user still throwns exception on persistent messages"""
111+
render_to_string.return_value = 'Test'
112+
113+
class TestNotification(Notification):
114+
name = 'foo'
115+
subject = 'This is {{ foo.id }}'
116+
context_object_name = 'foo'
117+
118+
build = fixture.get(Build)
119+
user = AnonymousUser()
120+
req = mock.MagicMock()
121+
notify = TestNotification(object=build, request=req, user=user)
122+
backend = SiteBackend(request=req)
123+
124+
self.assertEqual(PersistentMessage.objects.count(), 0)
125+
126+
# We should never be adding persistent messages for anonymous users.
127+
# Make sure message_extends sitll throws an exception here
128+
with self.assertRaises(NotImplementedError):
129+
backend.send(notify)
130+
131+
self.assertEqual(render_to_string.call_count, 1)
132+
self.assertEqual(PersistentMessage.objects.count(), 0)

0 commit comments

Comments
 (0)