From a3ad476c88d64bb6c5f70e7925cbb5a1da29aa23 Mon Sep 17 00:00:00 2001
From: Eliot Berriot <contact@eliotberriot.com>
Date: Tue, 9 Apr 2019 10:30:26 +0200
Subject: [PATCH] More configurable pagination

---
 api/funkwhale_api/common/pagination.py | 27 ++++++++++++++++++++++--
 api/funkwhale_api/music/views.py       |  1 +
 api/tests/common/test_pagination.py    | 29 ++++++++++++++++++++++++++
 api/tests/music/test_views.py          |  2 +-
 4 files changed, 56 insertions(+), 3 deletions(-)
 create mode 100644 api/tests/common/test_pagination.py

diff --git a/api/funkwhale_api/common/pagination.py b/api/funkwhale_api/common/pagination.py
index e5068bce2..ec7c27dc4 100644
--- a/api/funkwhale_api/common/pagination.py
+++ b/api/funkwhale_api/common/pagination.py
@@ -1,6 +1,29 @@
-from rest_framework.pagination import PageNumberPagination
+from rest_framework.pagination import PageNumberPagination, _positive_int
 
 
 class FunkwhalePagination(PageNumberPagination):
     page_size_query_param = "page_size"
-    max_page_size = 50
+    default_max_page_size = 50
+    default_page_size = None
+    view = None
+
+    def paginate_queryset(self, queryset, request, view=None):
+        self.view = view
+        return super().paginate_queryset(queryset, request, view)
+
+    def get_page_size(self, request):
+        max_page_size = (
+            getattr(self.view, "max_page_size", 0) or self.default_max_page_size
+        )
+        page_size = getattr(self.view, "default_page_size", 0) or max_page_size
+        if self.page_size_query_param:
+            try:
+                return _positive_int(
+                    request.query_params[self.page_size_query_param],
+                    strict=True,
+                    cutoff=max_page_size,
+                )
+            except (KeyError, ValueError):
+                pass
+
+        return page_size
diff --git a/api/funkwhale_api/music/views.py b/api/funkwhale_api/music/views.py
index b5242eeb1..2f0e67cb9 100644
--- a/api/funkwhale_api/music/views.py
+++ b/api/funkwhale_api/music/views.py
@@ -524,6 +524,7 @@ class LicenseViewSet(viewsets.ReadOnlyModelViewSet):
     serializer_class = serializers.LicenseSerializer
     queryset = models.License.objects.all().order_by("code")
     lookup_value_regex = ".*"
+    max_page_size = 1000
 
     def get_queryset(self):
         # ensure our licenses are up to date in DB
diff --git a/api/tests/common/test_pagination.py b/api/tests/common/test_pagination.py
new file mode 100644
index 000000000..cacbe740c
--- /dev/null
+++ b/api/tests/common/test_pagination.py
@@ -0,0 +1,29 @@
+import pytest
+
+from funkwhale_api.common import pagination
+
+
+@pytest.mark.parametrize(
+    "view_max_page_size, view_default_page_size, request_page_size, expected",
+    [
+        (50, 50, None, 50),
+        (50, 25, None, 25),
+        (25, None, None, 25),
+        (50, 25, 100, 50),
+        (50, None, 100, 50),
+        (50, 25, 33, 33),
+    ],
+)
+def test_funkwhale_pagination_uses_view_page_size(
+    view_max_page_size, view_default_page_size, request_page_size, expected, mocker
+):
+    p = pagination.FunkwhalePagination()
+
+    p.view = mocker.Mock(
+        max_page_size=view_max_page_size, default_page_size=view_default_page_size
+    )
+    query = {}
+    if request_page_size:
+        query["page_size"] = request_page_size
+    request = mocker.Mock(query_params=query)
+    assert p.get_page_size(request) == expected
diff --git a/api/tests/music/test_views.py b/api/tests/music/test_views.py
index 7b12c6c8f..2c3a61c05 100644
--- a/api/tests/music/test_views.py
+++ b/api/tests/music/test_views.py
@@ -612,7 +612,7 @@ def test_list_licenses(api_client, preferences, mocker):
 
     expected = [
         serializers.LicenseSerializer(l.conf).data
-        for l in models.License.objects.order_by("code")[:25]
+        for l in models.License.objects.order_by("code")
     ]
     url = reverse("api:v1:licenses-list")
 
-- 
GitLab