Projet

Général

Profil

0002-api-add-an-enpoint-to-patch-an-event-57305.patch

Nicolas Roche, 05 octobre 2021 17:42

Télécharger (12,2 ko)

Voir les différences:

Subject: [PATCH 2/2] api: add an enpoint to patch an event (#57305)

 chrono/api/urls.py      |   5 ++
 chrono/api/views.py     |  76 +++++++++++++++++
 tests/api/test_event.py | 179 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 260 insertions(+)
chrono/api/urls.py
44 44
        views.recurring_events_list,
45 45
        name='api-agenda-recurring-events',
46 46
    ),
47 47
    url(
48 48
        r'^agenda/(?P<agenda_identifier>[\w-]+)/recurring-events/fillslots/$',
49 49
        views.recurring_fillslots,
50 50
        name='api-recurring-fillslots',
51 51
    ),
52
    url(
53
        r'^agenda/(?P<agenda_identifier>[\w-]+)/event/(?P<event_identifier>[\w:-]+)/$',
54
        views.events,
55
        name='api-event',
56
    ),
52 57
    url(
53 58
        r'^agenda/(?P<agenda_identifier>[\w-]+)/status/(?P<event_identifier>[\w:-]+)/$',
54 59
        views.event_status,
55 60
        name='api-event-status',
56 61
    ),
57 62
    url(
58 63
        r'^agenda/(?P<agenda_identifier>[\w-]+)/bookings/(?P<event_identifier>[\w:-]+)/$',
59 64
        views.event_bookings,
chrono/api/views.py
2178 2178
    def success(self, booking):
2179 2179
        response = {'err': 0, 'booking_id': booking.pk}
2180 2180
        return Response(response)
2181 2181

  
2182 2182

  
2183 2183
resize_booking = ResizeBooking.as_view()
2184 2184

  
2185 2185

  
2186
class Events(APIView):
2187
    permission_classes = (permissions.IsAuthenticated,)
2188
    serializer_class = serializers.EventSerializer
2189

  
2190
    def get_object(self, agenda_identifier, event_identifier):
2191
        agenda = get_object_or_404(Agenda, slug=agenda_identifier, kind='events')
2192
        if ':' in event_identifier:
2193
            return get_event_recurrence(agenda, event_identifier)
2194
        try:
2195
            return agenda.event_set.get(slug=event_identifier)
2196
        except Event.DoesNotExist:
2197
            raise Http404()
2198

  
2199
    def patch(self, request, agenda_identifier=None, event_identifier=None, format=None):
2200
        event = self.get_object(agenda_identifier, event_identifier)
2201
        serializer = self.serializer_class(event, data=request.data, partial=True)
2202
        if not serializer.is_valid():
2203
            raise APIError(
2204
                _('invalid payload'),
2205
                err_class='invalid payload',
2206
                errors=serializer.errors,
2207
                http_status=status.HTTP_400_BAD_REQUEST,
2208
            )
2209

  
2210
        payload = serializer.validated_data
2211
        changed_data = []
2212
        for field in serializer.fields.keys():
2213
            if payload.get(field) and payload.get(field) != getattr(event, field):
2214
                changed_data.append(field)
2215

  
2216
        if event.primary_event:
2217
            errors = {}
2218
            for field in changed_data:
2219
                if field in (
2220
                    'recurrence_end_date',
2221
                    'publication_date',
2222
                    'recurrence_days',
2223
                    'recurrence_week_interval',
2224
                ):
2225
                    errors[field] = [_('Cannot be modified on an event recurrence')]
2226
            if errors:
2227
                raise APIError(
2228
                    _('invalid payload'),
2229
                    err_class='invalid payload',
2230
                    errors=errors,
2231
                    http_status=status.HTTP_400_BAD_REQUEST,
2232
                )
2233

  
2234
        protected_fields = ['start_datetime', 'recurrence_days', 'recurrence_week_interval']
2235
        if event.recurrence_days and event.has_recurrences_booked():
2236
            errors = {}
2237
            for field in changed_data:
2238
                if field in protected_fields:
2239
                    errors[field] = [
2240
                        _('Cannot be modified because some recurrences have bookings attached to them.')
2241
                    ]
2242
            if 'recurrence_end_date' in changed_data and event.has_recurrences_booked(
2243
                after=payload['recurrence_end_date']
2244
            ):
2245
                errors['recurrence_end_date'] = [_('Bookings exist after this date.')]
2246
            if errors:
2247
                raise APIError(
2248
                    _('invalid payload'),
2249
                    err_class='invalid payload',
2250
                    errors=errors,
2251
                    http_status=status.HTTP_400_BAD_REQUEST,
2252
                )
2253

  
2254
        with event.update_recurrences(changed_data, payload, protected_fields, ['recurrence_end_date']):
2255
            event = serializer.save()
2256
        return Response({'err': 0, 'data': get_event_detail(request, event)})
2257

  
2258

  
2259
events = Events.as_view()
2260

  
2261

  
2186 2262
class EventStatus(APIView):
2187 2263
    permission_classes = (permissions.IsAuthenticated,)
2188 2264

  
2189 2265
    def get_object(self, agenda_identifier, event_identifier):
2190 2266
        try:
2191 2267
            agenda = Agenda.objects.get(slug=agenda_identifier, kind='events')
2192 2268
        except Agenda.DoesNotExist:
2193 2269
            try:
tests/api/test_event.py
326 326
        '2021-11-20',
327 327
        '2021-11-29',
328 328
        '2021-12-02',
329 329
        '2021-12-04',
330 330
        '2021-12-13',
331 331
        '2021-12-16',
332 332
        '2021-12-18',
333 333
    ]
