From 26ae84cfef946f135a2b2ea91e6af4dd05048750 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Wed, 18 Dec 2024 15:17:41 +0100 Subject: [PATCH 1/1] moves on api --- src/api/fields.py | 7 +++--- src/api/pagination.py | 14 +++++++++++ src/api/urls.py | 9 +++++++ src/catalogue/api/serializers.py | 24 +++++++++++++++++++ src/catalogue/api/urls2.py | 18 ++++++++++++++ src/catalogue/api/views.py | 35 +++++++++++++++++++++++----- src/social/api/views.py | 1 + src/wolnelektury/settings/contrib.py | 5 +++- 8 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 src/api/pagination.py create mode 100644 src/catalogue/api/urls2.py diff --git a/src/api/fields.py b/src/api/fields.py index cdfcc4735..728323473 100644 --- a/src/api/fields.py +++ b/src/api/fields.py @@ -3,7 +3,7 @@ # from rest_framework import serializers from sorl.thumbnail import default -from django.urls import reverse +from rest_framework.reverse import reverse from club.models import Membership @@ -20,13 +20,14 @@ class AbsoluteURLField(serializers.ReadOnlyField): self.view_args[fields[0]] = fields[1] if len(fields) > 1 else fields[0] def to_representation(self, value): + request = self.context['request'] if self.view_name is not None: kwargs = { arg: getattr(value, field) for (arg, field) in self.view_args.items() } - value = reverse(self.view_name, kwargs=kwargs) - return self.context['request'].build_absolute_uri(value) + return reverse(self.view_name, kwargs=kwargs, request=request) + return request.build_absolute_uri(value) class LegacyMixin: diff --git a/src/api/pagination.py b/src/api/pagination.py new file mode 100644 index 000000000..0c4ae09d4 --- /dev/null +++ b/src/api/pagination.py @@ -0,0 +1,14 @@ +from rest_framework.pagination import LimitOffsetPagination, PageLink +from rest_framework.response import Response + + +class WLLimitOffsetPagination(LimitOffsetPagination): + def get_paginated_response(self, data): + return Response({ + "member": data, + "totalItems": self.count, + "view": { + "previous": self.get_previous_link(), + "next": self.get_next_link(), + } + }) diff --git a/src/api/urls.py b/src/api/urls.py index 703283ef7..b0ee94def 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -9,7 +9,15 @@ from stats.utils import piwik_track_view from . import views +urlpatterns1 = [ + path('login/', csrf_exempt(views.LoginView.as_view())), + path('', include('catalogue.api.urls2')), +] + + urlpatterns = [ + path('2/', include((urlpatterns1, 'api'), namespace="v2")), + path('oauth/request_token/', csrf_exempt(views.OAuth1RequestTokenView.as_view())), path('oauth/authorize/', views.oauth_user_auth, name='oauth_user_auth'), path('oauth/access_token/', csrf_exempt(views.OAuth1AccessTokenView.as_view())), @@ -37,4 +45,5 @@ urlpatterns = [ path('', include('social.api.urls')), path('', include('catalogue.api.urls')), + ] diff --git a/src/catalogue/api/serializers.py b/src/catalogue/api/serializers.py index daa86fec8..ee3c5067f 100644 --- a/src/catalogue/api/serializers.py +++ b/src/catalogue/api/serializers.py @@ -44,6 +44,30 @@ class TranslatorSerializer(serializers.Serializer): name = serializers.CharField(source='*') +class BookSerializer2(serializers.ModelSerializer): + url = AbsoluteURLField() + href = AbsoluteURLField(view_name='catalogue_api_book', view_args=['slug']) + xml = EmbargoURLField(source='xml_url') + html = EmbargoURLField(source='html_url') + txt = EmbargoURLField(source='txt_url') + fb2 = EmbargoURLField(source='fb2_url') + epub = EmbargoURLField(source='epub_url') + mobi = EmbargoURLField(source='mobi_url') + pdf = EmbargoURLField(source='pdf_url') + + class Meta: + model = Book + fields = [ + 'full_sort_key', 'title', + 'href', 'url', 'language', + #'epochs', 'genres', 'kinds', 'authors', 'translators', + #'children', + 'parent', 'preview', + 'epub', 'mobi', 'pdf', 'html', 'txt', 'fb2', 'xml', + 'cover_thumb', 'cover', + 'isbn_pdf', 'isbn_epub', 'isbn_mobi', + ] + class BookSerializer(LegacyMixin, serializers.ModelSerializer): author = serializers.CharField(source='author_unicode') kind = serializers.CharField(source='kind_unicode') diff --git a/src/catalogue/api/urls2.py b/src/catalogue/api/urls2.py new file mode 100644 index 000000000..3fd0023d1 --- /dev/null +++ b/src/catalogue/api/urls2.py @@ -0,0 +1,18 @@ +# This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Wolne Lektury. See NOTICE for more information. +# +from django.urls import path, re_path +from stats.utils import piwik_track_view +from . import views + + +urlpatterns = [ + path('books/', + piwik_track_view(views.BookList2.as_view()), + name='catalogue_api_book_list' + ), + path('books//', + piwik_track_view(views.BookDetail2.as_view()), + name='catalogue_api_book' + ), +] diff --git a/src/catalogue/api/views.py b/src/catalogue/api/views.py index c0dc57f76..abe8064c4 100644 --- a/src/catalogue/api/views.py +++ b/src/catalogue/api/views.py @@ -30,6 +30,10 @@ from . import serializers book_tag_categories = ['author', 'epoch', 'kind', 'genre'] +class LegacyListAPIView(ListAPIView): + pagination_class = None + + class CreateOnPutMixin: ''' Creates a new model instance when PUTting a nonexistent resource. @@ -47,7 +51,7 @@ class CreateOnPutMixin: raise -class CollectionList(ListAPIView): +class CollectionList(LegacyListAPIView): queryset = Collection.objects.filter(listed=True) serializer_class = serializers.CollectionListSerializer @@ -61,7 +65,7 @@ class CollectionDetail(CreateOnPutMixin, RetrieveUpdateAPIView): @vary_on_auth # Because of 'liked'. -class BookList(ListAPIView): +class BookList(LegacyListAPIView): permission_classes = [DjangoModelPermissionsOrAnonReadOnly] queryset = Book.objects.none() # Required for DjangoModelPermissions serializer_class = serializers.BookListSerializer @@ -179,6 +183,19 @@ class BookList(ListAPIView): return Response({}, status=status.HTTP_201_CREATED) +class BookList2(ListAPIView): + permission_classes = [DjangoModelPermissionsOrAnonReadOnly] + queryset = Book.objects.none() # Required for DjangoModelPermissions + serializer_class = serializers.BookSerializer2 + + def get_queryset(self): + books = Book.objects.all() + books = books.filter(findable=True) + books = order_books(books, True) + + return books + + @vary_on_auth # Because of 'liked'. class BookDetail(RetrieveAPIView): queryset = Book.objects.all() @@ -186,13 +203,19 @@ class BookDetail(RetrieveAPIView): serializer_class = serializers.BookDetailSerializer +class BookDetail2(RetrieveAPIView): + queryset = Book.objects.all() + lookup_field = 'slug' + serializer_class = serializers.BookSerializer2 + + @vary_on_auth # Because of embargo links. class EbookList(BookList): serializer_class = serializers.EbookSerializer @method_decorator(never_cache, name='dispatch') -class Preview(ListAPIView): +class Preview(LegacyListAPIView): #queryset = Book.objects.filter(preview=True) serializer_class = serializers.BookPreviewSerializer @@ -205,7 +228,7 @@ class Preview(ListAPIView): @vary_on_auth # Because of 'liked'. -class FilterBookList(ListAPIView): +class FilterBookList(LegacyListAPIView): serializer_class = serializers.FilterBookListSerializer def parse_bool(self, s): @@ -289,7 +312,7 @@ class EpubView(RetrieveAPIView): return HttpResponse(self.get_object().get_media('epub')) -class TagCategoryView(ListAPIView): +class TagCategoryView(LegacyListAPIView): serializer_class = serializers.TagSerializer def get_queryset(self): @@ -373,7 +396,7 @@ class TagView(RetrieveAPIView): @vary_on_auth # Because of 'liked'. -class FragmentList(ListAPIView): +class FragmentList(LegacyListAPIView): serializer_class = serializers.FragmentSerializer def get_queryset(self): diff --git a/src/social/api/views.py b/src/social/api/views.py index c5506ce79..a29930423 100644 --- a/src/social/api/views.py +++ b/src/social/api/views.py @@ -36,6 +36,7 @@ class LikeView(APIView): class ShelfView(ListAPIView): permission_classes = [IsAuthenticated] serializer_class = BookSerializer + pagination_class = None def get_queryset(self): state = self.kwargs['state'] diff --git a/src/wolnelektury/settings/contrib.py b/src/wolnelektury/settings/contrib.py index aaef8b2ce..e5b2f60d6 100644 --- a/src/wolnelektury/settings/contrib.py +++ b/src/wolnelektury/settings/contrib.py @@ -31,7 +31,10 @@ REST_FRAMEWORK = { 'api.drf_auth.WLTokenAuthentication', 'api.drf_auth.PistonOAuthAuthentication', 'rest_framework.authentication.SessionAuthentication', - ) + ), + 'DEFAULT_PAGINATION_CLASS': 'api.pagination.WLLimitOffsetPagination', + 'PAGE_SIZE': 10, + 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning', } -- 2.20.1