File Coverage

lib/eBay/API/Simple/Trading.pm
Criterion Covered Total %
statement 9 9 100.0
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 12 12 100.0


line stmt bran cond sub pod time code
1             package eBay::API::Simple::Trading;
2              
3 1     1   1332 use strict;
  1         2  
  1         30  
4 1     1   5 use warnings;
  1         3  
  1         26  
5              
6 1     1   5 use base 'eBay::API::SimpleBase';
  1         2  
  1         150  
7              
8             use HTTP::Request;
9             use HTTP::Headers;
10             use XML::Simple;
11             use utf8;
12              
13             our $DEBUG = 0;
14              
15             =head1 NAME
16              
17             eBay::API::Simple::Trading - Support for eBay's Trading web service
18              
19             =head1 DESCRIPTION
20              
21             This class provides support for eBay's Trading web services.
22              
23             See http://developer.ebay.com/products/trading/
24              
25             =head1 USAGE
26              
27             my $call = eBay::API::Simple::Trading->new( {
28             appid => '',
29             devid => '',
30             certid => '',
31             token => '',
32             } );
33            
34             $call->execute( 'GetSearchResults', { Query => 'shoe' } );
35              
36             if ( $call->has_error() ) {
37             die "Call Failed:" . $call->errors_as_string();
38             }
39              
40             # getters for the response DOM or Hash
41             my $dom = $call->response_dom();
42             my $hash = $call->response_hash();
43              
44             print $call->nodeContent( 'Timestamp' );
45              
46             my @nodes = $dom->findnodes(
47             '//Item'
48             );
49              
50             foreach my $n ( @nodes ) {
51             print $n->findvalue('Title/text()') . "\n";
52             }
53              
54             =head1 SANDBOX USAGE
55              
56             my $call = eBay::API::Simple::Trading->new( {
57             appid => '',
58             devid => '',
59             certid => '',
60             token => '',
61             domain => 'api.sandbox.ebay.com',
62             } );
63            
64             $call->execute( 'GetSearchResults', { Query => 'shoe' } );
65              
66             if ( $call->has_error() ) {
67             die "Call Failed:" . $call->errors_as_string();
68             }
69              
70             # getters for the response DOM or Hash
71             my $dom = $call->response_dom();
72             my $hash = $call->response_hash();
73              
74             =head1 PUBLIC METHODS
75              
76             =head2 new( { %options } }
77              
78             Constructor for the Trading API call
79              
80             my $call = eBay::API::Simple::Trading->new( {
81             appid => '',
82             devid => '',
83             certid => '',
84             token => '',
85             ...
86             } );
87              
88             =head3 Options
89              
90             =over 4
91              
92             =item appid (required)
93              
94             This is required by the web service and can be obtained at
95             http://developer.ebay.com
96              
97             =item devid (required)
98              
99             This is required by the web service and can be obtained at
100             http://developer.ebay.com
101              
102             =item certid (required)
103              
104             This is required by the web service and can be obtained at
105             http://developer.ebay.com
106              
107             =item token (required)
108              
109             This is required by the web service and can be obtained at
110             http://developer.ebay.com
111              
112             =item siteid
113              
114             eBay site id to be supplied to the web service endpoint
115              
116             defaults to 0
117              
118             =item domain
119              
120             domain for the web service endpoint
121              
122             defaults to open.api.ebay.com
123              
124             =item uri
125              
126             endpoint URI
127              
128             defaults to /ws/api.dll
129              
130             =item version
131              
132             Version to be supplied to the web service endpoint
133              
134             defaults to 543
135              
136             =item https
137              
138             Specifies is the API calls should be made over https.
139              
140             defaults to 1
141              
142             =item enable_attributes
143              
144             This flag adds support for attributes in the request. If enabled request
145             data notes much be defined like so,
146              
147             myElement => { content => 'element content', myattr => 'attr value' }
148              
149             defaults to 0
150              
151             =back
152              
153             =head3 ALTERNATE CONFIG VIA ebay.yaml
154              
155             An ebay.yaml file can be used for configuring each
156             service endpoint.
157              
158             YAML files can be placed at the below locations. The first
159             file found will be loaded.
160              
161             ./ebay.yaml, ~/ebay.yaml, /etc/ebay.yaml
162              
163             Sample YAML:
164              
165             # Trading - External
166             api.ebay.com:
167             appid:
168             certid:
169             devid:
170             token:
171              
172             # Shopping
173             open.api.ebay.com:
174             appid:
175             certid:
176             devid:
177             version: 671
178              
179             # Finding/Merchandising
180             svcs.ebay.com:
181             appid:
182             version: 1.0.0
183              
184             =cut
185              
186             sub new {
187             my $class = shift;
188             my $self = $class->SUPER::new(@_);
189            
190             $self->api_config->{domain} ||= 'api.ebay.com';
191             $self->api_config->{uri} ||= '/ws/api.dll';
192             $self->api_config->{version} ||= '543';
193            
194             unless ( defined $self->api_config->{https} ) {
195             $self->api_config->{https} = 1;
196             }
197              
198             unless ( defined $self->api_config->{siteid} ) {
199             $self->api_config->{siteid} = 0;
200             }
201              
202             $self->_load_yaml_defaults();
203            
204             if ( $DEBUG ) {
205             print STDERR sprintf( "API CONFIG:\n%s\n",
206             $self->api_config_dump()
207             );
208             }
209              
210            
211             return $self;
212             }
213              
214             =head2 prepare( $verb, $call_data )
215              
216             $call->prepare( 'GetSearchResults', { Query => 'shoe' } );
217            
218             This method will construct the API request based on the $verb and
219             the $call_data.
220              
221             =item $verb (required)
222              
223             call verb, i.e. GetSearchResults
224              
225             =item $call_data (required)
226              
227             hashref of call_data that will be turned into xml.
228              
229             =cut
230              
231             sub prepare {
232             my $self = shift;
233              
234             $self->{verb} = shift;
235             $self->{call_data} = shift;
236              
237             if ( ! defined $self->{verb} || ! defined $self->{call_data} ) {
238             die "missing verb and call_data";
239             }
240              
241             # make sure we have appid, devid, certid, token
242             $self->_load_credentials();
243             }
244              
245             =head1 BASECLASS METHODS
246              
247             =head2 request_agent
248              
249             Accessor for the LWP::UserAgent request agent
250              
251             =head2 request_object
252              
253             Accessor for the HTTP::Request request object
254              
255             =head2 request_content
256              
257             Accessor for the complete request body from the HTTP::Request object
258              
259             =head2 response_content
260              
261             Accessor for the HTTP response body content
262              
263             =head2 response_object
264              
265             Accessor for the HTTP::Request response object
266              
267             =head2 response_dom
268              
269             Accessor for the LibXML response DOM
270              
271             =head2 response_hash
272              
273             Accessor for the hashified response content
274              
275             =head2 nodeContent( $tag, [ $dom ] )
276              
277             Helper for LibXML that retrieves node content
278              
279             =head2 errors
280              
281             Accessor to the hashref of errors
282              
283             =head2 has_error
284              
285             Returns true if the call contains errors
286              
287             =head2 errors_as_string
288              
289             Returns a string of API errors if there are any.
290              
291             =head1 PRIVATE METHODS
292              
293             =head2 _validate_response
294              
295             This is called from the base class. The method is suppose to provide the
296             custom validation code and push to the error stack if the response isn't
297             valid
298              
299             =cut
300              
301             sub _validate_response {
302             my $self = shift;
303              
304             if ( $self->nodeContent('Ack') eq 'Failure' ) {
305             $self->errors_append( {
306             'Call Failure' => $self->nodeContent('LongMessage')
307             } );
308             }
309             }
310              
311             =head2 _get_request_body
312              
313             This method supplies the request body for the Shopping API call
314              
315             =cut
316              
317             sub _get_request_body {
318             my $self = shift;
319              
320             # if auth_method is set to 'token' use token
321             # if auth_method is set to 'iaftoken' use iaftoken
322             # if auth_method is set to 'user' use username/password
323             if ( $self->{auth_method} eq 'token' ) {
324             my $xml = ""
325             . "<" . $self->{verb} . "Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">"
326             . ""
327             . ( $self->api_config->{token} || '' )
328             . ""
329             . XMLout(
330             $self->{call_data},
331             NoAttr => !$self->api_config->{enable_attributes},
332             KeepRoot => 1,
333             RootName => undef
334             )
335             . "{verb} . "Request>";
336              
337             return $xml;
338             }
339             elsif ( $self->{auth_method} eq 'iaftoken' ) {
340             my $xml = ""
341             . "<" . $self->{verb} . "Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">"
342             . XMLout(
343             $self->{call_data},
344             NoAttr => !$self->api_config->{enable_attributes},
345             KeepRoot => 1,
346             RootName => undef
347             )
348             . "{verb} . "Request>";
349              
350             return $xml;
351             }
352             elsif ( $self->{auth_method} eq 'user' ) {
353             my $xml = ""
354             . "<" . $self->{verb} . "Request xmlns=\"urn:ebay:apis:eBLBaseComponents\">"
355             . ""
356             . $self->api_config->{username} . "";
357            
358             if ( $self->api_config->{password} ) {
359             $xml .= ""
360             . $self->api_config->{password} . "";
361             }
362            
363             $xml .= ""
364             . XMLout( $self->{call_data}, NoAttr => 1, KeepRoot => 1, RootName => undef )
365             . "{verb} . "Request>";
366              
367             return $xml;
368             }
369             }
370              
371             =head2 _get_request_headers
372              
373             This method supplies the headers for the Shopping API call
374              
375             =cut
376              
377             sub _get_request_headers {
378             my $self = shift;
379              
380             my $obj = HTTP::Headers->new();
381              
382             $obj->push_header("X-EBAY-API-COMPATIBILITY-LEVEL" =>
383             $self->api_config->{version});
384             $obj->push_header("X-EBAY-API-DEV-NAME" => $self->api_config->{devid});
385             $obj->push_header("X-EBAY-API-APP-NAME" => $self->api_config->{appid});
386             $obj->push_header("X-EBAY-API-CERT-NAME" => $self->api_config->{certid});
387             $obj->push_header("X-EBAY-API-SITEID" => $self->api_config->{siteid});
388             $obj->push_header("X-EBAY-API-CALL-NAME" => $self->{verb});
389             if ( $self->{auth_method} eq 'iaftoken' ) {
390             $obj->push_header("X-EBAY-API-IAF-TOKEN" => $self->api_config->{iaftoken});
391             }
392             $obj->push_header("Content-Type" => "text/xml");
393              
394             return $obj;
395             }
396              
397             =head2 _get_request_object
398              
399             This method creates the request object and returns to the parent class
400              
401             =cut
402              
403             sub _get_request_object {
404             my $self = shift;
405              
406             my $url = sprintf( 'http%s://%s%s',
407             ( $self->api_config->{https} ? 's' : '' ),
408             $self->api_config->{domain},
409             $self->api_config->{uri}
410             );
411             my $request_obj = HTTP::Request->new(
412             "POST",
413             $url,
414             $self->_get_request_headers,
415             $self->_get_request_body
416             );
417              
418             return $request_obj;
419             }
420              
421             sub _load_credentials {
422             my $self = shift;
423              
424             # we only need to load credentials once
425             return if $self->{_credentials_loaded};
426              
427             my @missing;
428              
429             # required by the API
430             for my $reqd ( qw/devid appid certid/ ) {
431             next if defined $self->api_config->{$reqd};
432              
433             if ( defined (my $val = $self->_fish_ebay_ini( $reqd )) ) {
434             $self->api_config->{$reqd} = $val;
435             }
436             else {
437             push( @missing, $reqd );
438             }
439             }
440              
441             if ( scalar @missing > 0 ) {
442             die "missing Authentication: " . join( ", ", @missing );
443             }
444              
445             # Collect token, iaftoken, username, password, domain, https, uri and version from
446             # the ebay.ini file
447             # if token found, set auth_method to 'token'
448             # if iaftoken found, set auth_method to 'iaftoken'
449             # if username/password found set auth_method to 'user'
450              
451             for my $optional ( qw/token iaftoken username password domain https uri version/ ) {
452              
453             next if defined $self->api_config->{$optional};
454              
455             if ( defined ( my $val = $self->_fish_ebay_ini( $optional )) ) {
456             $self->api_config->{$optional} = $val;
457             }
458             else {
459             print STDERR "Not defined : " . $optional . "\n" if $DEBUG;
460             }
461             }
462              
463             if ( exists ( $self->api_config->{token} ) ) {
464             $self->{auth_method} = 'token';
465              
466             delete($self->api_config->{iaftoken});
467             delete($self->api_config->{username});
468             delete($self->api_config->{password});
469             }
470             elsif ( exists ( $self->api_config->{iaftoken} ) ) {
471             $self->{auth_method} = 'iaftoken';
472              
473             delete($self->api_config->{username});
474             delete($self->api_config->{password});
475             }
476             elsif ((exists( $self->api_config->{username} ))
477             && (exists( $self->api_config->{password} ))) {
478             $self->{auth_method} = 'user';
479             }
480             elsif ( exists( $self->api_config->{username} ) ) {
481             $self->{auth_method} = 'user';
482             }
483             else {
484             die "missing Authentication : token or username/password \n";
485             }
486              
487              
488             $self->{_credentials_loaded} = 1;
489             return;
490             }
491              
492             sub _fish_ebay_ini {
493             my $self = shift;
494             my $arg = shift;
495             my @files;
496              
497             # initialize our hashref
498             $self->{_ebay_ini} ||= {};
499              
500             # revert eBay::API::Simple keys to standard keys
501             $arg = 'DeveloperKey' if $arg eq 'devid';
502             $arg = 'ApplicationKey' if $arg eq 'appid';
503             $arg = 'CertificateKey' if $arg eq 'certid';
504             $arg = 'Token' if $arg eq 'token';
505             $arg = 'IAFToken' if $arg eq 'iaftoken';
506             $arg = 'UserName' if $arg eq 'username';
507             $arg = 'Password' if $arg eq 'password';
508             $arg = 'Domain' if $arg eq 'domain';
509             $arg = 'Https' if $arg eq 'https';
510             $arg = 'Uri' if $arg eq 'uri';
511             $arg = 'Version' if $arg eq 'version';
512              
513             # return it if we've already found it
514             return $self->{_ebay_ini}{$arg} if defined $self->{_ebay_ini}{$arg};
515              
516             # ini files in order of importance
517              
518             # Make exception for windows
519             if ( $^O eq 'MSWin32' ) {
520             @files = ( './ebay.ini', './ebay.yaml' );
521             }
522             else {
523             @files = (
524             './ebay.yaml',
525             "$ENV{HOME}/ebay.yaml",
526             '/etc/ebay.yaml',
527             './ebay.ini',
528             "$ENV{HOME}/ebay.ini",
529             '/etc/ebay.ini',
530             );
531             }
532              
533             foreach my $file ( reverse @files ) {
534             if ( open( FILE, "<", $file ) ) {
535            
536             while ( my $line = ) {
537             chomp( $line );
538              
539             next if $line =~ m!^\s*\#!;
540              
541             my( $k, $v ) = split( /=/, $line );
542              
543             if ( defined $k && defined $v) {
544             $v =~ s/^\s+//;
545             $v =~ s/\s+$//;
546              
547             $self->{_ebay_ini}{$k} = $v;
548            
549             }
550             }
551              
552             close FILE;
553             }
554             }
555             return $self->{_ebay_ini}{$arg} if defined $self->{_ebay_ini}{$arg};
556             return undef;
557              
558             }
559              
560             1;
561              
562             =head1 AUTHOR
563              
564             Tim Keefer
565              
566             =head1 CONTRIBUTORS
567              
568             Jyothi Krishnan
569              
570             =cut