Projet

Général

Profil

0001-api-add-a-Publik-api-to-add-events-47337.patch

Nicolas Roche, 08 décembre 2020 22:39

Télécharger (9,4 ko)

Voir les différences:

Subject: [PATCH] api: add a Publik api to add events (#47337)

 chrono/api/urls.py  |   5 ++
 chrono/api/views.py |  79 ++++++++++++++++++++++++++++++
 tests/test_api.py   | 114 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 198 insertions(+)
chrono/api/urls.py
33 33
        views.slot_status,
34 34
        name='api-event-status',
35 35
    ),
36 36
    url(
37 37
        r'^agenda/(?P<agenda_identifier>[\w-]+)/bookings/(?P<event_identifier>[\w-]+)/$',
38 38
        views.slot_bookings,
39 39
        name='api-event-bookings',
40 40
    ),
41
    url(
42
        r'^agenda/(?P<agenda_identifier>[\w-]+)/add-event/$',
43
        views.agenda_add_event,
44
        name='api-agenda-add-event',
45
    ),
41 46
    url(
42 47
        r'^agenda/meetings/(?P<meeting_identifier>[\w-]+)/datetimes/$',
43 48
        views.meeting_datetimes,
44 49
        name='api-agenda-meeting-datetimes-legacy',
45 50
    ),
46 51
    url(r'^agenda/(?P<agenda_identifier>[\w-]+)/meetings/$', views.meeting_list, name='api-agenda-meetings'),
