Project

General

Profile

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

Nicolas Roche, 18 Jan 2022 07:26 PM

Download (8.92 KB)

View differences:

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

 combo/apps/notifications/api_views.py | 50 +++++++++++++++++------
 tests/test_notification.py            | 59 +++++++++++++++++++++++++++
 2 files changed, 96 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

  
22 25
from .models import Notification
23 26

  
24 27

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

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

  
35 52

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

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

  
65 89

  
66 90
add = Add.as_view()
67 91

  
68 92

  
69 93
class Ack(GenericAPIView):
70 94
    authentication_classes = (authentication.SessionAuthentication,)
71 95
    permission_classes = (permissions.IsAuthenticated,)
tests/test_notification.py
147 147

  
148 148

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

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

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

  
524

  
525
def test_notification_ws_create_many(john_doe, jane_doe):
526
    login(john_doe)
527

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

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

  
551

  
552
def test_notification_ws_update_many(john_doe, jane_doe):
553
    login(john_doe)
554

  
555
    data = {
556
        'summary': 'foo',
557
        'usernames': [john_doe.username, jane_doe.username],
558
        'id': 'test:42',
559
    }
560
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
561
    assert resp.status_code == 200
562
    result = resp.json()
563
    assert result['err'] == 0
564
    assert result['data']['nb_notify'] == 2
565
    assert result['data']['id'] == 'test:42'
566
    assert Notification.objects.count() == 2
567
    assert Notification.objects.get(user=john_doe, external_id='test:42').summary == 'foo'
568
    assert Notification.objects.get(user=jane_doe, external_id='test:42').summary == 'foo'
569

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