Projet

Général

Profil

0002-notification-send-a-notification-to-several-users-60.patch

Nicolas Roche, 25 février 2022 12:31

Télécharger (9,77 ko)

Voir les différences:

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

 combo/apps/notifications/api_views.py | 69 ++++++++++++++++++++------
 tests/test_notification.py            | 70 +++++++++++++++++++++++++++
 2 files changed, 125 insertions(+), 14 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
18 20
from rest_framework import authentication, permissions, serializers, status
19 21
from rest_framework.generics import GenericAPIView
20 22
from rest_framework.response import Response
21 23

  
24
from combo.profile.utils import get_user_from_name_id
25

  
22 26
from .models import Notification
23 27

  
24 28

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

  
42
    def validate(self, attrs):
43
        super().validate(attrs)
44
        users = []
45
        founded = []
46
        missing = []
47
        for uuid in attrs.get('name_ids') or []:
48
            try:
49
                user = get_user_from_name_id(uuid, raise_on_missing=True)
50
            except User.DoesNotExist:
51
                missing.append(uuid)
52
            else:
53
                users.append(user)
54
                founded.append(uuid)
55
        attrs['users'] = users
56
        attrs['founded_uuids'] = founded
57
        attrs['missing_uuids'] = missing
58
        return attrs
34 59

  
35 60

  
36 61
class Add(GenericAPIView):
37 62
    permission_classes = (permissions.IsAuthenticated,)
38 63
    serializer_class = NotificationSerializer
39 64

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

  
91
        data = {}
92
        if not payload.get('name_ids'):
93
            data = {'id': notification_ids.pop()}
61 94
        else:
62
            response = {'err': 0, 'data': {'id': notification.public_id}}
63
            return Response(response)
95
            data = {
96
                'nb_notify': len(notification_ids),
97
                'notified': payload.get('founded_uuids'),
98
                'unnotified': payload.get('missing_uuids'),
99
            }
100
            if payload.get('id'):
101
                data['id'] = payload.get('id')
102
            else:
103
                data['ids'] = notification_ids
104
        return Response({'err': 0, 'data': data})
64 105

  
65 106

  
66 107
add = Add.as_view()
67 108

  
68 109

  
69 110
class Ack(GenericAPIView):
70 111
    authentication_classes = (authentication.SessionAuthentication,)
71 112
    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
......
513 514
    Notification.objects.visible(john_doe).ack()
514 515

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

  
523

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

  
529
    data = {'summary': 'foo', 'name_ids': ['00a', '00b']}
530
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
531
    assert resp.status_code == 200
532
    assert len(resp.json()['data']['ids']) == 2
533
    assert Notification.objects.count() == 2
534
    assert Notification.objects.get(user=john_doe, id=resp.json()['data']['ids'][0]).summary == 'foo'
535
    assert Notification.objects.get(user=jane_doe, id=resp.json()['data']['ids'][1]).summary == 'foo'
536
    resp.json()['data']['ids'] = ['xx', 'yy']
537
    assert resp.json() == {
538
        'err': 0,
539
        'data': {'nb_notify': 2, 'notified': ['00a', '00b'], 'unnotified': [], 'ids': ['xx', 'yy']},
540
    }
541

  
542
    # wrong username
543
    data = {'summary': 'bar', 'name_ids': ['00a', 'unknown']}
544
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
545
    assert resp.status_code == 200
546
    assert len(resp.json()['data']['ids']) == 1
547
    assert Notification.objects.count() == 3
548
    assert Notification.objects.get(user=john_doe, id=resp.json()['data']['ids'][0]).summary == 'bar'
549
    resp.json()['data']['ids'] = ['xx']
550
    assert resp.json() == {
551
        'err': 0,
552
        'data': {'nb_notify': 1, 'notified': ['00a'], 'unnotified': ['unknown'], 'ids': ['xx']},
553
    }
554

  
555

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

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

  
577
    data['summary'] = 'bar'
578
    resp = client.post(reverse('api-notification-add'), json.dumps(data), content_type='application/json')
579
    assert resp.status_code == 200
580
    assert resp.json() == {
581
        'err': 0,
582
        'data': {'nb_notify': 2, 'notified': ['00a', '00b'], 'unnotified': [], 'id': 'test:42'},
583
    }
584
    result = resp.json()
585
    assert result['err'] == 0
586
    assert result['data']['nb_notify'] == 2
587
    assert result['data']['id'] == 'test:42'
588
    assert Notification.objects.count() == 2
589
    assert Notification.objects.get(user=john_doe, external_id='test:42').summary == 'bar'
590
    assert Notification.objects.get(user=jane_doe, external_id='test:42').summary == 'bar'
521
-