From 20581cd31cac1832006f959ce69326d69c7abdbc Mon Sep 17 00:00:00 2001 From: Martin Gross Date: Tue, 14 Feb 2023 14:47:18 +0100 Subject: [PATCH] API: Expose organizer and event URL (Z#23116269) (#3121) --- doc/api/resources/events.rst | 20 +++++++++++++++----- doc/api/resources/organizers.rst | 8 ++++++++ src/pretix/api/serializers/event.py | 7 ++++++- src/pretix/api/serializers/organizer.py | 12 +++++++++--- src/tests/api/test_events.py | 3 ++- src/tests/api/test_oauth.py | 8 +++++--- src/tests/api/test_organizers.py | 3 ++- 7 files changed, 47 insertions(+), 14 deletions(-) diff --git a/doc/api/resources/events.rst b/doc/api/resources/events.rst index 4575163f04..67f18b05bd 100644 --- a/doc/api/resources/events.rst +++ b/doc/api/resources/events.rst @@ -49,6 +49,7 @@ valid_keys object Cryptographic k only contained in detail views. Value can be cached. sales_channels list A list of sales channels this event is available for sale on. +public_url string The public, customer-facing URL of the event (read-only). ===================================== ========================== ======================================================= @@ -65,6 +66,10 @@ Endpoints The ``search`` query parameter has been added to filter events by their slug, name, or location in any language. +.. versionchanged:: 4.17 + + The ``public_url`` field has been added. + .. http:get:: /api/v1/organizers/(organizer)/events/ Returns a list of all events within a given organizer the authenticated user/token has access to. @@ -123,7 +128,8 @@ Endpoints "web", "pretixpos", "resellers" - ] + ], + "public_url": "https://pretix.eu/bigevents/sampleconf/" } ] } @@ -211,7 +217,8 @@ Endpoints "web", "pretixpos", "resellers" - ] + ], + "public_url": "https://pretix.eu/bigevents/sampleconf/" } :param organizer: The ``slug`` field of the organizer to fetch @@ -307,7 +314,8 @@ Endpoints "web", "pretixpos", "resellers" - ] + ], + "public_url": "https://pretix.eu/bigevents/sampleconf/" } :param organizer: The ``slug`` field of the organizer of the event to create. @@ -411,7 +419,8 @@ Endpoints "web", "pretixpos", "resellers" - ] + ], + "public_url": "https://pretix.eu/bigevents/sampleconf/" } :param organizer: The ``slug`` field of the organizer of the event to create. @@ -485,7 +494,8 @@ Endpoints "web", "pretixpos", "resellers" - ] + ], + "public_url": "https://pretix.eu/bigevents/sampleconf/" } :param organizer: The ``slug`` field of the organizer of the event to update diff --git a/doc/api/resources/organizers.rst b/doc/api/resources/organizers.rst index 19b0b9ac3d..74aeb782e4 100644 --- a/doc/api/resources/organizers.rst +++ b/doc/api/resources/organizers.rst @@ -17,12 +17,18 @@ Field Type Description name string The organizer's full name, i.e. the name of an organization or company. slug string A short form of the name, used e.g. in URLs. +public_url string The public, customer-facing URL of the organizer, where + the list of all events can be found (read-only). ===================================== ========================== ======================================================= Endpoints --------- +.. versionchanged:: 4.17 + + The ``public_url`` field has been added. + .. http:get:: /api/v1/organizers/ Returns a list of all organizers the authenticated user/token has access to. @@ -51,6 +57,7 @@ Endpoints { "name": "Big Events LLC", "slug": "Big Events", + "public_url": "https://pretix.eu/bigevents/" } ] } @@ -84,6 +91,7 @@ Endpoints { "name": "Big Events LLC", "slug": "Big Events", + "public_url": "https://pretix.eu/bigevents/" } :param organizer: The ``slug`` field of the organizer to fetch diff --git a/src/pretix/api/serializers/event.py b/src/pretix/api/serializers/event.py index 842a11f268..4c718cb85c 100644 --- a/src/pretix/api/serializers/event.py +++ b/src/pretix/api/serializers/event.py @@ -59,6 +59,7 @@ from pretix.base.settings import ( LazyI18nStringList, validate_event_settings, ) from pretix.base.signals import api_event_settings_fields +from pretix.multidomain.urlreverse import build_absolute_uri logger = logging.getLogger(__name__) @@ -164,6 +165,10 @@ class EventSerializer(I18nAwareModelSerializer): timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones]) valid_keys = ValidKeysField(source='*', read_only=True) best_availability_state = serializers.IntegerField(allow_null=True, read_only=True) + public_url = serializers.SerializerMethodField('get_event_url', read_only=True) + + def get_event_url(self, event): + return build_absolute_uri(event, 'presale:event.index') class Meta: model = Event @@ -171,7 +176,7 @@ class EventSerializer(I18nAwareModelSerializer): 'date_to', 'date_admission', 'is_public', 'presale_start', 'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan', 'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys', - 'sales_channels', 'best_availability_state') + 'sales_channels', 'best_availability_state', 'public_url') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index bd30cd68c9..60a27953bb 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -41,15 +41,21 @@ from pretix.base.models import ( from pretix.base.models.seating import SeatingPlanLayoutValidator from pretix.base.services.mail import SendMailException, mail from pretix.base.settings import validate_organizer_settings -from pretix.helpers.urls import build_absolute_uri +from pretix.helpers.urls import build_absolute_uri as build_global_uri +from pretix.multidomain.urlreverse import build_absolute_uri logger = logging.getLogger(__name__) class OrganizerSerializer(I18nAwareModelSerializer): + public_url = serializers.SerializerMethodField('get_organizer_url', read_only=True) + + def get_organizer_url(self, organizer): + return build_absolute_uri(organizer, 'presale:organizer.index') + class Meta: model = Organizer - fields = ('name', 'slug') + fields = ('name', 'slug', 'public_url') class SeatingPlanSerializer(I18nAwareModelSerializer): @@ -227,7 +233,7 @@ class TeamInviteSerializer(serializers.ModelSerializer): 'user': self, 'organizer': self.context['organizer'].name, 'team': instance.team.name, - 'url': build_absolute_uri('control:auth.invite', kwargs={ + 'url': build_global_uri('control:auth.invite', kwargs={ 'token': instance.token }) }, diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index 6745d4423d..5085dc708a 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -123,7 +123,8 @@ TEST_EVENT_RES = { 'item_meta_properties': { 'day': 'Monday', }, - 'sales_channels': ['web', 'bar', 'baz'] + 'sales_channels': ['web', 'bar', 'baz'], + 'public_url': 'http://example.com/dummy/dummy/' } diff --git a/src/tests/api/test_oauth.py b/src/tests/api/test_oauth.py index d3e74770f0..948c78de7a 100644 --- a/src/tests/api/test_oauth.py +++ b/src/tests/api/test_oauth.py @@ -319,7 +319,9 @@ def test_use_token_for_access_one_organizer(client, admin_user, organizer, appli resp = client.get('/api/v1/organizers/', HTTP_AUTHORIZATION='Bearer %s' % access_token) assert resp.status_code == 200 data = json.loads(resp.content.decode()) - assert data == {'count': 1, 'next': None, 'previous': None, 'results': [{'name': 'Dummy', 'slug': 'dummy'}]} + assert data == {'count': 1, 'next': None, 'previous': None, 'results': [ + {'name': 'Dummy', 'slug': 'dummy', 'public_url': 'http://example.com/dummy/'} + ]} resp = client.get('/api/v1/organizers/dummy/events/', HTTP_AUTHORIZATION='Bearer %s' % access_token) assert resp.status_code == 200 resp = client.get('/api/v1/organizers/a/events/', HTTP_AUTHORIZATION='Bearer %s' % access_token) @@ -362,8 +364,8 @@ def test_use_token_for_access_two_organizers(client, admin_user, organizer, appl assert resp.status_code == 200 data = json.loads(resp.content.decode()) assert data == {'count': 2, 'next': None, 'previous': None, 'results': [ - {'name': 'A', 'slug': 'a'}, - {'name': 'Dummy', 'slug': 'dummy'}, + {'name': 'A', 'slug': 'a', 'public_url': 'http://example.com/a/'}, + {'name': 'Dummy', 'slug': 'dummy', 'public_url': 'http://example.com/dummy/'}, ]} resp = client.get('/api/v1/organizers/dummy/events/', HTTP_AUTHORIZATION='Bearer %s' % access_token) assert resp.status_code == 200 diff --git a/src/tests/api/test_organizers.py b/src/tests/api/test_organizers.py index bb1123931d..4bc7fec36f 100644 --- a/src/tests/api/test_organizers.py +++ b/src/tests/api/test_organizers.py @@ -27,7 +27,8 @@ from pretix.testutils.mock import mocker_context TEST_ORGANIZER_RES = { "name": "Dummy", - "slug": "dummy" + "slug": "dummy", + "public_url": "http://example.com/dummy/" }