Project

General

Profile

0002-notification-send-notification-to-several-users-6064.patch

Nicolas Roche, 18 Jan 2022 10:46 PM

Download (9.8 KB)

View differences:

Subject: [PATCH 2/2] notification: send notification to several users (#60643)

 combo/apps/notifications/api_views.py | 52 ++++++++++++++++------
 tests/test_notification.py            | 64 +++++++++++++++++++++++++++
 2 files changed, 103 insertions(+), 13 deletions(-)
combo/apps/notifications/api_views.py
9 9
# This program is distributed in the hope that it will be useful,
10 10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 12
# GNU Affero General Public License for more details.
13 13
#
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
from django.contrib.auth.models import User
18
from django.db import transaction
17 19
from django.utils.encoding import force_text
20
from django.utils.translation import ugettext_lazy as _
18 21
from rest_framework import authentication, permissions, serializers, status
19 22
from rest_framework.generics import GenericAPIView
20 23
from rest_framework.response import Response
21 24

  
25
from combo.profile.utils import get_user_from_name_id
26

  
22 27
from .models import Notification
23 28

  
24 29

  
25 30
class NotificationSerializer(serializers.Serializer):
26 31
    summary = serializers.CharField(required=True, allow_blank=False, max_length=140)
27 32
    id = serializers.CharField(required=False, allow_null=True)
28 33
    body = serializers.CharField(allow_blank=False, default='')
29 34
    url = serializers.URLField(allow_blank=True, default='')
30 35
    origin = serializers.CharField(allow_blank=True, default='')
31 36
    start_timestamp = serializers.DateTimeField(required=False, allow_null=True)
32 37
    end_timestamp = serializers.DateTimeField(required=False, allow_null=True)
33 38
    duration = serializers.IntegerField(required=False, allow_null=True, min_value=0)
39
    name_ids = serializers.ListField(
40
        required=False, child=serializers.CharField(required=True, max_length=150)
41
    )
42

  
43
    def validate(self, attrs):
44
        super().validate(attrs)
45
        users = []
46
        for uuid in attrs.get('name_ids') or []:
47
            try:
48
                users.append(get_user_from_name_id(uuid, raise_on_missing=True))
49
            except User.DoesNotExist:
50
                raise serializers.ValidationError({'users': _('"%s" user does not exist.') % uuid})
51
        attrs['users'] = users
52
        return attrs
34 53

  
35 54

  
36 55
class Add(GenericAPIView):
37 56
    permission_classes = (permissions.IsAuthenticated,)
38 57
    serializer_class = NotificationSerializer
39 58

  
40 59
    def post(self, request, *args, **kwargs):
41 60
        serializer = self.get_serializer(data=request.data)
42 61
        if not serializer.is_valid():
43 62
            response = {'err': 1, 'err_desc': serializer.errors}
44 63
            return Response(response, status.HTTP_400_BAD_REQUEST)
45 64
        data = serializer.validated_data
65
        notification_ids = []
46 66
        try:
47
            notification = Notification.notify(
48
                user=request.user,
49
                summary=data['summary'],
50
                id=data.get('id'),
51
                body=data.get('body'),
52
                url=data.get('url'),
53
                origin=data.get('origin'),
54
                start_timestamp=data.get('start_timestamp'),
55
                end_timestamp=data.get('end_timestamp'),
56
                duration=data.get('duration'),
57
            )
67
            with transaction.atomic():
68
                for user in data.get('users') or [request.user]:
69
                    notification = Notification.notify(
70
                        user=user,
71
                        summary=data['summary'],
72
                        id=data.get('id'),
73
                        body=data.get('body'),
74
                        url=data.get('url'),
75
                        origin=data.get('origin'),
76
                        start_timestamp=data.get('start_timestamp'),
77
                        end_timestamp=data.get('end_timestamp'),
78
                        duration=data.get('duration'),
79
                    )
80
                    notification_ids.append(notification.public_id)
58 81
        except ValueError as e:
59 82
            response = {'err': 1, 'err_desc': {'id': [force_text(e)]}}
60 83
            return Response(response, status.HTTP_400_BAD_REQUEST)
84
        uniq_ids = set(notification_ids)
85
        if len(uniq_ids) == 1:
86
            response = {'err': 0, 'data': {'nb_notify': len(notification_ids), 'id': uniq_ids.pop()}}
61 87
        else:
62
            response = {'err': 0, 'data': {'id': notification.public_id}}
63
            return Response(response)
88
            response = {'err': 0, 'data': {'nb_notify': len(notification_ids), 'ids': notification_ids}}
89
        return Response(response)
64 90

  
65 91

  
66 92
add = Add.as_view()
67 93

  
68 94

  
69 95
class Ack(GenericAPIView):
70 96
    authentication_classes = (authentication.SessionAuthentication,)
71 97
    permission_classes = (permissions.IsAuthenticated,)
tests/test_notification.py
5 5

  
6 6
import pytest
7 7
from django.contrib.auth.models import User
8 8
from django.test import Client
9 9
from django.test.client import RequestFactory
10 10
from django.urls import reverse
11 11
from django.utils.encoding import force_text
12 12
from django.utils.timezone import now
13
from mellon.models import UserSAMLIdentifier
13 14

  
14 15
from combo.apps.lingo.models import ActiveItems, PaymentBackend, Regie
15 16
from combo.apps.notifications.models import Notification, NotificationsCell
16 17
from combo.data.models import Page
17 18

  
18 19
from .test_manager import login as login_app
19 20

  
20 21
pytestmark = pytest.mark.django_db
......
147 148

  
148 149

  
149 150
def test_notification_ws(john_doe):
150 151
    def notify(data, count, check_id=None):
151 152
        resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
152 153
        assert resp.status_code == 200
153 154
        result = json.loads(force_text(resp.content))
154 155
        assert result['err'] == 0
156
        assert result['data']['nb_notify'] == 1
157
        assert 'id' in result['data']
155 158
        if 'id' in data:
156 159
            assert check_id is not None and result['data']['id'] == check_id
157 160
        assert Notification.objects.filter(user=john_doe).count() == count
158 161
        return Notification.objects.filter(user=john_doe).order_by('id').last()
159 162

  
160 163
    login(john_doe)
161 164
    notify({'summary': 'foo'}, 1)
162 165
    notify({'summary': 'bar'}, 2)
......
513 516
    Notification.objects.visible(john_doe).ack()
514 517

  
515 518
    # acking a notification without and end_timestamp, still visible
516 519
    freezer.move_to(start + timedelta(days=365, seconds=1))
517 520
    content = cell.render(context)
518 521
    assert Notification.objects.visible(john_doe).count() == 1
519 522
    assert 'notibar' in content
520 523
    assert 'notifoo' not in content
524

  
525

  
526
def test_notification_ws_create_many(john_doe, jane_doe):
527
    UserSAMLIdentifier.objects.create(user=john_doe, name_id='00a')
528
    UserSAMLIdentifier.objects.create(user=jane_doe, name_id='00b')
529
    login(john_doe)
530

  
531
    data = {'summary': 'foo', 'name_ids': ['00a', '00b']}
532
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
533
    assert resp.status_code == 200
534
    result = resp.json()
535
    assert result['err'] == 0
536
    assert result['data']['nb_notify'] == 2
537
    assert len(result['data']['ids']) == 2
538
    assert Notification.objects.count() == 2
539
    notif = Notification.objects.filter(user=john_doe).last()
540
    assert notif.public_id == result['data']['ids'][0]
541
    assert notif.summary == 'foo'
542
    notif = Notification.objects.filter(user=jane_doe).last()
543
    assert notif.public_id == result['data']['ids'][1]
544
    assert notif.summary == 'foo'
545

  
546
    # wrong username
547
    data = {'summary': 'foo', 'name_ids': ['00a', 'unknown']}
548
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
549
    assert resp.status_code == 400
550
    result = resp.json()
551
    assert result['err_desc'] == {'users': ['"unknown" user does not exist.']}
552
    assert Notification.objects.count() == 2
553

  
554

  
555
def test_notification_ws_update_many(john_doe, jane_doe):
556
    UserSAMLIdentifier.objects.create(user=john_doe, name_id='00a')
557
    UserSAMLIdentifier.objects.create(user=jane_doe, name_id='00b')
558
    login(john_doe)
559

  
560
    data = {
561
        'summary': 'foo',
562
        'name_ids': ['00a', '00b'],
563
        'id': 'test:42',
564
    }
565
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
566
    assert resp.status_code == 200
567
    result = resp.json()
568
    assert result['err'] == 0
569
    assert result['data']['nb_notify'] == 2
570
    assert result['data']['id'] == 'test:42'
571
    assert Notification.objects.count() == 2
572
    assert Notification.objects.get(user=john_doe, external_id='test:42').summary == 'foo'
573
    assert Notification.objects.get(user=jane_doe, external_id='test:42').summary == 'foo'
574

  
575
    data['summary'] = 'bar'
576
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
577
    assert resp.status_code == 200
578
    result = resp.json()
579
    assert result['err'] == 0
580
    assert result['data']['nb_notify'] == 2
581
    assert result['data']['id'] == 'test:42'
582
    assert Notification.objects.count() == 2
583
    assert Notification.objects.get(user=john_doe, external_id='test:42').summary == 'bar'
584
    assert Notification.objects.get(user=jane_doe, external_id='test:42').summary == 'bar'
521
-