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 | |
... | ... | |
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 |
- |