LP#1951021: account for change in OverDrive checkout response
authorGalen Charlton <gmc@equinoxOLI.org>
Fri, 7 Jan 2022 23:50:04 +0000 (18:50 -0500)
committerJeff Davis <jdavis@sitka.bclibraries.ca>
Thu, 10 Feb 2022 17:39:22 +0000 (09:39 -0800)
The OverDrive circulation API soon will stop providing a direct
download link in favor of displaying a separate fulfillment page
(either as an iframe or a redirect). This patch implements this
change with the redirect option.

Additional information on the API change can be found at

http://developer.overdrive.com/overdrive-api-notices/01-sep-2021-coming-soon-changes-to-overdrive-checkouts-api

Signed-off-by: Galen Charlton <gmc@equinoxOLI.org>
Signed-off-by: Jeff Davis <jdavis@sitka.bclibraries.ca>
Signed-off-by: Garry Collum <gcollum@gmail.com>
Open-ILS/src/perlmods/lib/OpenILS/Application/EbookAPI.pm
Open-ILS/src/perlmods/lib/OpenILS/Application/EbookAPI/OverDrive.pm
Open-ILS/src/perlmods/lib/OpenILS/Utils/HTTPClient.pm
Open-ILS/web/js/ui/default/opac/ebook_api/loggedin.js

index bc968b5..6102b31 100644 (file)
@@ -326,6 +326,7 @@ sub request {
     my $self = shift;
     my $req = shift;
     my $session_id = shift;
+    my $do_not_redirect = shift;
 
     my $uri;
     if (!defined ($req->{uri})) {
@@ -365,7 +366,9 @@ sub request {
         $uri,
         $headers,
         $content,
-        $request_timeout
+        $request_timeout,
+        undef,
+        $do_not_redirect
     );
     if (!defined ($res)) {
         $logger->error('EbookAPI: no HTTP response received');
@@ -375,7 +378,8 @@ sub request {
         return {
             is_success => $res->is_success,
             status     => $res->status_line,
-            content    => OpenSRF::Utils::JSON->JSON2perl($res->decoded_content)
+            content    => OpenSRF::Utils::JSON->JSON2perl($res->decoded_content),
+            location   => $res->header('Location')
         };
     }
 }
index 6eca31e..9853c5b 100644 (file)
@@ -156,13 +156,15 @@ sub initialize {
 sub handle_http_request {
     my $self = shift;
     my $req = shift;
+    my $session_id = shift;
+    my $do_not_redirect = shift;
 
     # Prep our request using defaults.
     $req->{method} = 'GET' if (!$req->{method});
     $req = $self->set_http_headers($req);
 
     # Send the request.
-    my $res = $self->request($req, $self->{session_id});
+    my $res = $self->request($req, $self->{session_id}, $do_not_redirect);
 
     $logger->info("EbookAPI: raw OverDrive HTTP response: " . Dumper $res);
 
@@ -182,7 +184,7 @@ sub handle_http_request {
         # Now we can update our headers with our fresh client/patron tokens
         # and re-send our request.
         $req = $self->set_http_headers($req);
-        return $self->request($req, $self->{session_id});
+        return $self->request($req, $self->{session_id}, $do_not_redirect);
     }
 
     # For any non-401 response (including no response at all),
@@ -555,6 +557,18 @@ sub checkout {
                 }
                 $checkout->{formats} = $formats;
             }
+            if ($res->{content}->{links}->{downloadRedirect}->{href}) {
+                my $redir = $res->{content}->{links}->{downloadRedirect}->{href};
+                my $req2 = {
+                    method => 'GET',
+                    uri    => $redir
+                };
+                if (my $res2 = $self->handle_http_request($req2, $self->{session_id}, 1)) {
+                    if ($res2->{location}) {
+                        $checkout->{download_redirect} = $res2->{location};
+                    }
+                }
+            }
             return $checkout;
         }
         $logger->error("EbookAPI: checkout failed for OverDrive title $title_id");
@@ -680,12 +694,26 @@ sub get_patron_checkouts {
                 my $ftype = $f->{formatType};
                 $formats->{$ftype} = $f->{linkTemplates}->{downloadLink}->{href};
             };
+            my $download_redirect = '';
+            my $redirect = $checkout->{links}->{downloadRedirect}->{href};
+            if ($redirect) {
+                my $req2 = {
+                    method => 'GET',
+                    uri    => $redirect
+                };
+                if (my $res2 = $self->handle_http_request($req2, $self->{session_id}, 1)) {
+                    if ($res2->{location}) {
+                        $download_redirect = $res2->{location};
+                    }
+                }
+            }
             push @$checkouts, {
                 title_id => $title_id,
                 due_date => $checkout->{expires},
                 title => $title_info->{title},
                 author => $title_info->{author},
-                formats => $formats
+                formats => $formats,
+                download_redirect => $download_redirect
             }
         };
         $self->{checkouts} = $checkouts;
index 6474586..a0f6eae 100644 (file)
@@ -67,7 +67,7 @@ sub _initialize {
 # Use $res->content to get response content.
 #
 sub request {
-    my ($self, $method, $uri, $headers, $content, $request_timeout, $useragent) = @_;
+    my ($self, $method, $uri, $headers, $content, $request_timeout, $useragent, $do_not_redirect) = @_;
     my $ua = new LWP::UserAgent;
 
     $request_timeout = $request_timeout || $self->{default_timeout} || 60;
@@ -75,6 +75,9 @@ sub request {
 
     $useragent = $useragent || $self->{useragent} || 'SameOrigin/1.0';
     $ua->agent($useragent);
+    if ($do_not_redirect) {
+        $ua->requests_redirectable([]);
+    }
 
     my $h = HTTP::Headers->new();
     foreach my $k (keys %$headers) {
index 569260c..9b7323a 100644 (file)
@@ -146,7 +146,9 @@ function updateCheckoutView() {
             if (x.download_url) {
                 dl_td.innerHTML = '<a href="' + x.download_url + '">' + l_strings.download + '</a>';
             }
-            if (x.formats) {
+            if (x.download_redirect) {
+                dl_td.innerHTML = '<a target="_blank" href="' + x.download_redirect + '">' + l_strings.download + '</a>';
+            } else if (x.formats) {
                 var select = dojo.create("select", { id: "download-format" }, dl_td);
                 for (f in x.formats) {
                     dojo.create("option", { value: x.formats[f], innerHTML: f }, select);
@@ -319,6 +321,11 @@ function doCheckout() {
             new_xact.download_url = resp.download_url;
             dojo.create("a", { href: new_xact.download_url, innerHTML: l_strings.download }, dojo.byId('checkout-button-td'));
             new_xact.finish();
+        } else if (resp.download_redirect) {
+            // Use download URL from checkout response, if available.
+            new_xact.download_redirect = resp.download_redirect;
+            dojo.create("a", { target: "_blank", href: new_xact.download_redirect, innerHTML: l_strings.download }, dojo.byId('checkout-button-td'));
+            new_xact.finish();
         } else if (typeof resp.formats !== 'undefined') {
             // User must select download format from list of options.
             var select = dojo.create("select", { id: "download-format" }, dojo.byId('checkout-button-td'));