0003-api-add-post-method-on-agenda-endpoint-57103.patch
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) |
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 |
payload = serializer.validated_data |
|
750 |
agenda = Agenda.objects.create(**payload) |
|
751 |
return Response({'err': 0, 'data': [get_agenda_detail(request, agenda)]}) |
|
752 | ||
730 | 753 | |
731 | 754 |
agendas = Agendas.as_view() |
732 | 755 | |
733 | 756 | |
734 | 757 |
class AgendaDetail(APIView): |
735 | 758 |
""" |
736 | 759 |
Retrieve an agenda instance. |
737 | 760 |
""" |
tests/api/test_all.py | ||
---|---|---|
1 | 1 |
import datetime |
2 | 2 | |
3 | 3 |
import pytest |
4 |
from django.contrib.auth.models import Group |
|
4 | 5 |
from django.db import connection |
5 | 6 |
from django.test.utils import CaptureQueriesContext |
6 | 7 |
from django.utils.timezone import localtime, now |
7 | 8 | |
8 | 9 |
from chrono.agendas.models import ( |
9 | 10 |
AbsenceReason, |
10 | 11 |
AbsenceReasonGroup, |
11 | 12 |
Agenda, |
... | ... | |
590 | 591 |
'id': 'meeting1', |
591 | 592 |
'duration': 30, |
592 | 593 |
'api': { |
593 | 594 |
'datetimes_url': 'http://testserver/api/agenda/virtual-agenda/meetings/meeting1/datetimes/', |
594 | 595 |
}, |
595 | 596 |
}, |
596 | 597 |
] |
597 | 598 |
} |
599 | ||
600 | ||
601 |
@pytest.mark.freeze_time('2021-07-09') |
|
602 |
def test_add_agenda(app, user, settings): |
|
603 |
api_url = '/api/agenda/' |
|
604 | ||
605 |
# no authentication |
|
606 |
resp = app.post(api_url, status=401) |
|
607 |
assert resp.json['detail'] == 'Authentication credentials were not provided.' |
|
608 | ||
609 |
# wrong password |
|
610 |
app.authorization = ('Basic', ('john.doe', 'wrong')) |
|
611 |
resp = app.post(api_url, status=401) |
|
612 |
assert resp.json['detail'] == 'Invalid username/password.' |
|
613 | ||
614 |
app.authorization = ('Basic', ('john.doe', 'password')) |
|
615 | ||
616 |
# missing fields |
|
617 |
resp = app.post(api_url, status=400) |
|
618 |
assert resp.json['err'] |
|
619 |
assert resp.json['errors'] == {'label': ['This field is required.'], 'slug': ['This field is required.']} |
|
620 | ||
621 |
# slug already used |
|
622 |
meeting_agenda = Agenda(label='Foo bar Meeting', kind='meetings') |
|
623 |
meeting_agenda.save() |
|
624 |
params = { |
|
625 |
'label': 'foo', |
|
626 |
'slug': meeting_agenda.slug, |
|
627 |
} |
|
628 |
resp = app.post(api_url, params=params, status=400) |
|
629 |
assert resp.json['err'] |
|
630 |
assert resp.json['errors'] == {'slug': ['agenda with this Identifier already exists.']} |
|
631 | ||
632 |
# unknown kind |
|
633 |
params = { |
|
634 |
'label': 'foo', |
|
635 |
'slug': 'foo', |
|
636 |
'kind': 'oups', |
|
637 |
} |
|
638 |
resp = app.post(api_url, params=params, status=400) |
|
639 |
assert resp.json['err'] |
|
640 |
assert resp.json['errors'] == {'kind': ['"oups" is not a valid choice.']} |
|
641 | ||
642 |
# unknown group |
|
643 |
params = { |
|
644 |
'label': 'foo', |
|
645 |
'slug': 'foo', |
|
646 |
'edit_role': 'oups', |
|
647 |
'view_role': 'plop', |
|
648 |
} |
|
649 |
resp = app.post(api_url, params=params, status=400) |
|
650 |
assert resp.json['err'] |
|
651 |
assert resp.json['errors'] == { |
|
652 |
'edit_role': ['unknown role: oups'], |
|
653 |
'view_role': ['unknown role: plop'], |
|
654 |
} |
|
655 | ||
656 |
settings.WORKING_DAY_CALENDAR = 'workalendar.europe.France' |
|
657 |
edit_group = Group.objects.create(name='Edit') |
|
658 |
view_group = Group.objects.create(name='View') |
|
659 | ||
660 |
# add a meetings agenda |
|
661 |
params = { |
|
662 |
'label': 'foo Meetings', |
|
663 |
'slug': 'foo-meetings', |
|
664 |
'kind': 'meetings', |
|
665 |
'minimal_booking_delay': 1, |
|
666 |
'minimal_booking_delay_in_working_days': True, |
|
667 |
'maximal_booking_delay': 3, |
|
668 |
'anonymize_delay': 30, |
|
669 |
'edit_role': 'Edit', |
|
670 |
'view_role': 'View', |
|
671 |
} |
|
672 |
resp = app.post(api_url, params=params) |
|
673 |
assert not resp.json['err'] |
|
674 |
assert len(resp.json['data']) == 1 |
|
675 |
agenda = Agenda.objects.get(slug='foo-meetings') |
|
676 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12) |
|
677 |
assert agenda.edit_role == edit_group |
|
678 |
assert agenda.view_role == view_group |
|
679 | ||
680 |
# add an events agenda |
|
681 |
params = { |
|
682 |
'label': 'foo Events', |
|
683 |
'slug': 'foo-events', |
|
684 |
'kind': 'events', |
|
685 |
'minimal_booking_delay': 1, |
|
686 |
'minimal_booking_delay_in_working_days': True, |
|
687 |
'maximal_booking_delay': 3, |
|
688 |
'anonymize_delay': 30, |
|
689 |
'edit_role': 'Edit', |
|
690 |
'view_role': 'View', |
|
691 |
} |
|
692 |
resp = app.post(api_url, params=params) |
|
693 |
assert not resp.json['err'] |
|
694 |
assert len(resp.json['data']) == 1 |
|
695 |
agenda = Agenda.objects.get(slug='foo-events') |
|
696 |
assert agenda.edit_role == edit_group |
|
697 |
assert agenda.view_role == view_group |
|
698 |
assert agenda.min_booking_datetime.date() == datetime.date(2021, 7, 12) |
|
598 |
- |