diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index b72419f613..ba51524b7d 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -650,3 +650,20 @@ class EventMetaPropertiesSerializer(I18nAwareModelSerializer): 'id', 'name', 'default', 'required', 'protected', 'filter_public', 'public_label', 'filter_allowed', 'choices' ) + + def validate(self, data): + data = super().validate(data) + full_data = self.to_internal_value(self.to_representation(self.instance)) if self.instance else {} + full_data.update(data) + + choices = full_data.get("choices") + if choices is not None and not isinstance(choices, dict): + raise ValidationError("Choices need to be a dictionary or null.") + default = full_data.get("default") + if choices and default and default not in choices.keys(): + raise ValidationError("You cannot set a default value that is not a valid value.") + + if not choices and "choices" in data: + # normalize empty dict to None + data["choices"] = None + return data diff --git a/src/tests/api/test_event_meta_properties.py b/src/tests/api/test_event_meta_properties.py new file mode 100644 index 0000000000..9073323f87 --- /dev/null +++ b/src/tests/api/test_event_meta_properties.py @@ -0,0 +1,153 @@ +# +# This file is part of pretix (Community Edition). +# +# Copyright (C) 2014-2020 Raphael Michel and contributors +# Copyright (C) 2020-today pretix GmbH and contributors +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General +# Public License as published by the Free Software Foundation in version 3 of the License. +# +# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are +# applicable granting you additional permissions and placing additional restrictions on your usage of this software. +# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive +# this file, see . +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along with this program. If not, see +# . +# +import pytest +from django_scopes import scopes_disabled + + +@pytest.fixture +def event_meta_property(organizer): + return organizer.meta_properties.create( + name="Color", + default="Red", + required=False, + choices=None, + ) + + +TEST_TYPE_RES = { + "name": "Color", + "default": "Red", + "required": False, + "choices": None, + 'filter_allowed': True, + 'filter_public': False, + 'protected': False, + 'public_label': None, +} + + +@pytest.mark.django_db +def test_meta_property_list(token_client, organizer, event_meta_property): + res = dict(TEST_TYPE_RES) + + resp = token_client.get('/api/v1/organizers/{}/event_meta_properties/' + .format(organizer.slug)) + assert resp.status_code == 200 + event_meta_property.refresh_from_db() + res["id"] = event_meta_property.pk + assert res in resp.data['results'] + assert len(resp.data['results']) == 1 + + +@pytest.mark.django_db +def test_meta_property_detail(token_client, organizer, event_meta_property): + res = TEST_TYPE_RES + resp = token_client.get('/api/v1/organizers/{}/event_meta_properties/{}/' + .format(organizer.slug, event_meta_property.pk)) + assert resp.status_code == 200 + event_meta_property.refresh_from_db() + res["id"] = event_meta_property.pk + assert res == resp.data + + +@pytest.mark.django_db +def test_meta_property_create(token_client, organizer): + resp = token_client.post( + '/api/v1/organizers/{}/event_meta_properties/'.format(organizer.slug), + format='json', + data={ + "name": "Color", + "default": "Red", + "required": False, + "choices": ["Red", "Green", "Blue"] + } + ) + assert resp.status_code == 400 + choices = {"r": "Red", "g": "Green", "b": "Blue"} + resp = token_client.post( + '/api/v1/organizers/{}/event_meta_properties/'.format(organizer.slug), + format='json', + data={ + "name": "Color", + "default": "Red", + "required": False, + "choices": choices, + } + ) + assert resp.status_code == 400 + resp = token_client.post( + '/api/v1/organizers/{}/event_meta_properties/'.format(organizer.slug), + format='json', + data={ + "name": "Color", + "default": "r", + "required": False, + "choices": choices, + } + ) + assert resp.status_code == 201 + with scopes_disabled(): + event_meta_property = organizer.meta_properties.get(id=resp.data['id']) + assert event_meta_property.name == "Color" + assert event_meta_property.default == "r" + assert event_meta_property.choices == choices + assert not event_meta_property.required + assert len(organizer.meta_properties.all()) == 1 + + +@pytest.mark.django_db +def test_meta_property_patch(token_client, organizer, event_meta_property): + resp = token_client.patch( + '/api/v1/organizers/{}/event_meta_properties/{}/' + .format(organizer.slug, event_meta_property.pk), + format='json', + data={ + # existing default is not in choices + "choices": {'k': 'Black'}, + } + ) + assert resp.status_code == 400 + resp = token_client.patch( + '/api/v1/organizers/{}/event_meta_properties/{}/' + .format(organizer.slug, event_meta_property.pk), + format='json', + data={ + "required": True, + "choices": None, + } + ) + assert resp.status_code == 200 + event_meta_property.refresh_from_db() + assert event_meta_property.required + assert event_meta_property.choices is None + + + + +@pytest.mark.django_db +def test_meta_property_delete(token_client, organizer, event_meta_property): + resp = token_client.delete( + '/api/v1/organizers/{}/event_meta_properties/{}/' + .format(organizer.slug, event_meta_property.pk), + ) + assert resp.status_code == 204 + assert len(organizer.meta_properties.all()) == 0 diff --git a/src/tests/api/test_permissions.py b/src/tests/api/test_permissions.py index d25ec2d1ac..1306602c78 100644 --- a/src/tests/api/test_permissions.py +++ b/src/tests/api/test_permissions.py @@ -223,6 +223,8 @@ org_permission_sub_urls = [ ('post', 'organizer.customers:write', 'customers/1/anonymize/', 404), ('put', 'organizer.customers:write', 'customers/1/', 404), ('delete', 'organizer.customers:write', 'customers/1/', 404), + ('post', 'organizer.settings.general:write', 'event_meta_properties/', 400), + ('patch', 'organizer.settings.general:write', 'event_meta_properties/1/', 200), ('get', 'organizer.customers:read', 'memberships/', 200), ('post', 'organizer.customers:write', 'memberships/', 400), ('get', 'organizer.customers:read', 'memberships/1/', 404),