334

  
335

  
336
def test_update_event(app, user):
337
    api_url = '/api/agenda/%s/event/%s/' % ('nop', 'nop')
338

  
339
    # no authentication
340
    resp = app.patch(api_url, status=401)
341
    assert resp.json['detail'] == 'Authentication credentials were not provided.'
342

  
343
    # wrong password
344
    app.authorization = ('Basic', ('john.doe', 'wrong'))
345
    resp = app.patch(api_url, status=401)
346
    assert resp.json['detail'] == 'Invalid username/password.'
347

  
348
    app.authorization = ('Basic', ('john.doe', 'password'))
349

  
350
    # missing agenda
351
    resp = app.patch(api_url, status=404)
352
    assert resp.json['detail'] == 'Not found.'
353

  
354
    meeting_agenda = Agenda.objects.create(label='Foo bar Meeting', kind='meetings')
355

  
356
    # using meeting agenda
357
    api_url = '/api/agenda/%s/event/%s/' % (meeting_agenda.slug, 'nop')
358
    resp = app.patch(api_url, status=404)
359
    assert resp.json['detail'] == 'Not found.'
360

  
361
    agenda = Agenda.objects.create(label='Foo bar')
362

  
363
    # missing event
364
    api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'nop')
365
    resp = app.patch(api_url, status=404)
366
    assert resp.json['detail'] == 'Not found.'
367

  
368
    # missing recurring event
369
    api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'nop:2021-11-15-1538')
370
    resp = app.patch(api_url, status=400)
371
    assert resp.json['err']
372
    assert resp.json['err_desc'] == 'unknown recurring event slug: nop'
373

  
374
    event = Event.objects.create(agenda=agenda, start_datetime=now(), places=10, waiting_list_places=5)
375
    api_url = '/api/agenda/%s/event/%s/' % (agenda.slug, event.slug)
376

  
377
    # update with errors in datetime parts
378
    params = {
379
        'start_datetime': '2021-11-15 minuit',
380
        'recurrence_days': '7, 8',
381
    }
382
    resp = app.patch(api_url, params=params, status=400)
383
    assert resp.json['err']
384
    assert resp.json['err_desc'] == 'invalid payload'
385
    assert 'Datetime has wrong format' in resp.json['errors']['start_datetime'][0]
386
    assert resp.json['errors']['recurrence_days']['0'][0] == 'Ensure this value is less than or equal to 6.'
387

  
388
    # update with almost all optional managed fields
389
    params = {
390
        'start_datetime': '2021-11-15 15:38',
391
        'duration': 42,
392
        'publication_date': '2021-09-20',
393
        'places': 8,
394
        'waiting_list_places': 3,
395
        'label': 'FOO camp',
396
        'description': 'An event',
397
        'pricing': 'free',
398
        'url': 'http://example.org/foo/bar/?',
399
    }
400
    resp = app.patch(api_url, params=params)
401
    assert not resp.json['err']
402
    event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
403
    assert event.duration == 42
404
    assert event.places == 8
405
    assert event.waiting_list_places == 3
406
    assert event.label == 'FOO camp'
407
    assert event.description == 'An event'
408
    assert event.pricing == 'free'
