Add API endpoint /seats to event (Z#23159536) (#4321)

* add API endpoint /seats to event

* fix logging

* add Seat annotations

* add seats endpoint for subevents

* return ids of occupying objects instead of boolean flags

* wip

* include orderposition instead of order in seat info

* add API documentation

* Apply suggestions from code review

Co-authored-by: Raphael Michel <michel@rami.io>

* Apply suggestions from code review

* Clarify API docs

* add api examples

* add test cases

* require can_view_orders permission for retrieving seats

* improve permission handling

* Revert "improve permission handling"

This reverts commit f32b532cc68760a8a4af03208bd17e75e8c5723d.

* improve permission handling (minimal version)

* formatting

* add permission tests

* fix bug

* update permission checks

* Apply suggestions from code review

Co-authored-by: Raphael Michel <michel@rami.io>

* add tests for permission checks

* add tests for expand=voucher and expand=cartposition

* remove unused parameter

* test query count

* codestyle

---------

Co-authored-by: Raphael Michel <michel@rami.io>
This commit is contained in:
Mira
2024-08-02 09:17:46 +02:00
committed by GitHub
parent a0b046d204
commit dc1973f4ff
11 changed files with 548 additions and 19 deletions

View File

@@ -685,6 +685,7 @@ def seat(event, organizer, item):
@pytest.mark.django_db
def test_cartpos_create_with_seat(token_client, organizer, event, item, quota, seat, question):
res = copy.deepcopy(CARTPOS_CREATE_PAYLOAD)
res['expires'] = now() + datetime.timedelta(hours=1)
res['item'] = item.pk
res['seat'] = seat.seat_guid
resp = token_client.post(
@@ -697,6 +698,14 @@ def test_cartpos_create_with_seat(token_client, organizer, event, item, quota, s
p = CartPosition.objects.get(pk=resp.data['id'])
assert p.seat == seat
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/'.format(organizer.slug, event.slug, seat.pk))
assert resp.status_code == 200
assert resp.data['cartposition'] == p.pk
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/?expand=cartposition'.format(organizer.slug, event.slug, seat.pk))
assert resp.status_code == 200
assert resp.data['cartposition']['id'] == p.pk
@pytest.mark.django_db
def test_cartpos_create_with_blocked_seat(token_client, organizer, event, item, quota, seat, question):

View File

@@ -42,7 +42,8 @@ from django.conf import settings
from django.core.files.base import ContentFile
from django.utils.timezone import now
from django_countries.fields import Country
from django_scopes import scopes_disabled
from django_scopes import scope, scopes_disabled
from tests import assert_num_queries
from tests.const import SAMPLE_PNG
from pretix.base.models import (
@@ -999,6 +1000,10 @@ def seatingplan(event, organizer, item):
@pytest.mark.django_db
def test_event_update_seating(token_client, organizer, event, item, seatingplan):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 0
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
@@ -1019,6 +1024,11 @@ def test_event_update_seating(token_client, organizer, event, item, seatingplan)
assert m.layout_category == 'Stalls'
assert m.product == item
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 3
assert all(seat['product'] == item.pk for seat in resp.data['results'])
@pytest.mark.django_db
def test_event_update_seating_invalid_product(token_client, organizer, event, item, seatingplan):
@@ -1530,3 +1540,97 @@ def test_patch_event_settings_file(token_client, organizer, event):
)
assert resp.status_code == 200
assert resp.data['logo_image'] is None
@pytest.mark.django_db
def test_event_block_unblock_seat(token_client, organizer, event, seatingplan, item):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"seating_plan": seatingplan.pk,
"seat_category_mapping": {
"Stalls": item.pk
}
},
format='json'
)
assert resp.status_code == 200
event.refresh_from_db()
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'.format(organizer.slug, event.slug))
assert resp.status_code == 200
seat_id = resp.data['results'][0]['id']
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/seats/{}/'.format(organizer.slug, event.slug, seat_id),
{
"blocked": True,
},
format='json'
)
assert resp.status_code == 200
assert resp.data['blocked'] is True
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/'
'?expand=orderposition&expand=cartposition&expand=voucher'
.format(organizer.slug, event.slug, seat_id))
assert resp.status_code == 200
assert resp.data['blocked'] is True
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/seats/{}/'.format(organizer.slug, event.slug, seat_id),
{
"blocked": False,
},
format='json'
)
assert resp.status_code == 200
assert resp.data['blocked'] is False
@pytest.mark.django_db
def test_event_expand_seat_querycount(token_client, organizer, event, seatingplan, item):
resp = token_client.patch(
'/api/v1/organizers/{}/events/{}/'.format(organizer.slug, event.slug),
{
"seating_plan": seatingplan.pk,
"seat_category_mapping": {
"Stalls": item.pk
}
},
format='json'
)
assert resp.status_code == 200
event.refresh_from_db()
with assert_num_queries(9):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert len(resp.data['results']) == 3
with scope(organizer=organizer):
v0 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-0'))
with assert_num_queries(10):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert resp.data['results'][0]['voucher']['id'] == v0.pk
assert resp.data['results'][1]['voucher'] is None
assert resp.data['results'][2]['voucher'] is None
with scope(organizer=organizer):
v1 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-1'))
v2 = event.vouchers.create(item=item, seat=event.seats.get(seat_guid='0-2'))
with assert_num_queries(10):
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/'
'?expand=orderposition&expand=cartposition&expand=voucher'
.format(organizer.slug, event.slug))
assert resp.status_code == 200
assert resp.data['results'][0]['voucher']['id'] == v0.pk
assert resp.data['results'][1]['voucher']['id'] == v1.pk
assert resp.data['results'][2]['voucher']['id'] == v2.pk

