From 43a73c0e33c85c59de12590da08b53526cb5bfbb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 8 Feb 2022 09:15:47 +0100
Subject: [PATCH 1/2] api: flat extra_data in booking patch endpoint (#61074)

---
 chrono/api/serializers.py |  2 +-
 chrono/api/views.py       | 16 +++++++++++-----
 tests/api/test_booking.py | 18 +++++++++++-------
 3 files changed, 23 insertions(+), 13 deletions(-)

diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py
index c2339ec..d6f3cd9 100644
--- a/chrono/api/serializers.py
+++ b/chrono/api/serializers.py
@@ -143,7 +143,7 @@ class BookingSerializer(serializers.ModelSerializer):
     class Meta:
         model = Booking
         fields = ['id', 'in_waiting_list', 'user_was_present', 'user_absence_reason', 'extra_data']
-        read_only_fields = ['id', 'in_waiting_list']
+        read_only_fields = ['id', 'in_waiting_list', 'extra_data']
 
     def validate_user_absence_reason(self, value):
         if not value:
diff --git a/chrono/api/views.py b/chrono/api/views.py
index 02f3573..0578a16 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -2089,10 +2089,16 @@ class BookingAPI(APIView):
         ):
             raise APIErrorBadRequest(N_('event is marked as checked'), err=5)
 
-        if 'extra_data' in serializer.validated_data:
-            extra_data = self.booking.extra_data or {}
-            extra_data.update(serializer.validated_data['extra_data'] or {})
-            serializer.validated_data['extra_data'] = extra_data
+        extra_data = {}
+        if 'extra_data' in request.data:
+            # legacy format
+            extra_data = request.data['extra_data'] or {}
+        else:
+            extra_data = {k: v for k, v in request.data.items() if k not in serializer.validated_data}
+        if extra_data:
+            self.booking.extra_data = self.booking.extra_data or {}
+            self.booking.extra_data.update(extra_data)
+            self.booking.save()
 
         serializer.save()
         if 'user_was_present' in request.data:
@@ -2100,7 +2106,7 @@ class BookingAPI(APIView):
             self.booking.event.set_is_checked()
         if 'user_absence_reason' in request.data:
             self.booking.secondary_booking_set.update(user_absence_reason=self.booking.user_absence_reason)
-        if 'extra_data' in request.data:
+        if extra_data:
             self.booking.secondary_booking_set.update(extra_data=self.booking.extra_data)
 
         response = {'err': 0, 'booking_id': self.booking.pk}
diff --git a/tests/api/test_booking.py b/tests/api/test_booking.py
index 25802ae..a472aeb 100644
--- a/tests/api/test_booking.py
+++ b/tests/api/test_booking.py
@@ -638,22 +638,22 @@ def test_booking_patch_api_extra_data(app, user):
 
     app.authorization = ('Basic', ('john.doe', 'password'))
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foo': 'bar'}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foo': 'bar'}})  # legacy
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': 'bar'}
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foo': 'baz'}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'foo': 'baz'})
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': 'baz'}
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foooo': 'bar'}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'foooo': 'bar'})
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': 'baz', 'foooo': 'bar'}
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foooo': 'baz', 'foo': None}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'foooo': 'baz', 'foo': None})
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': None, 'foooo': 'baz'}
@@ -664,7 +664,7 @@ def test_booking_patch_api_extra_data(app, user):
     # and other booking
     other_booking = Booking.objects.create(event=event)
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {'foooo': 'baz', 'foo': None}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'foooo': 'baz', 'foo': None})
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': None, 'foooo': 'baz'}
     # all secondary bookings are upadted
@@ -679,11 +679,15 @@ def test_booking_patch_api_extra_data(app, user):
     other_booking.refresh_from_db()
     assert other_booking.extra_data is None  # not changed
 
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': None})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={})
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': None, 'foooo': 'baz'}  # not changed
-    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {}})
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': None})  # legacy
+    booking.refresh_from_db()
+    assert booking.user_was_present is True  # not changed
+    assert booking.extra_data == {'foo': None, 'foooo': 'baz'}  # not changed
+    app.patch_json('/api/booking/%s/' % booking.pk, params={'extra_data': {}})  # legacy
     booking.refresh_from_db()
     assert booking.user_was_present is True  # not changed
     assert booking.extra_data == {'foo': None, 'foooo': 'baz'}  # not changed
-- 
2.34.1

