Projet

Général

Profil

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

Nicolas Roche (absent jusqu'au 3 avril), 05 octobre 2021 18:35

Télécharger (8,96 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       |  18 ++++++-
 tests/api/test_all.py     | 110 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 171 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

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

  
162

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

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

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

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

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

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

  
691 691

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

  
695
    def get_permissions(self):
696
        permission_classes = [] if self.request.method == 'GET' else [permissions.IsAuthenticated]
697
        return [permission() for permission in permission_classes]
694 698

  
695 699
    def get(self, request, format=None):
696 700
        agendas_queryset = (
697 701
            Agenda.objects.all()
698 702
            .select_related('absence_reasons_group')
699 703
            .prefetch_related('resources', 'absence_reasons_group__absence_reasons')
700 704
            .order_by('label')
701 705
        )
......
723 727
                not e.full for e in agenda.get_open_events(prefetched_queryset=True)
724 728
            ):
725 729
                # exclude agendas without open events
726 730
                continue
727 731
            agendas.append(get_agenda_detail(request, agenda))
728 732

  
729 733
        return Response({'err': 0, 'data': agendas})
730 734

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

  
731 747

  
732 748
agendas = Agendas.as_view()
733 749

  
734 750

  
735 751
class AgendaDetail(APIView):
736 752
    """
737 753
    Retrieve an agenda instance.
738 754
    """
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
-