diff --git a/doc/api/deviceauth.rst b/doc/api/deviceauth.rst index 2ca8ee3bbb..f3fc0972ab 100644 --- a/doc/api/deviceauth.rst +++ b/doc/api/deviceauth.rst @@ -105,6 +105,37 @@ following endpoint: You will receive a response equivalent to the response of your initialization request. +Device Information +------------------ + +You can request information about your device and the server with one call: + +.. sourcecode:: http + + GET /api/v1/device/info HTTP/1.1 + Host: pretix.eu + +The response will look like this: + +.. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: application/json + + { + "device": { + "organizer": "foo", + "device_id": 5, + "unique_serial": "HHZ9LW9JWP390VFZ", + "api_token": "1kcsh572fonm3hawalrncam4l1gktr2rzx25a22l8g9hx108o9oi0rztpcvwnfnd", + "name": "Bar", + "gate": { + "id": 3, + "name": "South entrance" + } + } + } + Creating a new API key ---------------------- diff --git a/src/pretix/api/urls.py b/src/pretix/api/urls.py index cbb8607057..1ad42a60e0 100644 --- a/src/pretix/api/urls.py +++ b/src/pretix/api/urls.py @@ -138,6 +138,7 @@ urlpatterns = [ re_path(r"^device/update$", device.UpdateView.as_view(), name="device.update"), re_path(r"^device/roll$", device.RollKeyView.as_view(), name="device.roll"), re_path(r"^device/revoke$", device.RevokeKeyView.as_view(), name="device.revoke"), + re_path(r"^device/info$", device.InfoView.as_view(), name="device.info"), re_path(r"^device/eventselection$", device.EventSelectionView.as_view(), name="device.eventselection"), re_path(r"^idempotency_query$", idempotency.IdempotencyQueryView.as_view(), name="idempotency.query"), re_path(r"^upload$", upload.UploadView.as_view(), name="upload"), diff --git a/src/pretix/api/views/device.py b/src/pretix/api/views/device.py index 875faa9279..7e717c23bc 100644 --- a/src/pretix/api/views/device.py +++ b/src/pretix/api/views/device.py @@ -29,7 +29,9 @@ from rest_framework.exceptions import ValidationError from rest_framework.response import Response from rest_framework.views import APIView +from pretix import __version__ from pretix.api.auth.device import DeviceTokenAuthentication +from pretix.api.views.version import numeric_version from pretix.base.models import CheckinList, Device, SubEvent from pretix.base.models.devices import Gate, generate_api_token @@ -151,6 +153,24 @@ class RevokeKeyView(APIView): return Response(serializer.data) +class InfoView(APIView): + authentication_classes = (DeviceTokenAuthentication,) + + def get(self, request, format=None): + device = request.auth + serializer = DeviceSerializer(device) + return Response({ + 'device': serializer.data, + 'server': { + 'version': { + 'pretix': __version__, + 'pretix_numeric': numeric_version(__version__), + } + } + + }) + + class EventSelectionView(APIView): authentication_classes = (DeviceTokenAuthentication,) diff --git a/src/tests/api/test_deviceauth.py b/src/tests/api/test_deviceauth.py index bd2fdd86e0..4fd9bd9bf1 100644 --- a/src/tests/api/test_deviceauth.py +++ b/src/tests/api/test_deviceauth.py @@ -171,3 +171,15 @@ def test_revoke_valid(device_client, device: Device): assert resp.status_code == 200 device.refresh_from_db() assert device.revoked + + +@pytest.mark.django_db +def test_device_info(device_client, device: Device): + resp = device_client.get('/api/v1/device/info') + assert resp.status_code == 200 + assert resp.data['device']['organizer'] == 'dummy' + assert resp.data['device']['name'] == 'Foo' + assert 'device_id' in resp.data['device'] + assert 'unique_serial' in resp.data['device'] + assert 'api_token' in resp.data['device'] + assert 'pretix' in resp.data['server']['version']