File Coverage

blib/lib/Net/Amazon/Request.pm
Criterion Covered Total %
statement 84 120 70.0
branch 25 40 62.5
condition n/a
subroutine 20 27 74.0
pod 1 5 20.0
total 130 192 67.7


line stmt bran cond sub pod time code
1             ######################################################################
2             package Net::Amazon::Request;
3             ######################################################################
4              
5 9     9   14181 use Log::Log4perl qw(:easy get_logger);
  9         1241445  
  9         86  
6 9     9   20954 use Net::Amazon::Validate::Type;
  9         29  
  9         300  
7 9     9   6821 use Net::Amazon::Validate::ItemSearch;
  9         27  
  9         268  
8              
9 9     9   52 use Data::Dumper;
  9         18  
  9         463  
10              
11 9     9   47 use warnings;
  9         19  
  9         191  
12 9     9   40 use strict;
  9         17  
  9         302  
13 9     9   52 use constant DEFAULT_MODE => 'books';
  9         18  
  9         1228  
14 9     9   48 use constant DEFAULT_TYPE => 'Large';
  9         16  
  9         458  
15 9     9   65 use constant DEFAULT_PAGE_COUNT => 1;
  9         158  
  9         397  
16 9     9   59 use constant DEFAULT_FORMAT => 'xml';
  9         23  
  9         458  
17 9     9   48 use constant PAGE_NOT_VALID => qw(TextStream);
  9         13  
  9         462  
18              
19             # Attempt to provide backward compatability for AWS3 types.
20 9         1058 use constant AWS3_VALID_TYPES_MAP => {
21             'heavy' => 'Large',
22             'lite' => 'Medium',
23 9     9   44 };
  9         22  
24              
25             # Each key represents the REST operation used to execute the action.
26 9         839 use constant SEARCH_TYPE_OPERATION_MAP => {
27             Actor => 'ItemSearch',
28             Artist => 'ItemSearch',
29             All => 'ItemSearch',
30             Author => 'ItemSearch',
31             ASIN => 'ItemLookup',
32             Blended => 'ItemSearch',
33             BrowseNode => 'ItemSearch',
34             Director => 'ItemSearch',
35             EAN => 'ItemLookup',
36             Exchange => 'SellerListingLookup',
37             ISBN => 'ItemLookup',
38             Keyword => 'ItemSearch',
39             Keywords => 'ItemSearch',
40             Manufacturer => 'ItemSearch',
41             MP3Downloads => 'ItemSearch',
42             MusicLabel => 'ItemSearch',
43             Power => 'ItemSearch',
44             Publisher => 'ItemSearch',
45             Seller => 'SellerListingSearch',
46             Similar => 'SimilarityLookup',
47             TextStream => 'ItemSearch',
48             Title => 'ItemSearch',
49             UPC => 'ItemLookup',
50 9     9   46 };
  9         29  
51              
52             # if it isn't defined it defaults to salesrank
53 9         543 use constant DEFAULT_SORT_CRITERIA_MAP => {
54             All => '',
55             Blended => '',
56             Seller => '',
57             Exchange => '',
58 9     9   49 };
  9         17  
59              
60             # if it isn't defined it defaults to ItemPage
61 9         15268 use constant DEFAULT_ITEM_PAGE_MAP => {
62             Seller => 'ListingPage',
63 9     9   59 };
  9         21  
