Add openapi spec for API v2 (#1388)

This commit is contained in:
Kyle Robbertze 2021-10-16 18:34:03 +00:00 committed by GitHub
parent 9f1e41e6fa
commit 1274b2d849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 11 deletions

View File

@ -15,8 +15,8 @@ restarting:
Connections to the API are proxied through the Apache web server by default.
Endpoint exploration and documentation is available from
`http://example.com/api/v2/`, where `example.com` is the URL for the LibreTime
instance.
`http://example.com/api/v2/schema/swagger-ui/`, where `example.com` is the URL
for the LibreTime instance.
### Development

View File

@ -138,18 +138,21 @@ class ScheduleSerializer(serializers.HyperlinkedModelSerializer):
"id",
"starts",
"ends",
"file",
"file_id",
"stream",
"stream_id",
"clip_length",
"fade_in",
"fade_out",
"cue_in",
"cue_out",
"media_item_played",
"file",
"file_id",
"stream",
"stream_id",
"instance",
"instance_id",
"playout_status",
"broadcasted",
"position",
]

View File

@ -38,7 +38,8 @@ INSTALLED_APPS = [
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"url_filter",
"django_filters",
"drf_spectacular",
]
MIDDLEWARE = [
@ -114,8 +115,9 @@ REST_FRAMEWORK = {
"libretimeapi.permissions.IsSystemTokenOrUser",
],
"DEFAULT_FILTER_BACKENDS": [
"url_filter.integrations.drf.DjangoFilterBackend",
"django_filters.rest_framework.DjangoFilterBackend",
],
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"URL_FIELD_NAME": "item_url",
}

View File

@ -164,3 +164,56 @@ class TestScheduleViewSet(APITestCase):
self.assertEqual(len(result), 1)
self.assertEqual(dateparse.parse_datetime(result[0]["ends"]), scheduleItem.ends)
self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), f.cueout)
def test_schedule_item_range(self):
music_dir = baker.make(
"libretimeapi.MusicDir",
directory=os.path.join(os.path.dirname(__file__), "resources"),
)
f = baker.make(
"libretimeapi.File",
directory=music_dir,
mime="audio/mp3",
filepath="song.mp3",
length=timedelta(seconds=40.86),
cuein=timedelta(seconds=0),
cueout=timedelta(seconds=40.8131),
)
filter_point = datetime.now(tz=timezone.utc)
show = baker.make(
"libretimeapi.ShowInstance",
starts=filter_point - timedelta(minutes=5),
ends=filter_point + timedelta(minutes=5),
)
schedule_item = baker.make(
"libretimeapi.Schedule",
starts=filter_point,
ends=filter_point + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
previous_item = baker.make(
"libretimeapi.Schedule",
starts=filter_point - timedelta(minutes=5),
ends=filter_point - timedelta(minutes=5) + f.length,
cue_out=f.cueout,
instance=show,
file=f,
)
self.client.credentials(HTTP_AUTHORIZATION="Api-Key {}".format(self.token))
range_start = (filter_point - timedelta(minutes=1)).isoformat(
timespec="seconds"
)
range_end = (filter_point + timedelta(minutes=1)).isoformat(timespec="seconds")
response = self.client.get(
self.path, {"starts__range": "{},{}".format(range_start, range_end)}
)
self.assertEqual(response.status_code, 200)
result = response.json()
# The previous_item should be filtered out and not returned
self.assertEqual(len(result), 1)
self.assertEqual(
dateparse.parse_datetime(result[0]["starts"]), schedule_item.starts
)

View File

@ -1,4 +1,5 @@
from django.urls import include, path
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
from rest_framework import routers
from .views import *
@ -46,6 +47,12 @@ router.register("track-types", TrackTypeViewSet)
urlpatterns = [
path("api/v2/", include(router.urls)),
path("api/v2/schema/", SpectacularAPIView.as_view(), name="schema"),
path(
"api/v2/schema/swagger-ui/",
SpectacularSwaggerView.as_view(url_name="schema"),
name="swagger-ui",
),
path("api/v2/version/", version),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
]

View File

@ -4,7 +4,8 @@ from django.conf import settings
from django.db.models import F
from django.http import FileResponse
from django.shortcuts import get_object_or_404
from rest_framework import status, viewsets
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from rest_framework import fields, status, viewsets
from rest_framework.decorators import action, api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
@ -12,6 +13,15 @@ from rest_framework.response import Response
from .permissions import IsAdminOrOwnUser
from .serializers import *
FILTER_NUMERICAL_LOOKUPS = [
"exact",
"gt",
"lt",
"gte",
"lte",
"range",
]
class UserViewSet(viewsets.ModelViewSet):
queryset = get_user_model().objects.all()
@ -139,10 +149,27 @@ class PreferenceViewSet(viewsets.ModelViewSet):
model_permission_name = "preference"
@extend_schema_view(
list=extend_schema(
parameters=[
OpenApiParameter(
name="is_valid",
description="Filter on valid instances",
required=False,
type=bool,
),
]
)
)
class ScheduleViewSet(viewsets.ModelViewSet):
queryset = Schedule.objects.all()
serializer_class = ScheduleSerializer
filter_fields = ("starts", "ends", "playout_status", "broadcasted")
filter_fields = {
"starts": FILTER_NUMERICAL_LOOKUPS,
"ends": FILTER_NUMERICAL_LOOKUPS,
"playout_status": FILTER_NUMERICAL_LOOKUPS,
"broadcasted": FILTER_NUMERICAL_LOOKUPS,
}
model_permission_name = "schedule"
def get_queryset(self):

View File

@ -25,7 +25,8 @@ setup(
"coreapi",
"django~=3.0",
"djangorestframework",
"django-url-filter",
"django-filter",
"drf-spectacular",
"markdown",
"model_bakery",
],