From d2f56a1323927bc0044f48ef4d41e46d8974ecab 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 11:07:54 +0100
Subject: [PATCH 7/7] api: subscription patch endpoint (#61077)

---
 chrono/api/serializers.py      |   2 +-
 chrono/api/views.py            |  13 +++++
 tests/api/test_subscription.py | 103 +++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 1 deletion(-)

diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py
index d6b74ac..2fbd624 100644
--- a/chrono/api/serializers.py
+++ b/chrono/api/serializers.py
@@ -348,6 +348,6 @@ class SubscriptionSerializer(serializers.ModelSerializer):
 
     def validate(self, attrs):
         super().validate(attrs)
-        if attrs['date_start'] > attrs['date_end']:
+        if attrs.get('date_start') and attrs.get('date_end') and attrs['date_start'] > attrs['date_end']:
             raise ValidationError(_('start_datetime must be before end_datetime'))
         return attrs
diff --git a/chrono/api/views.py b/chrono/api/views.py
index fabec45..2ff38b6 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -1958,6 +1958,19 @@ class SubscriptionAPI(APIView):
         response.update({'err': 0})
         return Response(response)
 
+    def patch(self, request, *args, **kwargs):
+        serializer = self.serializer_class(self.subscription, data=request.data, partial=True)
+
+        if not serializer.is_valid():
+            raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors, err=4)
+
+        serializer.save()
+        extra_data = {k: v for k, v in request.data.items() if k not in serializer.validated_data}
+        self.subscription.extra_data.update(extra_data)
+        self.subscription.save()
+
+        return self.get(request, *args, **kwargs)
+
     def delete(self, request, *args, **kwargs):
         self.subscription.delete()
         response = {'err': 0}
diff --git a/tests/api/test_subscription.py b/tests/api/test_subscription.py
index 65d45de..93fc978 100644
--- a/tests/api/test_subscription.py
+++ b/tests/api/test_subscription.py
@@ -314,3 +314,106 @@ def test_api_delete_subscription(app, user):
     resp = app.delete('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk))
     assert resp.json['err'] == 0
     assert Subscription.objects.filter(pk=subscription.pk).exists() is False
+
+
+def test_api_patch_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),
+    )
+    other_agenda = Agenda.objects.create(label='Foo bar', kind='events')
+    other_subscription = Subscription.objects.create(
+        agenda=other_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),
+    )
+
+    resp = app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), status=401)
+
+    app.authorization = ('Basic', ('john.doe', 'password'))
+
+    resp = app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk))
+    assert resp.json == {
+        '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',
+        'foo': 'bar',
+        'err': 0,
+    }
+
+    to_test = [
+        ('user_external_id', 'yyy', 'yyy'),
+        ('user_first_name', 'fooo', 'fooo'),
+        ('user_last_name', 'baaaar', 'baaaar'),
+        ('user_email', '', ''),
+        ('user_email', 'fooo@baaaar.com', 'fooo@baaaar.com'),
+        ('user_phone_number', '', ''),
+        ('user_phone_number', '0606', '0606'),
+        ('date_start', '2021-08-31', datetime.date(2021, 8, 31)),
+        ('date_end', '2022-07-31', datetime.date(2022, 7, 31)),
+    ]
+    for key, value, db_value in to_test:
+        params = {
+            key: value,
+        }
+        resp = app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), params=params)
+        assert resp.json[key] == value
+        subscription.refresh_from_db()
+        assert getattr(subscription, key) == db_value
+
+    params = {
+        'foo': 'bar2',
+    }
+    resp = app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), params=params)
+    assert resp.json['foo'] == 'bar2'
+    subscription.refresh_from_db()
+    assert subscription.extra_data == {'foo': 'bar2'}
+    params = {
+        'bar': 'baz',
+    }
+    resp = app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), params=params)
+    assert resp.json['bar'] == 'baz'
+    subscription.refresh_from_db()
+    assert subscription.extra_data == {'foo': 'bar2', 'bar': 'baz'}
+
+    params = {
+        'date_start': 'foobar',
+        'date_end': 'foobar',
+    }
+    resp = app.patch(
+        '/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), params=params, status=400
+    )
+    assert resp.json['err_class'] == 'invalid payload'
+    assert 'wrong format' in resp.json['errors']['date_start'][0]
+    assert 'wrong format' in resp.json['errors']['date_end'][0]
+
+    params = {
+        'date_start': '2021-10-01',
+        'date_end': '2021-09-01',
+    }
+    resp = app.patch(
+        '/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), params=params, status=400
+    )
+    assert resp.json['errors']['non_field_errors'][0] == 'start_datetime must be before end_datetime'
+
+    app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, other_subscription.pk), status=404)
+    app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, 0), status=404)
+    app.patch('/api/agenda/%s/subscription/%s/' % ('unknown', subscription.pk), status=404)
+    for kind in ['meetings', 'virtual']:
+        agenda.kind = kind
+        agenda.save()
+        app.patch('/api/agenda/%s/subscription/%s/' % (agenda.slug, subscription.pk), status=404)
-- 
2.34.1

