Projet

Général

Profil

0003-api-add-post-method-on-agenda-endpoint-57103.patch

Nicolas Roche, 01 octobre 2021 17:06

Télécharger (9,06 ko)

Voir les différences:

Subject: [PATCH 3/3] api: add post method on agenda endpoint (#57103)

 chrono/api/serializers.py |  45 +++++++++++++++-
 chrono/api/views.py       |  24 ++++++++-
 tests/api/test_all.py     | 110 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 177 insertions(+), 2 deletions(-)
chrono/api/serializers.py
1
from django.contrib.auth.models import Group
1 2
from django.utils.translation import ugettext_lazy as _
2 3
from rest_framework import serializers
3 4
from rest_framework.exceptions import ValidationError
4 5

  
5
from chrono.agendas.models import AbsenceReason, Booking, Event
6
from chrono.agendas.models import AbsenceReason, Agenda, Booking, Event
6 7

  
7 8

  
8 9
class StringOrListField(serializers.ListField):
9 10
    def to_internal_value(self, data):
10 11
        if isinstance(data, str):
11 12
            data = [s.strip() for s in data.split(',') if s.strip()]
12 13
        return super().to_internal_value(data)
13 14

  
......
151 152
            'publication_date',
152 153
            'places',
153 154
            'waiting_list_places',
154 155
            'label',
155 156
            'description',
156 157
            'pricing',
157 158
            'url',
158 159
        ]
160

  
161

  
162
class AgendaSerializer(serializers.ModelSerializer):
163
    edit_role = serializers.CharField(required=False)
164
    view_role = serializers.CharField(required=False)
165

  
166
    class Meta:
167
        model = Agenda
168
        fields = [
169
            'slug',
170
            'label',
171
            'kind',
172
            'minimal_booking_delay',
173
            'minimal_booking_delay_in_working_days',
174
            'maximal_booking_delay',
175
            'anonymize_delay',
176
            'edit_role',
177
            'view_role',
178
        ]
179

  
180
    def get_role(self, value):
181
        try:
182
            return Group.objects.get(name=value)
183
        except Group.DoesNotExist:
184
            raise serializers.ValidationError(_('unknown role: %s' % value))
185

  
186
    def validate_edit_role(self, value):
187
        return self.get_role(value)
188

  
189
    def validate_view_role(self, value):
190
        return self.get_role(value)
191

  
192
    def validate(self, attrs):
193
        super().validate(attrs)
194
        if attrs['minimal_booking_delay_in_working_days'] and attrs.get('kind', 'events') != 'events':
195
            raise ValidationError(
196
                {
197
                    'minimal_booking_delay_in_working_days': _('Option not available on %s agenda')
198
                    % attrs['kind']
199
                }
200
            )
201
        return attrs
chrono/api/views.py
684 684
        cancel_callback_url=translate_to_publik_url(payload.get('cancel_callback_url', '')),
685 685
        user_display_label=payload.get('user_display_label', ''),
686 686
        extra_data=extra_data,
687 687
        color=color,
688 688
    )
689 689

  
690 690

  
691 691
class Agendas(APIView):
692
    permission_classes = ()
692
    serializer_class = serializers.AgendaSerializer
693

  
694
    def get_permissions(self):
695

  
696
        if self.request.method == 'GET':
697
            permission_classes = []
698
        else:
699
            permission_classes = [
700
                permissions.IsAuthenticated,
701
            ]
702
        return [permission() for permission in permission_classes]
693 703

  
694 704
    def get(self, request, format=None):
695 705
        agendas_queryset = (
696 706
            Agenda.objects.all()
697 707
            .select_related('absence_reasons_group')
698 708
            .prefetch_related('resources', 'absence_reasons_group__absence_reasons')
699 709
            .order_by('label')
700 710
        )
......
722 732
                not e.full for e in agenda.get_open_events(prefetched_queryset=True)
723 733
            ):
724 734
                # exclude agendas without open events
725 735
                continue
726 736
            agendas.append(get_agenda_detail(request, agenda))
727 737

  
728 738
        return Response({'err': 0, 'data': agendas})
729 739

  
740
    def post(self, request, format=None):
741
        serializer = self.serializer_class(data=request.data)
742
        if not serializer.is_valid():
743
            raise APIError(
744
                _('invalid payload'),
745
                err_class='invalid payload',
746
                errors=serializer.errors,
747
                http_status=status.HTTP_400_BAD_REQUEST,
748
            )
749
        agenda = serializer.save()
750
        return Response({'err': 0, 'data': [get_agenda_detail(request, agenda)]})
751

  
730 752

  
731 753
agendas = Agendas.as_view()
732 754

  
733 755

  
734 756
class AgendaDetail(APIView):
735 757
    """
736 758
    Retrieve an agenda instance.
737 759
    """
