]> git.ipfire.org Git - thirdparty/patchwork.git/commitdiff
REST: Include 'first', 'last' refs in 'Link' header
authorStephen Finucane <stephen@that.guru>
Tue, 14 Apr 2020 09:46:05 +0000 (10:46 +0100)
committerStephen Finucane <stephen@that.guru>
Fri, 6 May 2022 18:51:04 +0000 (19:51 +0100)
I've no idea why this wasn't done from day one, but it's a huge
usability win for anyone attempting to do pagination with this header.
Note that this change is not versioned as I haven't figured out how to
do that at this layer.

Signed-off-by: Stephen Finucane <stephen@that.guru>
docs/api/schemas/latest/patchwork.yaml
docs/api/schemas/patchwork.j2
docs/api/schemas/v1.0/patchwork.yaml
docs/api/schemas/v1.1/patchwork.yaml
docs/api/schemas/v1.2/patchwork.yaml
docs/api/schemas/v1.3/patchwork.yaml
patchwork/api/base.py
releasenotes/notes/improved-rest-pagination-headers-4140a70044bbd6cf.yaml [new file with mode: 0644]

index 06be99d35bcf681e72beb15249b844f419c9862a..3a1fdd3aa42dcb882bfc608c5baf196f1c50bbbc 100644 (file)
@@ -1351,7 +1351,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index f8c6d2143fafdd53de073dcc805ea401ccf6b4f9..b97866541a9d6805d9fc6ac5fdf1e7ba5cf9a2db 100644 (file)
@@ -1384,7 +1384,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index 53571b60622fcb05c0cfebd225943148403ac507..817b2f2abe889922122f1174a378eccaf5b980a3 100644 (file)
@@ -1082,7 +1082,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index f83b5e01a6ae757bce45d0c3670433a63583d3f7..574a8ad8cdeae372eadf238e4333e15b2014213c 100644 (file)
@@ -1082,7 +1082,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index e8cee0e508d78e4fc35acf27a6cce51c4876feee..7a4e8e8e2e4a633e7d77aa05f654201f5b0a2c6f 100644 (file)
@@ -1217,7 +1217,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index 8b3b1154e21bce65f5e803cacd658964840cda3d..6bd0419ddf904b3688175a0617fec877143a6eaf 100644 (file)
@@ -1351,7 +1351,10 @@ components:
         Links to related resources, in the format defined by
         [RFC 5988](https://tools.ietf.org/html/rfc5988#section-5).
         This will include a link with relation type `next` to the
-        next page, if there is a next page.
+        next page and `prev` to the previous page, if there is a next
+        or previous page. It will also include links with the
+        relation type `first` and `last` pointing to the first and
+        last page, respectively.
       schema:
         type: string
   requestBodies:
index d870a5119df8973b75836f8a47c4c086907bb53a..7baac2750575b434fcd587705edb2450b03ec8d2 100644 (file)
@@ -12,6 +12,7 @@ from rest_framework.pagination import PageNumberPagination
 from rest_framework.response import Response
 from rest_framework.serializers import HyperlinkedIdentityField
 from rest_framework.serializers import HyperlinkedModelSerializer
+from rest_framework.utils.urls import replace_query_param
 
 from patchwork.api import utils
 
@@ -59,19 +60,33 @@ class LinkHeaderPagination(PageNumberPagination):
     max_page_size = settings.MAX_REST_RESULTS_PER_PAGE
     page_size_query_param = 'per_page'
 
+    def get_first_link(self):
+        url = self.request.build_absolute_uri()
+        return replace_query_param(url, self.page_query_param, 1)
+
+    def get_last_link(self):
+        url = self.request.build_absolute_uri()
+        page_number = self.page.paginator.num_pages
+        return replace_query_param(url, self.page_query_param, page_number)
+
     def get_paginated_response(self, data):
         next_url = self.get_next_link()
         previous_url = self.get_previous_link()
+        first_url = self.get_first_link()
+        last_url = self.get_last_link()
+
+        links = []
+
+        if next_url is not None:
+            links.append(f'<{next_url}>; rel="next"')
+
+        if previous_url is not None:
+            links.append(f'<{previous_url}>; rel="prev"')
+
+        links.append(f'<{first_url}>; rel="first"')
+        links.append(f'<{last_url}>; rel="last"')
 
-        link = ''
-        if next_url is not None and previous_url is not None:
-            link = '<{next_url}>; rel="next", <{previous_url}>; rel="prev"'
-        elif next_url is not None:
-            link = '<{next_url}>; rel="next"'
-        elif previous_url is not None:
-            link = '<{previous_url}>; rel="prev"'
-        link = link.format(next_url=next_url, previous_url=previous_url)
-        headers = {'Link': link} if link else {}
+        headers = {'Link': ', '.join(links)} if links else {}
         return Response(data, headers=headers)
 
 
diff --git a/releasenotes/notes/improved-rest-pagination-headers-4140a70044bbd6cf.yaml b/releasenotes/notes/improved-rest-pagination-headers-4140a70044bbd6cf.yaml
new file mode 100644 (file)
index 0000000..215fa11
--- /dev/null
@@ -0,0 +1,9 @@
+---
+features:
+  - |
+    The ``Link`` header included in REST API responses now includes ``first``
+    and ``last`` relations, as described in `RFC 5988`__. As their name would
+    suggest, these can be used to navigate to the beginning and end of the
+    resource.
+
+    .. __: https://datatracker.ietf.org/doc/html/rfc5988