Bug 19196: Add Koha::REST::Plugin::Pagination
authorTomas Cohen Arazi <tomascohen@theke.io>
Tue, 29 Aug 2017 19:52:48 +0000 (16:52 -0300)
committerJonathan Druart <jonathan.druart@bugs.koha-community.org>
Mon, 9 Oct 2017 19:15:51 +0000 (16:15 -0300)
This patch introduces a Mojolicious plugin to be used on the REST api.
It adds a helper method:

add_pagination_headers
======================

When used, it adds a _Link_ header to the reponse with the calculated
values for pagination, and X-Total-Count containing the total results
like this:

    my $params  = $c->validation->output;
    my $patrons = Koha::Patrons->search;
    my $count   = $patrons->count;

    $c->add_pagination_headers({
            total  => $count,
            params => $params )};

To test:
- Run:
  $ sudo koha-shell kohadev
 k$ cd kohaclone
 k$ prove t/Koha/REST/Plugin/Pagination.t
=> SUCCESS: Tests pass!
- Sign off :-D

Sponsored-by: ByWater solutions
Sponsored-by: Camden County
Edit: I fixed a mistake on the POD (tcohen)

Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Lari Taskula <lari.taskula@jns.fi>
Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Koha/REST/Plugin/Pagination.pm [new file with mode: 0644]

diff --git a/Koha/REST/Plugin/Pagination.pm b/Koha/REST/Plugin/Pagination.pm
new file mode 100644 (file)
index 0000000..8b09dd1
--- /dev/null
@@ -0,0 +1,143 @@
+package Koha::REST::Plugin::Pagination;
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Plugin';
+
+=head1 NAME
+
+Koha::REST::Plugin::Pagination
+
+=head1 API
+
+=head2 Mojolicious::Plugin methods
+
+=head3 register
+
+=cut
+
+sub register {
+    my ( $self, $app ) = @_;
+
+=head2 Helper methods
+
+=head3 add_pagination_headers
+
+    my $patrons = Koha::Patrons->search( ... );
+    $c->add_pagination_headers({
+        total  => $patrons->count,
+        params => {
+            page     => ...
+            per_page => ...
+            ...
+        }
+    });
+
+Adds a Link header to the response message $c carries, following RFC5988, including
+the following relation types: 'prev', 'next', 'first' and 'last'.
+It also adds X-Total-Count, containing the total results count.
+
+=cut
+
+    $app->helper(
+        'add_pagination_headers' => sub {
+            my ( $c, $args ) = @_;
+
+            my $total    = $args->{total};
+            my $req_page = $args->{params}->{page};
+            my $per_page = $args->{params}->{per_page};
+
+            my $pages = int $total / $per_page;
+            $pages++
+                if $total % $per_page > 0;
+
+            my @links;
+
+            if ( $pages > 1 and $req_page > 1 ) {    # Previous exists?
+                push @links,
+                    _build_link(
+                    $c,
+                    {   page     => $req_page - 1,
+                        per_page => $per_page,
+                        rel      => 'prev',
+                        params   => $args->{params}
+                    }
+                    );
+            }
+
+            if ( $pages > 1 and $req_page < $pages ) {    # Next exists?
+                push @links,
+                    _build_link(
+                    $c,
+                    {   page     => $req_page + 1,
+                        per_page => $per_page,
+                        rel      => 'next',
+                        params   => $args->{params}
+                    }
+                    );
+            }
+
+            push @links,
+                _build_link( $c,
+                { page => 1, per_page => $per_page, rel => 'first', params => $args->{params} } );
+            push @links,
+                _build_link( $c,
+                { page => $pages, per_page => $per_page, rel => 'last', params => $args->{params} } );
+
+            # Add Link header
+            $c->res->headers->add( 'Link' => join( ',', @links ) );
+
+            # Add X-Total-Count header
+            $c->res->headers->add( 'X-Total-Count' => $total );
+            return $c;
+        }
+    );
+}
+
+=head2 Internal methods
+
+=head3 _build_link
+
+    my $link = _build_link( $c, { page => 1, per_page => 5, rel => 'prev' });
+
+Returns a string, suitable for using in Link headers following RFC5988.
+
+=cut
+
+sub _build_link {
+    my ( $c, $args ) = @_;
+
+    my $params = $args->{params};
+
+    $params->{page}     = $args->{page};
+    $params->{per_page} = $args->{per_page};
+
+    my $link = '<'
+        . $c->req->url->clone->query(
+            $params
+        )->to_abs
+        . '>; rel="'
+        . $args->{rel} . '"';
+
+    # TODO: Find a better solution for this horrible (but needed) fix
+    $link =~ s|api/v1/app\.pl/||;
+
+    return $link;
+}
+
+1;