From 2e091220f50c81629c8f4aaae3ac837a8871c2ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 19 May 2020 17:53:34 +0200
Subject: [PATCH] api: use APIError in fillslots view (#43077)

---
 chrono/api/views.py | 153 ++++++++++++++++++--------------------------
 1 file changed, 64 insertions(+), 89 deletions(-)

diff --git a/chrono/api/views.py b/chrono/api/views.py
index 8abb585..7d20ca6 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -692,7 +692,10 @@ class Fillslots(APIView):
     serializer_class = SlotsSerializer
 
     def post(self, request, agenda_identifier=None, event_identifier=None, format=None):
-        return self.fillslot(request=request, agenda_identifier=agenda_identifier, format=format)
+        try:
+            return self.fillslot(request=request, agenda_identifier=agenda_identifier, format=format)
+        except APIError as e:
+            return e.to_response()
 
     def fillslot(self, request, agenda_identifier=None, slots=[], format=None):
         multiple_booking = bool(not slots)
@@ -707,27 +710,21 @@ class Fillslots(APIView):
 
         serializer = self.serializer_class(data=request.data, partial=True)
         if not serializer.is_valid():
-            return Response(
-                {
-                    'err': 1,
-                    'err_class': 'invalid payload',
-                    'err_desc': _('invalid payload'),
-                    'errors': serializer.errors,
-                },
-                status=status.HTTP_400_BAD_REQUEST,
+            raise APIError(
+                _('invalid payload'),
+                err_class='invalid payload',
+                errors=serializer.errors,
+                http_status=status.HTTP_400_BAD_REQUEST,
             )
         payload = serializer.validated_data
 
         if 'slots' in payload:
             slots = payload['slots']
         if not slots:
-            return Response(
-                {
-                    'err': 1,
-                    'err_class': 'slots list cannot be empty',
-                    'err_desc': _('slots list cannot be empty'),
-                },
-                status=status.HTTP_400_BAD_REQUEST,
+            raise APIError(
+                _('slots list cannot be empty'),
+                err_class='slots list cannot be empty',
+                http_status=status.HTTP_400_BAD_REQUEST,
             )
 
         if 'count' in payload:
@@ -737,25 +734,19 @@ class Fillslots(APIView):
             try:
                 places_count = int(request.query_params['count'])
             except ValueError:
-                return Response(
-                    {
-                        'err': 1,
-                        'err_class': 'invalid value for count (%s)' % request.query_params['count'],
-                        'err_desc': _('invalid value for count (%s)') % request.query_params['count'],
-                    },
-                    status=status.HTTP_400_BAD_REQUEST,
+                raise APIError(
+                    _('invalid value for count (%s)') % request.query_params['count'],
+                    err_class='invalid value for count (%s)' % request.query_params['count'],
+                    http_status=status.HTTP_400_BAD_REQUEST,
                 )
         else:
             places_count = 1
 
         if places_count <= 0:
-            return Response(
-                {
-                    'err': 1,
-                    'err_class': 'count cannot be less than or equal to zero',
-                    'err_desc': _('count cannot be less than or equal to zero'),
-                },
-                status=status.HTTP_400_BAD_REQUEST,
+            raise APIError(
+                _('count cannot be less than or equal to zero'),
+                err_class='count cannot be less than or equal to zero',
+                http_status=status.HTTP_400_BAD_REQUEST,
             )
 
         to_cancel_booking = None
@@ -764,13 +755,10 @@ class Fillslots(APIView):
             try:
                 cancel_booking_id = int(payload.get('cancel_booking_id'))
             except (ValueError, TypeError):
-                return Response(
-                    {
-                        'err': 1,
-                        'err_class': 'cancel_booking_id is not an integer',
-                        'err_desc': _('cancel_booking_id is not an integer'),
-                    },
-                    status=status.HTTP_400_BAD_REQUEST,
+                raise APIError(
+                    _('cancel_booking_id is not an integer'),
+                    err_class='cancel_booking_id is not an integer',
+                    http_status=status.HTTP_400_BAD_REQUEST,
                 )
 
         if cancel_booking_id is not None:
@@ -790,7 +778,7 @@ class Fillslots(APIView):
                 cancel_error = gettext_noop('cancel booking: booking does no exist')
 
             if cancel_error:
-                return Response({'err': 1, 'err_class': cancel_error, 'err_desc': _(cancel_error)})
+                raise APIError(_(cancel_error), err_class=cancel_error)
 
         extra_data = {}
         for k, v in request.data.items():
@@ -808,24 +796,16 @@ class Fillslots(APIView):
                 try:
                     meeting_type_id_, datetime_str = slot.split(':')
                 except ValueError:
-                    return Response(
-                        {
-                            'err': 1,
-                            'err_class': 'invalid slot: %s' % slot,
-                            'err_desc': _('invalid slot: %s') % slot,
-                        },
-                        status=status.HTTP_400_BAD_REQUEST,
+                    raise APIError(
+                        _('invalid slot: %s') % slot,
+                        err_class='invalid slot: %s' % slot,
+                        http_status=status.HTTP_400_BAD_REQUEST,
                     )
                 if meeting_type_id_ != meeting_type_id:
-                    return Response(
-                        {
-                            'err': 1,
-                            'err_class': 'all slots must have the same meeting type id (%s)'
-                            % meeting_type_id,
-                            'err_desc': _('all slots must have the same meeting type id (%s)')
-                            % meeting_type_id,
-                        },
-                        status=status.HTTP_400_BAD_REQUEST,
+                    raise APIError(
+                        _('all slots must have the same meeting type id (%s)') % meeting_type_id,
+                        err_class='all slots must have the same meeting type id (%s)' % meeting_type_id,
+                        http_status=status.HTTP_400_BAD_REQUEST,
                     )
                 datetimes.add(make_aware(datetime.datetime.strptime(datetime_str, '%Y-%m-%d-%H%M')))
 
@@ -842,13 +822,10 @@ class Fillslots(APIView):
                     # legacy access by id
                     meeting_type = agenda.get_meetingtype(id_=meeting_type_id)
             except (MeetingType.DoesNotExist, ValueError):
-                return Response(
-                    {
-                        'err': 1,
-                        'err_class': 'invalid meeting type id: %s' % meeting_type_id,
-                        'err_desc': _('invalid meeting type id: %s') % meeting_type_id,
-                    },
-                    status=status.HTTP_400_BAD_REQUEST,
+                raise APIError(
+                    _('invalid meeting type id: %s') % meeting_type_id,
+                    err_class='invalid meeting type id: %s' % meeting_type_id,
+                    http_status=status.HTTP_400_BAD_REQUEST,
                 )
             all_slots = sorted(
                 get_all_slots(agenda, meeting_type, resources=resources),
@@ -908,12 +885,8 @@ class Fillslots(APIView):
                         break
 
             if available_desk is None:
-                return Response(
-                    {
-                        'err': 1,
-                        'err_class': 'no more desk available',
-                        'err_desc': _('no more desk available'),
-                    }
+                raise APIError(
+                    _('no more desk available'), err_class='no more desk available',
                 )
 
             # all datetimes are free, book them in order
@@ -948,29 +921,24 @@ class Fillslots(APIView):
 
             for event in events:
                 if not event.in_bookable_period():
-                    return Response(
-                        {'err': 1, 'err_class': 'event not bookable', 'err_desc': _('event not bookable')}
-                    )
+                    raise APIError(_('event not bookable'), err_class='event not bookable')
                 if event.cancelled:
-                    return Response(
-                        {'err': 1, 'err_class': 'event is cancelled', 'err_desc': _('event is cancelled')}
-                    )
+                    raise APIError(_('event is cancelled'), err_class='event is cancelled')
 
             if not events.count():
-                return Response(
-                    {
-                        'err': 1,
-                        'err_class': 'unknown event identifiers or slugs',
-                        'err_desc': _('unknown event identifiers or slugs'),
-                    },
-                    status=status.HTTP_400_BAD_REQUEST,
+                raise APIError(
+                    _('unknown event identifiers or slugs'),
+                    err_class='unknown event identifiers or slugs',
+                    http_status=status.HTTP_400_BAD_REQUEST,
                 )
 
         # search free places. Switch to waiting list if necessary.
         in_waiting_list = False
         for event in events:
             if payload.get('force_waiting_list') and not event.waiting_list_places:
-                return Response({'err': 1, 'err_class': 'no waiting list', 'err_desc': _('no waiting list')})
+                raise APIError(
+                    _('no waiting list'), err_class='no waiting list',
+                )
 
             if event.waiting_list_places:
                 if (
@@ -982,10 +950,14 @@ class Fillslots(APIView):
                     # in the waiting list.
                     in_waiting_list = True
                     if (event.waiting_list + places_count) > event.waiting_list_places:
-                        return Response({'err': 1, 'err_class': 'sold out', 'err_desc': _('sold out')})
+                        raise APIError(
+                            _('sold out'), err_class='sold out',
+                        )
             else:
                 if (event.booked_places + places_count) > event.places:
-                    return Response({'err': 1, 'err_class': 'sold out', 'err_desc': _('sold out')})
+                    raise APIError(
+                        _('sold out'), err_class='sold out',
+                    )
 
         with transaction.atomic():
             if to_cancel_booking:
@@ -1081,12 +1053,15 @@ class Fillslot(Fillslots):
     serializer_class = SlotSerializer
 
     def post(self, request, agenda_identifier=None, event_identifier=None, format=None):
-        return self.fillslot(
-            request=request,
-            agenda_identifier=agenda_identifier,
-            slots=[event_identifier],  # fill a "list on one slot"
-            format=format,
-        )
+        try:
+            return self.fillslot(
+                request=request,
+                agenda_identifier=agenda_identifier,
+                slots=[event_identifier],  # fill a "list on one slot"
+                format=format,
+            )
+        except APIError as e:
+            return e.to_response()
 
 
 fillslot = Fillslot.as_view()
-- 
2.28.0

