3 # Copyright (C) 2015 BC Libraries Cooperative
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 package OpenILS::Application::EbookAPI::OneClickdigital;
24 use OpenILS::Application;
25 use OpenILS::Application::EbookAPI;
26 use base qw/OpenILS::Application::EbookAPI/;
27 use OpenSRF::AppSession;
28 use OpenSRF::EX qw(:try);
29 use OpenSRF::Utils::SettingsClient;
30 use OpenSRF::Utils::Logger qw($logger);
31 use OpenSRF::Utils::Cache;
32 use OpenILS::Application::AppUtils;
36 my( $class, $args ) = @_;
37 $class = ref $class || $class;
38 return bless $args, $class;
48 return $self->{vendor};
53 return $self->{session_id};
58 return $self->{base_uri};
63 return $self->{library_id};
68 return $self->{basic_token};
73 return $self->{patron_id};
80 my $base_uri = 'https://api.oneclickdigital.com/v1';
81 $self->{base_uri} = OpenILS::Application::AppUtils->ou_ancestor_setting_value($ou, 'ebook_api.oneclickdigital.base_uri') || $base_uri;
83 my $library_id = OpenILS::Application::AppUtils->ou_ancestor_setting_value($ou, 'ebook_api.oneclickdigital.library_id');
85 $self->{library_id} = $library_id;
87 $logger->error("EbookAPI: no OneClickdigital library ID found for org unit $ou");
91 my $basic_token = OpenILS::Application::AppUtils->ou_ancestor_setting_value($ou, 'ebook_api.oneclickdigital.basic_token');
93 $self->{basic_token} = $basic_token;
95 $logger->error("EbookAPI: no OneClickdigital basic token found for org unit $ou");
103 # OneClickdigital API does not require separate client auth;
104 # we just need to include our basic auth token in requests
110 # retrieve OneClickdigital patron ID (if any) based on patron barcode
111 # GET http://api.oneclickdigital.us/v1/rpc/libraries/{libraryID}/patrons/{barcode}
113 my ($self, $barcode) = @_;
114 my $base_uri = $self->{base_uri};
115 my $library_id = $self->{library_id};
116 my $session_id = $self->{session_id};
119 uri => "$base_uri/rpc/libraries/$library_id/patrons/$barcode"
121 my $res = $self->request($req, $session_id);
122 # TODO distinguish between unregistered patrons and patron auth failure
123 if (defined ($res) && $res->{content}->{patronId}) {
124 return $res->{content}->{patronId};
126 $logger->error("EbookAPI: no OneClickdigital patron ID found for barcode $barcode");
130 # get basic metadata for an item (title, author, cover image if any)
131 # GET http://api.oneclickdigital.us/v1/libraries/{libraryId}/media/{isbn}
133 my ($self, $isbn) = @_;
134 my $base_uri = $self->{base_uri};
135 my $library_id = $self->{library_id};
136 my $session_id = $self->{session_id};
139 uri => "$base_uri/libraries/$library_id/media/$isbn"
141 my $res = $self->request($req, $session_id);
142 if (defined ($res)) {
144 title => $res->{content}->{title},
145 author => $res->{content}->{authors}
148 $logger->error("EbookAPI: could not retrieve OneClickdigital title details for ISBN $isbn");
153 # does this title have available "copies"? y/n
154 # GET http://api.oneclickdigital.us/v1/libraries/{libraryID}/media/{isbn}/availability
155 sub do_availability_lookup {
156 my ($self, $isbn) = @_;
157 my $base_uri = $self->{base_uri};
158 my $library_id = $self->{library_id};
159 my $session_id = $self->{session_id};
162 uri => "$base_uri/libraries/$library_id/media/$isbn/availability"
164 my $res = $self->request($req, $session_id);
165 if (defined ($res)) {
166 $logger->info("EbookAPI: received availability response for ISBN $isbn: " . Dumper $res);
167 return $res->{content}->{availability};
169 $logger->error("EbookAPI: could not retrieve OneClickdigital availability for ISBN $isbn");
174 # OneClickdigital API does not support detailed holdings lookup,
175 # so we return basic availability information.
176 sub do_holdings_lookup {
177 my ($self, $isbn) = @_;
178 my $avail = $self->do_availability_lookup($isbn);
179 return { available => $avail };
182 # checkout an item to a patron
183 # item is identified by ISBN, patron ID is their barcode
184 # POST //api.{domain}/v1/libraries/{libraryId}/patrons/{patronId}/checkouts/{isbn}
186 my ($self, $isbn, $patron_id) = @_;
187 my $base_uri = $self->{base_uri};
188 my $library_id = $self->{library_id};
189 my $session_id = $self->{session_id};
192 uri => "$base_uri/libraries/$library_id/patrons/$patron_id/checkouts/$isbn",
193 headers => { "Content-Length" => "0" }
195 my $res = $self->request($req, $session_id);
197 # TODO: more sophisticated response handling
198 # HTTP 200 response indicates success, HTTP 409 indicates checkout limit reached
199 if (defined ($res)) {
200 if ($res->{is_success}) {
202 xact_id => $res->{content}->{transactionId},
203 due_date => $res->{content}->{expiration}
206 $logger->error("EbookAPI: checkout failed for OneClickdigital title $isbn");
207 return { error_msg => $res->{content} };
210 $logger->error("EbookAPI: no response received from OneClickdigital server");
215 # renew a checked-out item
216 # item id = ISBN, patron id = barcode
217 # PUT //api.{domain}/v1/libraries/{libraryId}/patrons/{patronId}/checkouts/{isbn}
219 my ($self, $isbn, $patron_id) = @_;
220 my $base_uri = $self->{base_uri};
221 my $library_id = $self->{library_id};
222 my $session_id = $self->{session_id};
225 uri => "$base_uri/libraries/$library_id/patrons/$patron_id/checkouts/$isbn",
226 headers => { "Content-Length" => "0" }
228 my $res = $self->request($req, $session_id);
230 # TODO: more sophisticated response handling
231 # HTTP 200 response indicates success
232 if (defined ($res)) {
233 if ($res->{is_success}) {
235 xact_id => $res->{content}->{transactionId},
236 due_date => $res->{content}->{expiration}
239 $logger->error("EbookAPI: renewal failed for OneClickdigital title $isbn");
240 return { error_msg => $res->{content} };
243 $logger->error("EbookAPI: no response received from OneClickdigital server");
248 # checkin a checked-out item
249 # item id = ISBN, patron id = barcode
250 # XXX API docs indicate that a bearer token is required!
251 # DELETE //api.{domain}/v1/libraries/{libraryId}/patrons/{patronId}/checkouts/{isbn}
261 # GET //api.{domain}/v1/libraries/{libraryId}/patrons/{patronId}/checkouts/all
262 sub get_patron_checkouts {
263 my ($self, $patron_id) = @_;
264 my $base_uri = $self->{base_uri};
265 my $library_id = $self->{library_id};
266 my $session_id = $self->{session_id};
269 uri => "$base_uri/libraries/$library_id/patrons/$patron_id/checkouts/all"
271 my $res = $self->request($req, $session_id);
274 if (defined ($res)) {
275 $logger->info("EbookAPI: received response for OneClickdigital checkouts: " . Dumper $res);
276 foreach my $checkout (@{$res->{content}}) {
278 xact_id => $checkout->{transactionId},
279 title_id => $checkout->{isbn},
280 due_date => $checkout->{expiration},
281 download_url => $checkout->{downloadUrl},
282 title => $checkout->{title},
283 author => $checkout->{authors}
286 $logger->info("EbookAPI: retrieved " . scalar(@$checkouts) . " OneClickdigital checkouts for patron $patron_id");
287 $self->{checkouts} = $checkouts;
288 return $self->{checkouts};
290 $logger->error("EbookAPI: failed to retrieve OneClickdigital checkouts for patron $patron_id");
295 # GET //api.{domain}/v1/libraries/{libraryId}/patrons/{patronId}/holds/all
296 sub get_patron_holds {
297 my ($self, $patron_id) = @_;
298 my $base_uri = $self->{base_uri};
299 my $library_id = $self->{library_id};
300 my $session_id = $self->{session_id};
303 uri => "$base_uri/libraries/$library_id/patrons/$patron_id/holds/all"
305 my $res = $self->request($req, $session_id);
308 if (defined ($res)) {
309 $logger->info("EbookAPI: received response for OneClickdigital holds: " . Dumper $res);
310 foreach my $hold (@{$res->{content}}) {
312 xact_id => $hold->{transactionId},
313 title_id => $hold->{isbn},
314 expire_date => $hold->{expiration},
315 title => $hold->{title},
316 author => $hold->{authors},
317 # XXX queue position/size and pending vs ready info not available via API
318 queue_position => '-',
323 $logger->info("EbookAPI: retrieved " . scalar(@$holds) . " OneClickdigital holds for patron $patron_id");
324 $self->{holds} = $holds;
325 return $self->{holds};
327 $logger->error("EbookAPI: failed to retrieve OneClickdigital holds for patron $patron_id");