mirror of
https://github.com/pretix/pretix.git
synced 2026-08-18 12:06:26 +00:00
Tests and docs
This commit is contained in:
@@ -29,8 +29,8 @@ item_assignments list of objects Products this l
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
Layout endpoints
|
||||
----------------
|
||||
|
||||
.. http:get:: /api/v1/organizers/(organizer)/events/(event)/ticketlayouts/
|
||||
|
||||
@@ -268,5 +268,75 @@ Endpoints
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to delete this resource.
|
||||
|
||||
API ticket rendering
|
||||
--------------------
|
||||
|
||||
.. http:post:: /api/v1/organizers/(organizer)/events/(event)/ticketpdfrenderer/render_batch/
|
||||
|
||||
With this API call, you can instruct the system to render a set of tickets into one combined PDF file. To specify
|
||||
which tickets to render, you need to submit a list of "parts". For every part, the following fields are supported:
|
||||
|
||||
* ``orderposition`` (``integer``, required): The ID of the order position to render.
|
||||
* ``override_channel`` (``string``, optional): The sales channel ID to be used for layout selection instead of the
|
||||
original channel of the order.
|
||||
* ``override_layout`` (``integer``, optional): The ticket layout ID to be used instead of the auto-selected one.
|
||||
|
||||
If your input parameters validate correctly, a ``202 Accepted`` status code is returned.
|
||||
The body points you to the download URL of the result. Running a ``GET`` request on that result URL will
|
||||
yield one of the following status codes:
|
||||
|
||||
* ``200 OK`` – The export succeeded. The body will be your resulting file. Might be large!
|
||||
* ``409 Conflict`` – Your export is still running. The body will be JSON with the structure ``{"status": "running", "percentage": 40}``. ``percentage`` can be ``null`` if it is not known and ``status`` can be ``waiting`` before the task is actually being processed. Please retry, but wait at least one second before you do.
|
||||
* ``410 Gone`` – Running the export has failed permanently. The body will be JSON with the structure ``{"status": "failed", "message": "Error message"}``
|
||||
* ``404 Not Found`` – The export does not exist / is expired.
|
||||
|
||||
.. warning:: This endpoint is considered **experimental**. It might change at any time without prior notice.
|
||||
|
||||
.. note:: To avoid performance issues, a maximum number of 1000 parts is currently allowed.
|
||||
|
||||
**Example request**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
POST /api/v1/organizers/bigevents/events/sampleconf/ticketpdfrenderer/render_batch/ HTTP/1.1
|
||||
Host: pretix.eu
|
||||
Accept: application/json, text/javascript
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": 55412
|
||||
},
|
||||
{
|
||||
"orderposition": 55412,
|
||||
"override_channel": "web"
|
||||
},
|
||||
{
|
||||
"orderposition": 55412,
|
||||
"override_layout": 56
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
|
||||
.. sourcecode:: http
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Vary: Accept
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"download": "https://pretix.eu/api/v1/organizers/bigevents/events/sampleconf/ticketpdfrenderer/download/29891ede-196f-4942-9e26-d055a36e98b8/3f279f13-c198-4137-b49b-9b360ce9fcce/"
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
:param event: The ``slug`` field of the event to fetch
|
||||
:statuscode 202: no error
|
||||
:statuscode 400: Invalid input options
|
||||
:statuscode 401: Authentication failure
|
||||
:statuscode 403: The requested organizer/event does not exist **or** you have no permission to view this resource.
|
||||
|
||||
|
||||
.. _here: https://github.com/pretix/pretix/blob/master/src/pretix/static/schema/pdf-layout.schema.json
|
||||
|
||||
@@ -201,7 +201,7 @@ class RenderJobSerializer(serializers.Serializer):
|
||||
|
||||
def validate(self, attrs):
|
||||
if len(attrs["parts"]) > 1000:
|
||||
raise ValidationError("Please do not submit more than 1000 parts.")
|
||||
raise ValidationError({"parts": ["Please do not submit more than 1000 parts."]})
|
||||
return super().validate(attrs)
|
||||
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ def bulk_render(event: Event, fileid: int, parts: list) -> int:
|
||||
merger.close()
|
||||
outbuffer.seek(0)
|
||||
|
||||
file.type = "application/pdf"
|
||||
file.file.save(cachedfile_name(file, file.filename), ContentFile(outbuffer.getvalue()))
|
||||
file.save()
|
||||
return file.pk
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#
|
||||
import copy
|
||||
import json
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
import pytest
|
||||
from django.core.files.base import ContentFile
|
||||
@@ -28,7 +30,9 @@ from django.utils.timezone import now
|
||||
from django_scopes import scopes_disabled
|
||||
from rest_framework.test import APIClient
|
||||
|
||||
from pretix.base.models import Event, Item, Organizer, Team
|
||||
from pretix.base.models import (
|
||||
Event, Item, Order, OrderPosition, Organizer, Team,
|
||||
)
|
||||
from pretix.plugins.ticketoutputpdf.models import TicketLayoutItem
|
||||
|
||||
|
||||
@@ -39,14 +43,35 @@ def env():
|
||||
organizer=o, name='Dummy', slug='dummy',
|
||||
date_from=now(), plugins='pretix.plugins.banktransfer'
|
||||
)
|
||||
t = Team.objects.create(organizer=event.organizer)
|
||||
t = Team.objects.create(organizer=event.organizer, can_view_orders=True)
|
||||
t.limit_events.add(event)
|
||||
item1 = Item.objects.create(event=event, name="Ticket", default_price=23)
|
||||
tl = event.ticket_layouts.create(name="Foo", default=True, layout='[{"a": 2}]')
|
||||
tl = event.ticket_layouts.create(
|
||||
name="Foo",
|
||||
default=True,
|
||||
layout='[{"type": "poweredby", "left": "0", "bottom": "0", "size": "1.00", "content": "dark"}]',
|
||||
)
|
||||
TicketLayoutItem.objects.create(layout=tl, item=item1, sales_channel=o.sales_channels.get(identifier="web"))
|
||||
return event, tl, item1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def position(env):
|
||||
item = env[0].items.create(name="Ticket", default_price=3, admission=True)
|
||||
order = Order.objects.create(
|
||||
code='FOO', event=env[0], email='dummy@dummy.test',
|
||||
status=Order.STATUS_PAID, locale='en',
|
||||
datetime=now() - timedelta(days=4),
|
||||
expires=now() - timedelta(hours=4) + timedelta(days=10),
|
||||
total=Decimal('23.00'),
|
||||
sales_channel=env[0].organizer.sales_channels.get(identifier="web"),
|
||||
)
|
||||
return OrderPosition.objects.create(
|
||||
order=order, item=item, variation=None,
|
||||
price=Decimal("23.00"), attendee_name_parts={"full_name": "Peter"}, positionid=1
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client():
|
||||
return APIClient()
|
||||
@@ -213,3 +238,92 @@ def test_api_delete(env, token_client):
|
||||
)
|
||||
assert resp.status_code == 204
|
||||
assert not env[0].ticket_layouts.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_renderer_batch_valid(env, token_client, position):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
},
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
"override_channel": "web",
|
||||
},
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
"override_layout": env[1].pk,
|
||||
},
|
||||
]
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 202
|
||||
assert "download" in resp.data
|
||||
resp = token_client.get("/" + resp.data["download"].split("/", 3)[3])
|
||||
assert resp.status_code == 200
|
||||
assert resp["Content-Type"] == "application/pdf"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_renderer_batch_invalid(env, token_client, position):
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": -2,
|
||||
},
|
||||
]
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"orderposition": ["Invalid pk \"-2\" - object does not exist."]}]}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
"override_layout": -2,
|
||||
},
|
||||
]
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"override_layout": ["Invalid pk \"-2\" - object does not exist."]}]}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
"override_channel": "magic",
|
||||
},
|
||||
]
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": [{"override_channel": ["Object with identifier=magic does not exist."]}]}
|
||||
|
||||
resp = token_client.post(
|
||||
'/api/v1/organizers/{}/events/{}/ticketpdfrenderer/render_batch/'.format(env[0].slug, env[0].slug),
|
||||
{
|
||||
"parts": [
|
||||
{
|
||||
"orderposition": position.pk,
|
||||
}
|
||||
] * 1002
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.data == {"parts": ["Please do not submit more than 1000 parts."]}
|
||||
|
||||
Reference in New Issue
Block a user