tests/api/test_all.py
587 587
                'id': 'meeting1',
588 588
                'duration': 30,
589 589
                'api': {
590 590
                    'datetimes_url': 'http://testserver/api/agenda/virtual-agenda/meetings/meeting1/datetimes/',
591 591
                },
592 592
            },
593 593
        ]
594 594
    }
595

  
596

  
597
@pytest.mark.freeze_time('2021-07-09')
598
def test_add_agenda(app, user, settings):
599
    api_url = '/api/agenda/'
600

  
601
    # no authentication
602
    resp = app.post(api_url, status=401)
603
    assert resp.json['detail'] == 'Authentication credentials were not provided.'
604

  
605
    # wrong password
606
    app.authorization = ('Basic', ('john.doe', 'wrong'))
607
    resp = app.post(api_url, status=401)
608
    assert resp.json['detail'] == 'Invalid username/password.'
609

  
610
    app.authorization = ('Basic', ('john.doe', 'password'))
611

  
612
    # missing fields
613
    resp = app.post(api_url, status=400)
614
    assert resp.json['err']
615
    assert resp.json['errors'] == {'label': ['This field is required.'], 'slug': ['This field is required.']}
616

  
617
    # wrong contents
618
    params = {
619
        'label': 'foo',
620
        'slug': 'foo',
621
        'kind': 'oups',
622
        'minimal_booking_delay': 'oups',
623
        'minimal_booking_delay_in_working_days': 'oups',
624
        'anonymize_delay': 'oups',
625
        'edit_role': 'oups',
626
        'view_role': 'plop',
627
    }
628
    resp = app.post(api_url, params=params, status=400)
629
    assert resp.json['err']
630
    assert resp.json['errors'] == {
631
        'kind': ['"oups" is not a valid choice.'],
632
        'minimal_booking_delay': ['A valid integer is required.'],
633
        'minimal_booking_delay_in_working_days': ['Must be a valid boolean.'],
634
        'anonymize_delay': ['A valid integer is required.'],
635
        'edit_role': ['unknown role: oups'],
636
        'view_role': ['unknown role: plop'],
637
    }
638

  
639
    # slug already used
640
    meeting_agenda = Agenda(label='Foo bar Meeting', kind='meetings')
641
    meeting_agenda.save()
642
    params = {
643
        'label': 'foo',
644
        'slug': meeting_agenda.slug,
645
    }
646
    resp = app.post(api_url, params=params, status=400)
647
    assert resp.json['err']
648
    assert resp.json['errors'] == {'slug': ['agenda with this Identifier already exists.']}
649

  
650
    # option only available on events agenda
651
    params = {
652
        'label': 'foo',
653
        'slug': 'foo',
654
        'kind': 'meetings',
655
        'minimal_booking_delay_in_working_days': True,
656
    }
657
    resp = app.post(api_url, params=params, status=400)
658
    assert resp.json['err']
659
    assert resp.json['errors'] == {
660
        'minimal_booking_delay_in_working_days': ['Option not available on meetings agenda']
661
    }
662

  
663
    settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France'
664
    edit_group = Group.objects.create(name='Edit')
665
    view_group = Group.objects.create(name='View')
666

  
667
    # add a meetings agenda
668
    params = {
669
        'label': 'foo Meetings',
670
        'slug': 'foo-meetings',
671
        'kind': 'meetings',
672
        'minimal_booking_delay': 1,
673
        'maximal_booking_delay': 3,
674
        'anonymize_delay': 30,
675
        'edit_role': 'Edit',
676
        'view_role': 'View',
677
    }
678
    resp = app.post(api_url, params=params)
679
    assert not resp.json['err']
680
    assert len(resp.json['data']) == 1
681
    agenda = Agenda.objects.get(slug='foo-meetings')
682
    assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 10)
683
    assert agenda.edit_role == edit_group
684
    assert agenda.view_role == view_group
685

  
686
    # add an events agenda
687
    params = {
688
        'label': 'foo Events',
689
        'slug': 'foo-events',
690
        'kind': 'events',
691
        'minimal_booking_delay': 1,
692
        'minimal_booking_delay_in_working_days': True,
693
        'maximal_booking_delay': 3,
694
        'anonymize_delay': 30,
695
        'edit_role': 'Edit',
696
        'view_role': 'View',
697
    }
698
    resp = app.post(api_url, params=params)
699
    assert not resp.json['err']
700
    assert len(resp.json['data']) == 1
701
    agenda = Agenda.objects.get(slug='foo-events')
702
    assert agenda.edit_role == edit_group
703
    assert agenda.view_role == view_group
704
    assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12)
595
-