From 6551ddb4b117a7dbbed878e576eba4bf9f83296d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 27 Jan 2022 09:55:42 +0100
Subject: [PATCH 4/4] api: list subscriptions endpoint (#61079)

---
 chrono/api/serializers.py      |   3 +
 chrono/api/views.py            |  38 +++++++++-
 tests/api/test_subscription.py | 127 +++++++++++++++++++++++++++++++++
 3 files changed, 165 insertions(+), 3 deletions(-)

diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py
index ef02869..b4dc8ac 100644
--- a/chrono/api/serializers.py
+++ b/chrono/api/serializers.py
@@ -335,6 +335,7 @@ class SubscriptionSerializer(serializers.ModelSerializer):
     class Meta:
         model = Subscription
         fields = [
+            'id',
             'user_external_id',
             'user_first_name',
             'user_last_name',
@@ -342,7 +343,9 @@ class SubscriptionSerializer(serializers.ModelSerializer):
             'user_phone_number',
             'date_start',
             'date_end',
+            'extra_data',
         ]
+        read_only_fields = ['id', 'extra_data']
 
     def validate(self, attrs):
         super().validate(attrs)
diff --git a/chrono/api/views.py b/chrono/api/views.py
index 8eb236d..581566e 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -1880,12 +1880,44 @@ class MultipleAgendasEventsFillslots(EventsFillslots):
 agendas_events_fillslots = MultipleAgendasEventsFillslots.as_view()
 
 
-class SubscriptionAPI(APIView):
+class SubscriptionFilter(filters.FilterSet):
+    date_start = filters.DateFilter(lookup_expr='gte')
+    date_end = filters.DateFilter(lookup_expr='lte')
+
+    class Meta:
+        model = Subscription
+        fields = [
+            'user_external_id',
+            'date_start',
+            'date_end',
+        ]
+
+
+class SubscriptionAPI(ListAPIView):
+    filter_backends = (filters.DjangoFilterBackend,)
     serializer_class = serializers.SubscriptionSerializer
+    filterset_class = SubscriptionFilter
     permission_classes = (permissions.IsAuthenticated,)
 
+    def get_agenda(self, agenda_identifier):
+        return get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
+
+    def get(self, request, agenda_identifier):
+        self.agenda = self.get_agenda(agenda_identifier)
+
+        try:
+            subscriptions = self.filter_queryset(self.get_queryset())
+        except ValidationError as e:
+            raise APIErrorBadRequest(N_('invalid filters'), errors=e.detail)
+
+        serializer = self.serializer_class(subscriptions, many=True)
+        return Response({'err': 0, 'data': serializer.data})
+
+    def get_queryset(self):
+        return self.agenda.subscriptions.order_by('date_start', 'date_end', 'user_external_id', 'pk')
+
     def post(self, request, agenda_identifier):
-        agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
+        self.agenda = self.get_agenda(agenda_identifier)
 
         serializer = self.serializer_class(data=request.data)
         if not serializer.is_valid():
@@ -1893,7 +1925,7 @@ class SubscriptionAPI(APIView):
         extra_data = {k: v for k, v in request.data.items() if k not in serializer.validated_data}
 
         subscription = Subscription.objects.create(
-            agenda=agenda, extra_data=extra_data, **serializer.validated_data
+            agenda=self.agenda, extra_data=extra_data, **serializer.validated_data
         )
         return Response({'err': 0, 'id': subscription.pk})
 
diff --git a/tests/api/test_subscription.py b/tests/api/test_subscription.py
index e694eb1..63551e5 100644
--- a/tests/api/test_subscription.py
+++ b/tests/api/test_subscription.py
@@ -7,6 +7,133 @@ from chrono.agendas.models import Agenda, Subscription
 pytestmark = pytest.mark.django_db
 
 
+def test_api_list_subscription(app, user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='events')
+    subscription = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        user_first_name='Foo',
+        user_last_name='BAR',
+        user_email='foo@bar.com',
+        user_phone_number='06',
+        extra_data={'foo': 'bar'},
+        date_start=datetime.date(year=2021, month=9, day=1),
+        date_end=datetime.date(year=2021, month=10, day=1),
+    )
+
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, status=401)
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug)
+    assert len(resp.json['data']) == 1
+    assert resp.json['data'][0] == {
+        'id': subscription.pk,
+        'user_external_id': 'xxx',
+        'user_first_name': 'Foo',
+        'user_last_name': 'BAR',
+        'user_email': 'foo@bar.com',
+        'user_phone_number': '06',
+        'date_start': '2021-09-01',
+        'date_end': '2021-10-01',
+        'extra_data': {
+            'foo': 'bar',
+        },
+    }
+
+
+def test_api_list_subscription_filter_user_external_id(app, user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='events')
+    subscription1 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2021, month=9, day=1),
+        date_end=datetime.date(year=2021, month=10, day=1),
+    )
+    subscription2 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='yyy',
+        date_start=datetime.date(year=2021, month=9, day=1),
+        date_end=datetime.date(year=2021, month=10, day=1),
+    )
+    other_agenda = Agenda.objects.create(label='Foo bar 2', kind='events')
+    Subscription.objects.create(
+        agenda=other_agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2000, month=1, day=1),
+        date_end=datetime.date(year=2099, month=12, day=31),
+    )
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'user_external_id': 'xxx'})
+    assert [d['id'] for d in resp.json['data']] == [subscription1.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'user_external_id': 'yyy'})
+    assert [d['id'] for d in resp.json['data']] == [subscription2.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'user_external_id': 'zzz'})
+    assert [d['id'] for d in resp.json['data']] == []
+
+
+def test_api_list_subscription_filter_date_start(app, user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='events')
+    subscription1 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2021, month=9, day=1),
+        date_end=datetime.date(year=2021, month=10, day=1),
+    )
+    subscription2 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2022, month=9, day=1),
+        date_end=datetime.date(year=2022, month=10, day=1),
+    )
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2021-08-31'})
+    assert [d['id'] for d in resp.json['data']] == [subscription1.pk, subscription2.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2021-09-02'})
+    assert [d['id'] for d in resp.json['data']] == [subscription2.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': '2022-09-02'})
+    assert [d['id'] for d in resp.json['data']] == []
+
+    resp = app.get(
+        '/api/agenda/%s/subscription/' % agenda.slug, params={'date_start': 'wrong-format'}, status=400
+    )
+    assert resp.json['err_class'] == 'invalid filters'
+
+
+def test_api_list_subscription_filter_date_end(app, user):
+    agenda = Agenda.objects.create(label='Foo bar', kind='events')
+    subscription1 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2021, month=9, day=1),
+        date_end=datetime.date(year=2021, month=10, day=1),
+    )
+    subscription2 = Subscription.objects.create(
+        agenda=agenda,
+        user_external_id='xxx',
+        date_start=datetime.date(year=2022, month=9, day=1),
+        date_end=datetime.date(year=2022, month=10, day=1),
+    )
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2022-10-02'})
+    assert [d['id'] for d in resp.json['data']] == [subscription1.pk, subscription2.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2021-10-02'})
+    assert [d['id'] for d in resp.json['data']] == [subscription1.pk]
+    resp = app.get('/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': '2021-09-30'})
+    assert [d['id'] for d in resp.json['data']] == []
+
+    resp = app.get(
+        '/api/agenda/%s/subscription/' % agenda.slug, params={'date_end': 'wrong-format'}, status=400
+    )
+    assert resp.json['err_class'] == 'invalid filters'
+
+
 def test_api_create_subscription(app, user):
     agenda = Agenda.objects.create(label='Foo bar', kind='events')
 
-- 
2.34.1

