| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package WebService::ILS::OverDrive; |
|
2
|
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
1021
|
use Modern::Perl; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
17
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=encoding utf-8 |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 NAME |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
WebService::ILS::OverDrive - WebService::ILS module for OverDrive services |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
use WebService::ILS::OverDrive::Library; |
|
14
|
|
|
|
|
|
|
or |
|
15
|
|
|
|
|
|
|
use WebService::ILS::OverDrive::Patron; |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
L - anonymous discovery |
|
20
|
|
|
|
|
|
|
services - no individual user credentials required |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
L - discovery and circulation |
|
23
|
|
|
|
|
|
|
services that require individual user credentials |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
See L |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=cut |
|
28
|
|
|
|
|
|
|
|
|
29
|
3
|
|
|
3
|
|
309
|
use Carp; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
157
|
|
|
30
|
3
|
|
|
3
|
|
14
|
use HTTP::Request::Common; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
128
|
|
|
31
|
3
|
|
|
3
|
|
14
|
use URI::Escape; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
178
|
|
|
32
|
|
|
|
|
|
|
|
|
33
|
3
|
|
|
3
|
|
18
|
use parent qw(WebService::ILS::JSON); |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
22
|
|
|
34
|
|
|
|
|
|
|
|
|
35
|
3
|
|
|
3
|
|
151
|
use constant API_VERSION => "v1"; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
169
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
3
|
|
|
3
|
|
20
|
use constant DISCOVERY_API_URL => "http://api.overdrive.com/"; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
169
|
|
|
38
|
3
|
|
|
3
|
|
22
|
use constant TEST_DISCOVERY_API_URL => "http://integration.api.overdrive.com/"; |
|
|
3
|
|
|
|
|
5
|
|
|
|
3
|
|
|
|
|
287
|
|
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head2 new (%params_hash or $params_hashref) |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head3 Additional constructor params: |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=over 10 |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=item C => if set to true use OverDrive test API urls |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=back |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=cut |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
use Class::Tiny qw( |
|
55
|
|
|
|
|
|
|
collection_token |
|
56
|
|
|
|
|
|
|
test |
|
57
|
|
|
|
|
|
|
), { |
|
58
|
0
|
0
|
|
|
|
0
|
_discovery_api_url => sub { $_[0]->test ? TEST_DISCOVERY_API_URL : DISCOVERY_API_URL }, |
|
59
|
3
|
|
|
3
|
|
19
|
}; |
|
|
3
|
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
29
|
|
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
__PACKAGE__->_set_param_spec({ |
|
62
|
|
|
|
|
|
|
test => { required => 0 }, |
|
63
|
|
|
|
|
|
|
}); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 DISCOVERY METHODS |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head2 search ($params_hashref) |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head3 Additional input params: |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=over 16 |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=item C => if true, no metadata calls will be made for result items; |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
only id, title, rating and media will be available |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=back |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
my %SORT_XLATE = ( |
|
82
|
|
|
|
|
|
|
available_date => "dateadded", |
|
83
|
|
|
|
|
|
|
rating => "starrating", |
|
84
|
|
|
|
|
|
|
publication_date => undef, # not available |
|
85
|
|
|
|
|
|
|
); |
|
86
|
|
|
|
|
|
|
sub search { |
|
87
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
88
|
0
|
|
0
|
|
|
|
my $params = shift || {}; |
|
89
|
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
my $short_response = delete $params->{no_details}; |
|
91
|
|
|
|
|
|
|
|
|
92
|
0
|
|
|
|
|
|
my $url = $self->products_url; |
|
93
|
|
|
|
|
|
|
|
|
94
|
0
|
0
|
|
|
|
|
if (my $query = delete $params->{query}) { |
|
95
|
0
|
0
|
|
|
|
|
$query = join " ", @$query if ref $query; |
|
96
|
0
|
|
|
|
|
|
$params->{q} = $query; |
|
97
|
|
|
|
|
|
|
} |
|
98
|
0
|
|
|
|
|
|
my $page_size = delete $params->{page_size}; |
|
99
|
0
|
0
|
|
|
|
|
$params->{limit} = $page_size if $page_size; |
|
100
|
0
|
0
|
|
|
|
|
if (my $page_number = delete $params->{page}) { |
|
101
|
0
|
0
|
|
|
|
|
croak "page_size must be specified for paging" unless $params->{limit}; |
|
102
|
0
|
|
|
|
|
|
$params->{offset} = ($page_number - 1)*$page_size; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
0
|
0
|
|
|
|
|
if (my $sort = delete $params->{sort}) { |
|
105
|
0
|
|
|
|
|
|
$params->{sort} = join ",", @{ $self->_parse_sort_string($sort, \%SORT_XLATE) }; |
|
|
0
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
} |
|
107
|
0
|
0
|
|
|
|
|
$params->{formats} = join ",", @{$params->{formats}} if ref $params->{formats}; |
|
|
0
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
my $res = $self->get_response($url, $params); |
|
110
|
0
|
|
|
|
|
|
my @items; |
|
111
|
0
|
0
|
|
|
|
|
foreach (@{$res->{products} || []}) { |
|
|
0
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
my $item; |
|
113
|
0
|
0
|
|
|
|
|
if ($short_response) { |
|
114
|
0
|
|
|
|
|
|
$item = $self->_item_xlate($_); |
|
115
|
|
|
|
|
|
|
} else { |
|
116
|
0
|
0
|
|
|
|
|
my $native_metadata = $self->native_item_metadata($_) or next; |
|
117
|
0
|
|
|
|
|
|
$item = $self->_item_metadata_xlate($native_metadata); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
0
|
0
|
|
|
|
|
next unless $item; |
|
120
|
0
|
|
|
|
|
|
push @items, $item; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
0
|
|
|
|
|
|
my $tot = $res->{totalItems}; |
|
123
|
0
|
|
|
|
|
|
my %ret = ( |
|
124
|
|
|
|
|
|
|
total => $tot, |
|
125
|
|
|
|
|
|
|
items => \@items, |
|
126
|
|
|
|
|
|
|
); |
|
127
|
0
|
0
|
|
|
|
|
if (my $page_size = $res->{limit}) { |
|
128
|
0
|
|
|
|
|
|
my $pages = int($tot/$page_size); |
|
129
|
0
|
0
|
|
|
|
|
$pages++ if $tot > $page_size*$pages; |
|
130
|
0
|
|
|
|
|
|
$ret{pages} = $pages; |
|
131
|
0
|
|
|
|
|
|
$ret{page_size} = $page_size; |
|
132
|
0
|
|
|
|
|
|
$ret{page} = $res->{offset}/$page_size + 1; |
|
133
|
|
|
|
|
|
|
} |
|
134
|
0
|
|
|
|
|
|
return \%ret; |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
my %SEARCH_RESULT_ITEM_XLATE = ( |
|
138
|
|
|
|
|
|
|
id => "id", |
|
139
|
|
|
|
|
|
|
title => "title", |
|
140
|
|
|
|
|
|
|
subtitle => "subtitle", |
|
141
|
|
|
|
|
|
|
starRating => "rating", |
|
142
|
|
|
|
|
|
|
mediaType => "media", |
|
143
|
|
|
|
|
|
|
); |
|
144
|
|
|
|
|
|
|
sub _item_xlate { |
|
145
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
146
|
0
|
|
|
|
|
|
my $item = shift; |
|
147
|
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
|
my $std_item = $self->_result_xlate($item, \%SEARCH_RESULT_ITEM_XLATE); |
|
149
|
|
|
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
|
if (my $formats = $item->{formats}) { |
|
151
|
0
|
|
|
|
|
|
$std_item->{formats} = [map $_->{id}, @$formats]; |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
0
|
0
|
|
|
|
|
if (my $images = $item->{images}) { |
|
155
|
0
|
|
|
|
|
|
$std_item->{images} = {map { $_ => $images->{$_}{href} } keys %$images}; |
|
|
0
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# XXX |
|
159
|
|
|
|
|
|
|
#if (my $details = $item->{contentDetails}) { |
|
160
|
|
|
|
|
|
|
# $std_item->{details_url} = $details->{href}; |
|
161
|
|
|
|
|
|
|
#} |
|
162
|
|
|
|
|
|
|
|
|
163
|
0
|
|
|
|
|
|
return $std_item; |
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
my %METADATA_XLATE = ( |
|
167
|
|
|
|
|
|
|
id => "id", |
|
168
|
|
|
|
|
|
|
mediaType => "media", |
|
169
|
|
|
|
|
|
|
title => "title", |
|
170
|
|
|
|
|
|
|
publisher => "publisher", |
|
171
|
|
|
|
|
|
|
shortDescription => "subtitle", |
|
172
|
|
|
|
|
|
|
starRating => "rating", |
|
173
|
|
|
|
|
|
|
popularity => "popularity", |
|
174
|
|
|
|
|
|
|
); |
|
175
|
|
|
|
|
|
|
sub item_metadata { |
|
176
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
177
|
0
|
0
|
|
|
|
|
my $id = shift or croak "No item id"; |
|
178
|
0
|
|
|
|
|
|
my $native_metadata = $self->get_response($self->products_url."/$id/metadata"); |
|
179
|
0
|
|
|
|
|
|
return $self->_item_metadata_xlate($native_metadata); |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub _item_metadata_xlate { |
|
183
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
184
|
0
|
0
|
|
|
|
|
my $metadata = shift or croak "No native metadata"; |
|
185
|
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
|
my $item = $self->_result_xlate($metadata, \%METADATA_XLATE); |
|
187
|
|
|
|
|
|
|
|
|
188
|
0
|
|
|
|
|
|
my @authors; |
|
189
|
0
|
|
|
|
|
|
foreach (@{ $metadata->{creators} }) { |
|
|
0
|
|
|
|
|
|
|
|
190
|
0
|
0
|
|
|
|
|
push @authors, $_->{name} if $_->{role} eq "Author"; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
0
|
|
|
|
|
|
$item->{author} = join ", ", @authors; |
|
193
|
|
|
|
|
|
|
|
|
194
|
0
|
0
|
|
|
|
|
if (my $images = $metadata->{images}) { |
|
195
|
0
|
|
|
|
|
|
$item->{images} = {map { $_ => $images->{$_}{href} } keys %$images}; |
|
|
0
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
0
|
0
|
|
|
|
|
if (my $languages = $metadata->{languages}) { |
|
199
|
0
|
|
|
|
|
|
$item->{languages} = [map $_->{name}, @$languages]; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
0
|
0
|
|
|
|
|
if (my $subjects = $metadata->{subjects}) { |
|
203
|
0
|
|
|
|
|
|
$item->{subjects} = [map $_->{value}, @$subjects]; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
0
|
0
|
|
|
|
|
if (my $formats = $metadata->{formats}) { |
|
207
|
0
|
|
|
|
|
|
$item->{formats} = [map $_->{id}, @$formats]; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
return $item; |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
my %AVAILABILITY_RESULT_XLATE = ( |
|
214
|
|
|
|
|
|
|
id => "id", |
|
215
|
|
|
|
|
|
|
available => "available", |
|
216
|
|
|
|
|
|
|
copiesAvailable => "copies_available", |
|
217
|
|
|
|
|
|
|
copiesOwned => "copies_owned", |
|
218
|
|
|
|
|
|
|
availabilityType => "type", |
|
219
|
|
|
|
|
|
|
); |
|
220
|
|
|
|
|
|
|
sub item_availability { |
|
221
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
222
|
0
|
0
|
|
|
|
|
my $id = shift or croak "No item id"; |
|
223
|
0
|
|
|
|
|
|
return $self->_result_xlate( |
|
224
|
|
|
|
|
|
|
$self->get_response($self->products_url."/$id/availability"), |
|
225
|
|
|
|
|
|
|
\%AVAILABILITY_RESULT_XLATE |
|
226
|
|
|
|
|
|
|
); |
|
227
|
|
|
|
|
|
|
} |
|
228
|
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub is_item_available { |
|
230
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
231
|
0
|
0
|
|
|
|
|
my $id = shift or croak "No item id"; |
|
232
|
0
|
|
|
|
|
|
my $type = shift; |
|
233
|
|
|
|
|
|
|
|
|
234
|
0
|
0
|
|
|
|
|
my $availability = $self->item_availability($id) or return; |
|
235
|
0
|
0
|
|
|
|
|
return unless $availability->{available}; |
|
236
|
0
|
|
0
|
|
|
|
return !$type || $type eq $availability->{type}; |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head1 NATIVE METHODS |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=head2 native_search ($params_hashref) |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
See L |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=head2 native_search_[next|prev|first|last] ($data_as returned_by_native_search*) |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
For iterating through search result pages. Each native_search_*() method |
|
248
|
|
|
|
|
|
|
accepts record returned by any native_search*() method as input. |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Example: |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
my $res = $od->native_search({q => "Dogs"}); |
|
253
|
|
|
|
|
|
|
while ($res) { |
|
254
|
|
|
|
|
|
|
do_something($res); |
|
255
|
|
|
|
|
|
|
$res = $od->native_search_next($res); |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
or |
|
258
|
|
|
|
|
|
|
my $res = $od->native_search({q => "Dogs"}); |
|
259
|
|
|
|
|
|
|
my $last = $od->native_search_last($res); |
|
260
|
|
|
|
|
|
|
my $next_to_last = $od->native_search_prev($last); |
|
261
|
|
|
|
|
|
|
my $first = $od->native_search_first($next_to_last) |
|
262
|
|
|
|
|
|
|
# Same as $od->native_search_first($last) |
|
263
|
|
|
|
|
|
|
# Same as $res |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=cut |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# params: q, limit, offset, formats, sort ? availability |
|
268
|
|
|
|
|
|
|
sub native_search { |
|
269
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
270
|
0
|
|
|
|
|
|
my $search_params = shift; |
|
271
|
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
|
return $self->get_response($self->products_url, $search_params); |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
foreach my $f (qw(next prev first last)) { |
|
276
|
3
|
|
|
3
|
|
4479
|
no strict 'refs'; |
|
|
3
|
|
|
|
|
7
|
|
|
|
3
|
|
|
|
|
1452
|
|
|
277
|
|
|
|
|
|
|
my $method = "native_search_$f"; |
|
278
|
|
|
|
|
|
|
*$method = sub { |
|
279
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
280
|
0
|
0
|
|
|
|
|
my $search_data = shift or croak "No search result data"; |
|
281
|
0
|
0
|
|
|
|
|
my $url = _extract_link($search_data, $f) or return; |
|
282
|
0
|
|
|
|
|
|
return $self->get_response($url); |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
# Item API |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head2 native_item_metadata ($item_data as returned by native_search*) |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
=head2 native_item_availability ($item_data as returned by native_search*) |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Example: |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
my $res = $od->native_search({q => "Dogs"}); |
|
295
|
|
|
|
|
|
|
foreach (@{ $res->{products} }) { |
|
296
|
|
|
|
|
|
|
my $meta = $od->native_item_metadata($_); |
|
297
|
|
|
|
|
|
|
my $availability = $od->native_item_availability($_); |
|
298
|
|
|
|
|
|
|
... |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=cut |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub native_item_metadata { |
|
304
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
305
|
0
|
0
|
|
|
|
|
my $item = shift or croak "No item record"; |
|
306
|
|
|
|
|
|
|
|
|
307
|
0
|
0
|
|
|
|
|
my $url = _extract_link($item, 'metadata') or die "No metadata link\n"; |
|
308
|
0
|
|
|
|
|
|
return $self->get_response($url); |
|
309
|
|
|
|
|
|
|
} |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub native_item_availability { |
|
312
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
313
|
0
|
0
|
|
|
|
|
my $item = shift or croak "No item record"; |
|
314
|
0
|
|
|
|
|
|
return $self->get_response(_extract_link($item, 'availability')); |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# Discovery helpers |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub discovery_action_url { |
|
320
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
321
|
0
|
|
|
|
|
|
my $action = shift; |
|
322
|
0
|
|
|
|
|
|
return $self->_discovery_api_url.$self->API_VERSION.$action; |
|
323
|
|
|
|
|
|
|
} |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
sub products_url { |
|
326
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
327
|
|
|
|
|
|
|
|
|
328
|
0
|
0
|
|
|
|
|
my $collection_token = $self->collection_token or die "No collection token"; |
|
329
|
|
|
|
|
|
|
|
|
330
|
0
|
0
|
|
|
|
|
if ($collection_token) { |
|
331
|
0
|
|
|
|
|
|
return $self->_discovery_api_url.$self->API_VERSION."/collections/$collection_token/products"; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
# API helpers |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub _extract_link { |
|
338
|
0
|
|
|
0
|
|
|
my ($data, $link) = @_; |
|
339
|
|
|
|
|
|
|
my $href = $data->{links}{$link}{href} |
|
340
|
0
|
0
|
|
|
|
|
or croak "No '$link' url in data"; |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# Utility methods |
|
344
|
|
|
|
|
|
|
|
|
345
|
0
|
|
|
0
|
|
|
sub _basic_callback { return $_[0]; } |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# This is not exatly how we meant to use with_get_request() |
|
348
|
|
|
|
|
|
|
# ie processing should be placed within the callback. |
|
349
|
|
|
|
|
|
|
# However, if all goes well, it is faster (from the development perspective) |
|
350
|
|
|
|
|
|
|
# this way. |
|
351
|
|
|
|
|
|
|
sub get_response { |
|
352
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
353
|
0
|
0
|
|
|
|
|
my $url = shift or croak "No url"; |
|
354
|
0
|
|
|
|
|
|
my $get_params = shift; # hash ref |
|
355
|
|
|
|
|
|
|
|
|
356
|
0
|
|
|
|
|
|
return $self->with_get_request(\&_basic_callback, $url, $get_params); |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub _error_from_json { |
|
360
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
361
|
0
|
0
|
|
|
|
|
my $data = shift or croak "No json data"; |
|
362
|
0
|
|
0
|
|
|
|
my $error = join " ", grep defined($_), $data->{errorCode}, $data->{error_description} || $data->{error} || $data->{message} || $data->{Message}; |
|
363
|
0
|
0
|
|
|
|
|
$error = "$error\n" if $error; # strip code line when dying |
|
364
|
0
|
|
|
|
|
|
return $error; |
|
365
|
|
|
|
|
|
|
} |
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
1; |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
__END__ |