47 52
    url(
48 53
        r'^agenda/(?P<agenda_identifier>[\w-]+)/meetings/(?P<meeting_identifier>[\w-]+)/$',
chrono/api/views.py
1483 1483

  
1484 1484
    def get(self, request, booking_pk=None, format=None):
1485 1485
        booking = get_object_or_404(Booking, id=booking_pk)
1486 1486
        response = HttpResponse(booking.get_ics(request), content_type='text/calendar')
1487 1487
        return response
1488 1488

  
1489 1489

  
1490 1490
booking_ics = BookingICS.as_view()
1491

  
1492

  
1493
class ExceptionHandlerMixin(object):
1494
    def handle_exception(self, exc):
1495
        if not isinstance(exc, APIError):
1496
            exc = APIError(str(exc), err_class=str(type(exc)))
1497
        return exc.to_response()
1498

  
1499

  
1500
class ViewableAgendaMixin(ExceptionHandlerMixin):
1501
    agenda = None
1502

  
1503
    def set_agenda(self, **kwargs):
1504
        agenda_identifier = kwargs.get('agenda_identifier')
1505
        try:
1506
            self.agenda = Agenda.objects.get(slug=agenda_identifier)
1507
        except Agenda.DoesNotExist:
1508
            raise APIError(
1509
                _('The agenda "%s" does not exist.') % agenda_identifier,
1510
                err_class='invalid agenda'
1511
            )
1512

  
1513
    def initial(self, request, *args, **kwargs):
1514
        super().initial(request, *args, **kwargs)
1515
        self.set_agenda(**kwargs)
1516
        if not self.check_agenda_permissions(request.user):
1517
            raise APIError(
1518
                _('User "%s" cannot access agenda "%s".') % (
1519
                    str(request.user), self.agenda.slug),
1520
                err_class='invalid agenda'
1521
            )
1522

  
1523
    def check_agenda_permissions(self, user):
1524
        return self.agenda.can_be_viewed(user)
1525

  
1526
    def success(self, event):
1527
        response = {'err': 0, 'event_id': event.pk}
1528
        return Response(response)
1529

  
1530

  
1531
class ManagedAgendaMixin(ViewableAgendaMixin):
1532
    def check_agenda_permissions(self, user):
1533
        return self.agenda.can_be_managed(user)
1534

  
1535

  
1536
class EventSerializer(serializers.ModelSerializer):
1537
    class Meta:
1538
        model = Event
1539
        fields = [
1540
            'start_datetime',
1541
            'duration',
1542
            'publication_date',
1543
            'places',
1544
            'waiting_list_places',
1545
            'label',
1546
            'description',
1547
            'pricing',
1548
            'url',
1549
        ]
1550

  
1551

  
1552
class AgendaAddEventView(ManagedAgendaMixin, APIView):
1553
    permission_classes = (permissions.IsAuthenticated, )
1554
    serializer_class = EventSerializer
1555

  
1556
    def post(self, request, *args, **kwargs):
1557
        serializer = self.serializer_class(data=request.data)
1558
        if not serializer.is_valid():
1559
            raise APIError(
1560
                _('invalid payload'),
1561
                err_class='invalid payload',
1562
                errors=serializer.errors
1563
            )
1564
        payload = serializer.validated_data
1565
        event = Event.objects.create(agenda=self.agenda, **payload)
1566
        return self.success(event)
1567

  
1568

  
1569
agenda_add_event = AgendaAddEventView.as_view()
tests/test_api.py
2 2

  
3 3
import datetime
4 4
import mock
5 5
import urllib.parse as urlparse
6 6
import pytest
7 7
import sys
8 8

  
9 9
from django.contrib.auth import get_user_model
10
from django.contrib.auth.models import Group
10 11
from django.db import connection
11 12
from django.test import override_settings
12 13
from django.test.utils import CaptureQueriesContext
13 14
from django.utils.timezone import now, make_aware, localtime
14 15

  
15 16
from chrono.agendas.models import (
16 17
    Agenda,
17 18
    Booking,
......
40 41
    user = User.objects.create(
41 42
        username='john.doe', first_name=u'John', last_name=u'Doe', email='john.doe@example.net'
42 43
    )
43 44
    user.set_password('password')
44 45
    user.save()
45 46
    return user
46 47

  
47 48

  
49
@pytest.fixture
50
def managers_group():
51
    group, _ = Group.objects.get_or_create(name='Managers')
52
    return group
53

  
54

  
55
@pytest.fixture
56
def manager_user(managers_group):
57
    User = get_user_model()
58
    user = User.objects.create_user('manager', password='manager')
59
    user.groups.set([managers_group])
60
    return user
61

  
62

  
48 63
@pytest.fixture(params=['Europe/Brussels', 'Asia/Kolkata', 'Brazil/East'])
49 64
def time_zone(request, settings):
50 65
    settings.TIME_ZONE = request.param
51 66

  
52 67

  
53 68
# 2017-05-20 -> saturday
54 69
@pytest.fixture(
55 70
    params=[
......
4573 4588
    agenda_foo.save()
4574 4589
    resp = app.get(foo_api_url, params=params)
4575 4590
    assert len(resp.json['data']) == 16
4576 4591
    # also on virtual agenda
4577 4592
    virtual_agenda.maximal_booking_delay = 5
4578 4593
    virtual_agenda.save()
4579 4594
    resp = app.get(virtual_api_url, params=params)
4580 4595
    assert len(resp.json['data']) == 16
4596

  
4597

  
4598
def test_add_event(app, user):
4599
    agenda = Agenda(label=u'Foo bar')
4600
    agenda.maximal_booking_delay = 0
4601
    agenda.save()
4602

  
4603
    params = {
4604
        'start_datetime': '2020-12-08 15:38',
4605
        'places': 10,
4606
    }
4607
    api_url = '/api/agenda/%s/add-event/' % (agenda.slug)
4608

  
4609
    # not an admin user
4610
    app.authorization = ('Basic', ('john.doe', 'password'))
4611
    resp = app.post(api_url, params=params, status=200)
4612
    assert resp.json['err']
4613
    assert resp.json['err_desc'] == 'User "john.doe" cannot access agenda "foo-bar".'
4614

  
4615
    # add an event
4616
    user.is_staff = True
4617
    user.save()
4618
    resp = app.post(api_url, params=params)
4619
    assert not resp.json['err']
4620
    event_id = resp.json['event_id']
4621
    assert Event.objects.filter(agenda=agenda).count() == 1
4622
    event = Event.objects.filter(agenda=agenda, id=event_id)[0]
4623
    assert str(event.start_datetime) == '2020-12-08 14:38:00+00:00'
4624
    assert str(event.start_datetime.tzinfo) == 'UTC'
4625
    assert event.places == 10
4626
    assert event.publication_date is None
4627

  
4628
    # add with a description
4629
    params = {
4630
        'start_datetime': '2020-12-08 15:38',
4631
        'publication_date': '2020-05-11',
4632
        'description': 'A description',
4633
        'places': 11,
4634
    }
4635
    resp = app.post(api_url, params=params)
4636
    event_id = resp.json['event_id']
4637
    event = Event.objects.filter(agenda=agenda, id=event_id)[0]
4638
    assert event.description == 'A description'
4639
    assert event.publication_date == datetime.date(2020, 5, 11)
4640

  
4641
    # add with errors in datetime parts
4642
    params = {
4643
        'start_datetime': '2020-12-08 minuit',
4644
        'places': 10,
4645
    }
4646
    resp = app.post(api_url, params=params, status=200)
4647
    assert resp.json['err']
4648
    assert resp.json['err_desc'] == 'invalid payload'
4649
    assert 'Datetime has wrong format' in resp.json['errors']['start_datetime'][0]
4650
    assert Event.objects.filter(agenda=agenda).count() == 2
4651

  
4652

  
4653
def test_add_event_on_missing_agenda(app, user):
4654
    api_url = '/api/agenda/%s/add-event/' % ('999')
4655

  
4656
    # no authentication
4657
    resp = app.post(api_url, status=200)
4658
    assert resp.json['err']
4659
    assert resp.json['err_desc'] == 'Authentication credentials were not provided.'
4660

  
4661
    # wrong password
4662
    app.authorization = ('Basic', ('john.doe', 'wrong'))
4663
    resp = app.post(api_url, status=200)
4664
    assert resp.json['err']
4665
    assert resp.json['err_desc'] == 'Invalid username/password.'
4666

  
4667
    # wrong agenda
4668
    app.authorization = ('Basic', ('john.doe', 'password'))
4669
    resp = app.post(api_url, status=200)
4670
    assert resp.json['err']
4671
    assert resp.json['err_desc'] == 'The agenda "999" does not exist.'
4672

  
4673

  
4674
def test_add_event_as_manager(app, manager_user):
4675
    agenda = Agenda(label=u'Foo bar')
4676
    agenda.view_role = manager_user.groups.all()[0]
4677
    agenda.save()
4678

  
4679
    app.authorization = ('Basic', ('manager', 'manager'))
4680
    params = {
4681
        'start_datetime': '2020-12-08 15:38',
4682
        'places': 10,
4683
    }
4684
    api_url = '/api/agenda/%s/add-event/' % (agenda.slug)
4685
    resp = app.post(api_url, params=params, status=200)
4686
    assert resp.json['err']
4687
    assert resp.json['err_desc'] == 'User "manager" cannot access agenda "foo-bar".'
4688
    assert not Event.objects.filter(agenda=agenda).count()
4689

  
4690
    agenda.edit_role = manager_user.groups.all()[0]
4691
    agenda.save()
4692
    resp = app.post(api_url, params=params)
4693
    assert not resp.json['err']
4694
    assert Event.objects.filter(agenda=agenda).count()
4581
-