64              
65             our $AMZN_XML_URL = 'http://webservices.amazon.com/onca/xml?Service=AWSECommerceService';
66              
67             ##################################################
68             sub amzn_xml_url {
69             ##################################################
70 0     0 0 0 return $AMZN_XML_URL;
71             }
72              
73             ##################################################
74             sub new {
75             ##################################################
76 15     15 0 38 my($class, %options) = @_;
77              
78 15         170 my ($operation) = $class =~ m/([^:]+)$/;
79              
80 15         81 my $self = {
81             Operation => SEARCH_TYPE_OPERATION_MAP->{$operation},
82             %options,
83             };
84              
85 15 50       69 $self->{page} = DEFAULT_PAGE_COUNT unless exists $self->{page};
86              
87             # TextStream doesn't allow a page (ItemPage) parameter
88 15 100       28 delete $self->{page} if grep{$operation eq $_} (PAGE_NOT_VALID);
  15         64  
89              
90             # salesrank isn't a valid sort criteria for all operations
91 15 50       43 if (! exists $self->{sort}) {
92 15 100       45 my $sort = (defined DEFAULT_SORT_CRITERIA_MAP->{$operation})
93             ? DEFAULT_SORT_CRITERIA_MAP->{$operation} : 'salesrank';
94 15 100       55 $self->{sort} = $sort if length($sort);
95             }
96              
97 15         68 my $valid = Net::Amazon::Validate::Type::factory(operation => $self->{Operation});
98              
99             # There is no initial default type (ResponseGroup) defined,
100             # if there is, then attempt to map the AWS3 type to the
101             # AWS4 type.
102 15 50       43 if ($self->{type}) {
103 0 0       0 if ( ref $self->{type} eq 'ARRAY' ) {
104 0         0 my @types;
105 0         0 for (@{$self->{type}}) {
  0         0  
106 0         0 push @types, _get_valid_response_group($_, $valid);
107             }
108 0         0 $self->{type} = join(',', @types);
109             } else {
110 0         0 $self->{type} = _get_valid_response_group($self->{type}, $valid);
111             }
112             }
113             # If no type was defined then try to default to Large, which is a good
114             # all around response group. If Large is not a valid response group
115             # let Amazon pick.
116             else {
117 15         23 eval { $valid->ResponseGroup(DEFAULT_TYPE) };
  15         51  
118 15 100       55 $self->{type} = DEFAULT_TYPE unless $@;
119             }
120              
121 15 100       38 my $item_page = (defined DEFAULT_ITEM_PAGE_MAP->{$operation})
122             ? DEFAULT_ITEM_PAGE_MAP->{$operation} : 'ItemPage';
123            
124 15         71 __PACKAGE__->_convert_option($self, 'page', $item_page);
125 15         40 __PACKAGE__->_convert_option($self, 'sort', 'Sort');
126 15 100       53 __PACKAGE__->_convert_option($self, 'type', 'ResponseGroup') if defined $self->{type};
127              
128             # Convert all of the normal user input into Amazon's expected input. Do it
129             # here to allow a user to narrow down there based on any field that is valid
130             # for a search operation.
131             #
132             # One could add all of the different qualifiers for an ItemSearch for free.
133 15 100       45 if (SEARCH_TYPE_OPERATION_MAP->{$operation} eq 'ItemSearch' ) {
134 11         12 for (keys %{(SEARCH_TYPE_OPERATION_MAP)}) {
  11         74  
135 253 50       501 __PACKAGE__->_convert_option($self, lc($_), $_) if defined $self->{lc($_)};
136             }
137             }
138              
139 15         137 bless $self, $class;
140             }
141              
142             ##################################################
143             sub page {
144             ##################################################
145 0     0 1 0 my($self) = @_;
146 0         0 return $self->{$self->_page_type};
147             }
148              
149             ##################################################
150             sub params {
151             ##################################################
152 0     0 0 0 my ($self, %options) = @_;
153              
154 0         0 my $class = ref $self;
155 0         0 my ($operation) = $class =~ m/([^:]+)$/;
156              
157 0 0       0 unless (grep{$operation eq $_} (PAGE_NOT_VALID)) {
  0         0  
158 0         0 my $type = $self->_page_type;
159 0         0 $self->{$type} = $options{page};
160             }
161              
162 0         0 return(%$self);
163             }
164              
165             ##################################################
166             # Figure out the Response class to a given Request
167             # class. To be used by sub classes.
168             ##################################################
169             sub response_class {
170             ##################################################
171 0     0 0 0 my($self) = @_;
172              
173 0         0 my $response_class = ref($self);
174 0         0 $response_class =~ s/Request/Response/;
175 0         0 return $response_class;
176             }
177              
178             ##
179             ## 'PRIVATE' METHODS
180             ##
181              
182             # A subroutine (not a class method), to map a response group
183             # to from AWS3 to AWS4, or validate that a response group
184             # is valid for AWS4.
185             sub _get_valid_response_group {
186 0     0   0 my ($response_group, $valid) = @_;
187              
188 0 0       0 if (defined AWS3_VALID_TYPES_MAP->{$response_group}) {
    0          
189 0         0 return AWS3_VALID_TYPES_MAP->{$response_group};
190             } elsif ($valid->ResponseGroup($response_group)) {
191 0         0 return $response_group;
192             }
193              
194             # never reached, valid-> will die if the response group
195             # is not valid for AWS4.
196 0         0 return undef;
197             }
198              
199             # CLASS->_convert_option( OPTIONS, ORIGINAL, TARGET [, CALLBACK] )
200             #
201             # Takes a reference to a hash of OPTIONS and renames the
202             # ORIGINAL key name to the TARGET key name. If the optional
203             # CALLBACK subroutine reference is defined, that subroutine
204             # is invoked with two arguments:
205             #
206             # CALLBACK->( OPTIONS, TARGET )
207             #
208             # The result of the CALLBACK's execution is then returned to
209             # the caller. No assumptions are made about what the CALLBACK
210             # should return (or even *if* is should return)--that's the
211             # caller's responsibility.
212             #
213             # Returns 1 in the absensence of a CALLBACK.
214             #
215             sub _convert_option {
216 60     60   115 my ($class, $options, $original, $target, $callback) = @_;
217              
218 60 100       134 if ( exists $options->{$original} ) {
219 56         113 $options->{$target} = $options->{$original};
220 56         101 delete $options->{$original};
221             }
222              
223 60 100       183 return 1 unless ( $callback );
224            
225             # The key name is explicitly passed-in so that the caller doesn't
226             # have think "Hrmm.. now which key am I working on, the original
227             # or the target key?" Confusion is bad.
228 2         7 return $callback->($options, $target);
229             }
230              
231             # CLASS->_assert_options_defined( OPTIONS, KEYS )
232             #
233             # Takes a reference to a hash of OPTIONS and a list of
234             # one or more KEYS. Tests to see if each key in KEYS
235             # has a defined value. Calls die() upon the first
236             # missing key. Otherwise, returns undef.
237             #
238             sub _assert_options_defined {
239 15     15   41 my ($class, $options, @keys) = @_;
240            
241 15         37 foreach my $key ( @keys ) {
242 15 50       91 die "Mandatory parameter '$key' not defined"
243             unless ( defined $options->{$key} );
244             }
245             }
246              
247             # CLASS->_option_or_default( OPTIONS, DEFAULT, USER )
248             #
249             # Takes a list of options, a default option, and a
250             # possibly supplied user option. If the user option
251             # is defined, it is verified that the option is valid.
252             # If no user option is supplied, the default option is
253             # used.
254             sub _option_or_default {
255 0     0   0 my ($self, $options, $default, $user) = @_;
256             # if(defined $user) {
257             # unless(grep {$user eq $_} @$options) {
258             # die "User supplied value, $user, is not a valid option"
259             # }
260             # return $user;
261             # }
262 0         0 return $default;
263             }
264              
265             # CLASS->_itemsearch_factory()
266             #
267             # Create an instance of an ItemSearch validator based on the
268             # Request class. This class is used to validate user input
269             # against valid options for a given mode, and the type of
270             # Request.
271             sub _itemsearch_factory {
272 11     11   16 my($self) = @_;
273              
274 11         58 my $request_class = ref($self);
275 11         52 my $request_type = (split(/::/, $request_class))[-1];
276              
277             # XXX: I'm not sure what to do here. The ItemSearch validate class
278             # is called Keywords, but the Request/Response class is called
279             # Keyword. For now I'm going to special case Keywords to map
280             # to Keyword.
281 11 100       34 $request_type = 'Keywords' if $request_type eq 'Keyword';
282              
283 11         46 return Net::Amazon::Validate::ItemSearch::factory(search_index => $request_type);
284             }
285              
286             sub _convert_itemsearch {
287 11     11   24 my($self) = @_;
288              
289 11         59 my $is = $self->_itemsearch_factory();
290 0           $self->{mode} = $is->user_or_default($self->{mode});
291              
292 0           __PACKAGE__->_convert_option($self, 'mode', 'SearchIndex');
293             }
294              
295             sub _page_type {
296 0     0     my ($self, %options) = @_;
297              
298 0           my $class = ref $self;
299 0           my ($operation) = $class =~ m/([^:]+)$/;
300              
301 0 0         my $type = (defined DEFAULT_ITEM_PAGE_MAP->{$operation})
302             ? DEFAULT_ITEM_PAGE_MAP->{$operation} : 'ItemPage';
303              
304 0           return $type;
305             }
306              
307              
308             1;
309              
310             __END__