409
    assert event.url == 'http://example.org/foo/bar/?'
410

  
411
    # update event as a recurring event
412
    params = {
413
        'recurrence_days': '0,3,5',
414
        'recurrence_week_interval': 2,
415
        'recurrence_end_date': '2021-12-27',
416
    }
417
    resp = app.patch(api_url, params=params)
418
    assert not resp.json['err']
419
    event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
420
    assert event.recurrences.count() == 9
421
    assert [x.places for x in event.recurrences.all()] == [8] * 9
422

  
423
    recurrence_slug = event.recurrences.first().slug
424
    assert recurrence_slug == 'foo-bar-event--2021-11-15-1538'
425
    recurrence_url = '/api/agenda/%s/event/%s/' % (agenda.slug, 'foo-bar-event:2021-11-15-1538')
426

  
427
    # update unprotected fields of one of the event recurrencies
428
    params = {
429
        'start_datetime': '2021-11-14 14:00',
430
        'duration': 43,
431
        'places': 9,
432
        'waiting_list_places': 4,
433
        'label': 'BAR camp',
434
        'description': 'An occurence of an event recurrence',
435
        'pricing': '5€',
436
        'url': 'http://example.org/bar/bar/',
437
    }
438
    resp = app.patch(recurrence_url, params=params)
439
    assert not resp.json['err']
440
    recurrence = Event.objects.filter(agenda=agenda).get(slug=recurrence_slug)
441
    assert recurrence.duration == 43
442
    assert recurrence.places == 9
443
    assert recurrence.waiting_list_places == 4
444
    assert recurrence.label == 'BAR camp'
445
    assert recurrence.description == 'An occurence of an event recurrence'
446
    assert recurrence.pricing == '5€'
447
    assert recurrence.url == 'http://example.org/bar/bar/'
448

  
449
    # try to update protected fields of one of the event recurrencies
450
    params = {
451
        'publication_date': '2021-11-15',
452
        'recurrence_days': '0,3,5',
453
        'recurrence_week_interval': 1,
454
        'recurrence_end_date': '2021-12-27',
455
    }
456
    resp = app.patch(recurrence_url, params=params, status=400)
457
    assert resp.json['err']
458
    assert 'Cannot be modified' in resp.json['errors']['publication_date'][0]
459
    assert 'Cannot be modified' in resp.json['errors']['recurrence_days'][0]
460
    assert 'Cannot be modified' in resp.json['errors']['recurrence_end_date'][0]
461

  
462
    booking = Booking.objects.create(event=event.recurrences.all()[2])
463

  
464
    # update unprotected fields
465
    params = {
466
        'places': 7,
467
    }
468
    resp = app.patch(api_url, params=params)
469
    assert not resp.json['err']
470
    event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
471
    assert [x.places for x in event.recurrences.all()] == [7] * 9
472

  
473
    # try to update recurring event protected fields
474
    params = {
475
        'recurrence_days': '1,2',
476
        'recurrence_week_interval': 1,
477
    }
478
    resp = app.patch(api_url, params=params, status=400)
479
    assert resp.json['err']
480
    assert 'Cannot be modified' in resp.json['errors']['recurrence_days'][0]
481
    assert 'Cannot be modified' in resp.json['errors']['recurrence_week_interval'][0]
482

  
483
    assert booking.event.start_datetime.date() == datetime.date(2021, 11, 20)
484

  
485
    # try to reduce recurrence end date before booked event
486
    params = {
487
        'recurrence_end_date': '2021-11-20',
488
    }
489
    resp = app.patch(api_url, params=params, status=400)
490
    assert resp.json['err']
491
    assert resp.json['errors']['recurrence_end_date'][0] == 'Bookings exist after this date.'
492

  
493
    # reduce recurrence end date after booked event
494
    params = {
495
        'recurrence_end_date': '2021-11-21',
496
    }
497
    resp = app.patch(api_url, params=params)
498
    assert not resp.json['err']
499
    event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
500
    assert event.recurrences.count() == 3
501

  
502
    booking.cancel()
503

  
504
    # update no more protected fields
505
    params = {
506
        'recurrence_days': '1,2,3,4,5',
507
        'recurrence_week_interval': 1,
508
    }
509
    resp = app.patch(api_url, params=params)
510
    assert not resp.json['err']
511
    event = Event.objects.filter(agenda=agenda).get(slug=event.slug)
512
    assert event.recurrences.count() == 5
334
-