View File

@@ -2010,6 +2010,14 @@ def test_order_create_with_seat(token_client, organizer, event, item, quota, sea
p = o.positions.first()
assert p.seat == seat
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/'.format(organizer.slug, event.slug, seat.pk))
assert resp.status_code == 200
assert resp.data['orderposition'] == p.pk
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/?expand=orderposition'.format(organizer.slug, event.slug, seat.pk))
assert resp.status_code == 200
assert resp.data['orderposition']['id'] == p.pk
@pytest.mark.django_db
def test_order_create_with_blocked_seat_allowed(token_client, organizer, event, item, quota, seat, question):

View File

@@ -54,6 +54,7 @@ event_urls = [
(None, 'taxrules/'),
('can_view_orders', 'waitinglistentries/'),
('can_view_orders', 'checkinlists/'),
(None, 'seats/'),
]
event_permission_sub_urls = [
@@ -191,6 +192,12 @@ event_permission_sub_urls = [
('post', 'can_change_event_settings', 'item_meta_properties/', 400),
('patch', 'can_change_event_settings', 'item_meta_properties/0/', 404),
('delete', 'can_change_event_settings', 'item_meta_properties/0/', 404),
('get', None, 'seats/', 200),
('get', 'can_view_orders', 'seats/?expand=orderposition', 200),
('get', 'can_view_orders', 'seats/?expand=cartposition', 200),
('get', 'can_view_vouchers', 'seats/?expand=voucher', 200),
('get', None, 'seats/1/', 404),
('patch', 'can_change_event_settings', 'seats/1/', 404),
]
org_permission_sub_urls = [
@@ -254,6 +261,7 @@ org_permission_sub_urls = [
('get', 'can_change_teams', 'teams/{team_id}/tokens/0/', 404),
('delete', 'can_change_teams', 'teams/{team_id}/tokens/0/', 404),
('post', 'can_change_teams', 'teams/{team_id}/tokens/', 400),
('get', 'can_manage_reusable_media', 'reusablemedia/1/', 404),
]

View File

@@ -1210,6 +1210,14 @@ def test_set_seat_ok(token_client, organizer, event, seatingplan, seat1, item):
v.refresh_from_db()
assert v.seat == seat1
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/'.format(organizer.slug, event.slug, seat1.pk))
assert resp.status_code == 200
assert resp.data['voucher'] == v.pk
resp = token_client.get('/api/v1/organizers/{}/events/{}/seats/{}/?expand=voucher'.format(organizer.slug, event.slug, seat1.pk))
assert resp.status_code == 200
assert resp.data['voucher']['id'] == v.pk
@pytest.mark.django_db
def test_save_set_seat(token_client, organizer, event, seatingplan, seat1, item):