Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

A couple of changes #5

Open
wants to merge 9 commits into from

1 participant

@amaltsev

Thanks for the great MWS module, you should push it to CPAN!

I made a couple of fixes while integrating it into the project I'm working on. I hope you find them useful.

amaltsev added some commits
@amaltsev amaltsev Signature, tracing, endpoint fix
The sha1 signature did not work for me. Replaced with SHA256.

Also making sure the endpoint ends with '/' -- without it the base
string for the signature does not match the original Amazon assumes and
signature verification fails.

Replaced GET with POST. Not sure why GET does not work, but it does not
for me.

Added exception catching to the example for Exception::Class newbies
like myself.
95d6cff
@amaltsev amaltsev Bug fix for ManageReportSchedule
It's an incompatible change, but without it it simply does not work.
2b6aa49
@amaltsev amaltsev Version bump for require ebbcc49
@amaltsev amaltsev Changed report ID to a string
Report ids are 64bit integers, they may overflow perl's integers.
There is no math on report IDs, so it's better to treat them as
strings.
b6fbc92
@amaltsev amaltsev Primitive auto-throttling f9926a7
@amaltsev amaltsev Added payment settlement report types 555f189
@amaltsev amaltsev Fixed 'Use of qw(...) as parentheses is deprecated' 45f8434
@amaltsev amaltsev Fixed obsoleted settlement report IDs
Report IDs
    _GET_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_
    _GET_ALT_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_
    _GET_PAYMENT_SETTLEMENT_DATA_

were obsoleted on 5/31/2015 and since mid-June do not return any documents.

The are replaced by:

    _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_
    _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2_
    _GET_V2_SETTLEMENT_REPORT_DATA_XML_

Also fixed syntax for Perl 5.20.2 compatibility.
8635363
@amaltsev amaltsev Fixed POD constants 152eddf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 6, 2012
  1. @amaltsev

    Signature, tracing, endpoint fix

    amaltsev authored
    The sha1 signature did not work for me. Replaced with SHA256.
    
    Also making sure the endpoint ends with '/' -- without it the base
    string for the signature does not match the original Amazon assumes and
    signature verification fails.
    
    Replaced GET with POST. Not sure why GET does not work, but it does not
    for me.
    
    Added exception catching to the example for Exception::Class newbies
    like myself.
  2. @amaltsev

    Bug fix for ManageReportSchedule

    amaltsev authored
    It's an incompatible change, but without it it simply does not work.
  3. @amaltsev

    Version bump for require

    amaltsev authored
Commits on Nov 7, 2012
  1. @amaltsev

    Changed report ID to a string

    amaltsev authored
    Report ids are 64bit integers, they may overflow perl's integers.
    There is no math on report IDs, so it's better to treat them as
    strings.
  2. @amaltsev

    Primitive auto-throttling

    amaltsev authored
  3. @amaltsev
Commits on Jun 15, 2014
  1. @amaltsev
Commits on Jul 17, 2015
  1. @amaltsev

    Fixed obsoleted settlement report IDs

    amaltsev authored
    Report IDs
        _GET_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_
        _GET_ALT_FLAT_FILE_PAYMENT_SETTLEMENT_DATA_
        _GET_PAYMENT_SETTLEMENT_DATA_
    
    were obsoleted on 5/31/2015 and since mid-June do not return any documents.
    
    The are replaced by:
    
        _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_
        _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2_
        _GET_V2_SETTLEMENT_REPORT_DATA_XML_
    
    Also fixed syntax for Perl 5.20.2 compatibility.
  2. @amaltsev

    Fixed POD constants

    amaltsev authored
This page is out of date. Refresh to see the latest.
View
6 .gitignore
@@ -0,0 +1,6 @@
+Build
+MANIFEST
+MYMETA.json
+MYMETA.yml
+_build/
+blib/
View
2  Build.PL
@@ -31,7 +31,7 @@ my $build = Module::Build->new(
'MIME::Base64' => 0,
'Digest::MD5' => 0,
- 'Digest::HMAC_SHA1' => 0,
+ 'Digest::SHA' => 0,
},
);
View
2  MANIFEST.SKIP
@@ -45,3 +45,5 @@
\B\._
# Avoid archives of this distribution
\bAmazon-MWS-[\d\.\_]+
+^MYMETA\.yml$
+^MYMETA\.json$
View
15 examples/request-FBA-fulfillment-report.pl
@@ -2,6 +2,7 @@
use strict;
+use Amazon::MWS::Enumeration::ReportType qw(:all);
use Amazon::MWS::Client;
use DateTime;
@@ -10,12 +11,22 @@
merchant_id => "ZZZ",
marketplace_id => "VVV");
-my $req = $mws->RequestReport(ReportType => '_GET_AMAZON_FULFILLED_SHIPMENTS_DATA_',
+my $req;
+
+eval {
+ $req = $mws->RequestReport(ReportType => (_GET_AMAZON_FULFILLED_SHIPMENTS_DATA_),
StartDate => DateTime->now->add(weeks => -1),
EndDate => DateTime->now);
+};
+
+if(my $e = Exception::Class->caught('Amazon::MWS::Client::Exception')) {
+ die $e->error . "\n" . $e->trace->as_string . "\n";
+}
+elsif($@) {
+ die $@;
+}
if (my $req_id = $req->{ReportRequestInfo}->[0]->{ReportRequestId}) {
open my $req, "> request.${req_id}";
close $req;
}
-
View
112 lib/Amazon/MWS/Client.pm
@@ -3,7 +3,7 @@ package Amazon::MWS::Client;
use warnings;
use strict;
-our $VERSION = '0.1';
+our $VERSION = '0.3';
use URI;
use Readonly;
@@ -11,7 +11,7 @@ use DateTime;
use XML::Simple;
use URI::Escape;
use MIME::Base64;
-use Digest::HMAC_SHA1 qw(hmac_sha1);
+use Digest::SHA qw(hmac_sha256_base64);
use HTTP::Request;
use LWP::UserAgent;
use Class::InsideOut qw(:std);
@@ -21,6 +21,19 @@ use Amazon::MWS::TypeMap qw(:all);
my $baseEx;
BEGIN { Readonly $baseEx => 'Amazon::MWS::Client::Exception' }
+# Data for automatic throttling. First is the maximum request quota,
+# second is the restore rate.
+
+my %throttleconfig=(
+ '*' => [ 10, 60 ], # conservative default
+ GetFeedSubmissionList => [ 10, 45 ],
+ GetFeedSubmissionResult => [ 15, 60 ],
+ GetReport => [ 15, 60 ],
+ GetReportList => [ 10, 60 ],
+ ManageReportSchedule => [ 10, 45 ],
+ UpdateReportAcknowledgements => [ 10, 45 ],
+);
+
use Exception::Class (
$baseEx,
"${baseEx}::MissingArgument" => {
@@ -51,6 +64,8 @@ readonly access_key_id => my %access_key_id;
readonly secret_key => my %secret_key;
readonly merchant_id => my %merchant_id;
readonly marketplace_id => my %marketplace_id;
+readonly throttling => my %throttling;
+readonly debugging => my %debugging;
sub force_array {
my ($hash, $key) = @_;
@@ -81,7 +96,7 @@ sub convert_FeedSubmissionInfo {
}
sub convert_ReportRequestInfo {
- my $root = shift;
+ my $root = shift;
force_array($root, 'ReportRequestInfo');
foreach my $info (@{ $root->{ReportRequestInfo} }) {
@@ -129,10 +144,12 @@ sub define_api_method {
Marketplace => $self->marketplace_id,
Version => '2009-01-01',
SignatureVersion => 2,
- SignatureMethod => 'HmacSHA1',
+ SignatureMethod => 'HmacSHA256',
Timestamp => to_amazon('datetime', DateTime->now),
);
+ $self->throttle($method_name);
+
foreach my $name (keys %$params) {
my $param = $params->{$name};
@@ -170,17 +187,27 @@ sub define_api_method {
$request->uri($uri);
if ($body) {
- $request->method('POST');
+ $request->method('POST');
$request->content($body);
$request->header('Content-MD5' => md5_base64($body) . '==');
$request->content_type($args->{content_type});
}
else {
- $request->method('GET');
+ $request->method('POST');
}
$self->sign_request($request);
+
+ if($debugging{id $self}) {
+ print STDERR "REQUEST: ".$request->as_string."\n";
+ }
+
my $response = $self->agent->request($request);
+
+ if($debugging{id $self}) {
+ print STDERR "RESPONSE: ".$response->as_string."\n";
+ }
+
my $content = $response->content;
my $xs = XML::Simple->new( KeepRoot => 1 );
@@ -197,7 +224,7 @@ sub define_api_method {
}
if (my $md5 = $response->header('Content-MD5')) {
- bad_checksum(response => $response)
+ bad_checksum(response => $response)
unless ($md5 eq md5_base64($content) . '==');
}
@@ -226,17 +253,51 @@ sub sign_request {
"$param=$value";
} sort keys %params;
+ ### print STDERR ">SIGNATURE: canonical=$canonical\n" if $debugging{id $self};
+
my $string = $request->method . "\n"
. $uri->authority . "\n"
. $uri->path . "\n"
. $canonical;
- $params{Signature} =
- encode_base64(hmac_sha1($string, $self->secret_key), '');
+ ### print STDERR ">SIGNATURE: string=$string\n" if $debugging{id $self};
+
+ my $sig=hmac_sha256_base64($string, $self->secret_key);
+ $sig.='=' while length($sig) % 4;
+
+ ### print STDERR ">SIGNATURE: signature=$sig\n" if $debugging{id $self};
+
+ $params{Signature} = $sig;
+
$uri->query_form(\%params);
+
+ ### print STDERR ">SIGNATURE: uri=".$uri->as_string."\n" if $debugging{id $self};
+
$request->uri($uri);
}
+sub throttle {
+ my ($self,$action)=@_;
+
+ # TODO: Support bursts!
+
+ my $cf=$throttleconfig{$action} || $throttleconfig{'*'} || return;
+
+ my $td=$throttling{id $self}->{$action} || 0;
+
+ my $now=time;
+
+ my $wtime=$cf->[1] - ($now - $td);
+
+ if($wtime>0) {
+ print STDERR "..throttling $action for $wtime seconds\n" if $debugging{id $self};
+
+ sleep $wtime;
+ }
+
+ $throttling{id $self}->{$action}=$now;
+}
+
sub new {
my $class = shift;
my $opts = slurp_kwargs(@_);
@@ -251,7 +312,12 @@ sub new {
my $agent_string = "$appname/$version ($attr_str)";
$agent{id $self} = LWP::UserAgent->new(agent => $agent_string);
- $endpoint{id $self} = $opts->{endpoint} || 'https://mws.amazonaws.com/';
+
+ $endpoint{id $self} = $opts->{endpoint} || 'https://mws.amazonservices.com/';
+
+ # Signature verification depends on the slash
+ #
+ $endpoint{id $self}.='/' unless $endpoint{id $self}=~/\/$/;
$access_key_id{id $self} = $opts->{access_key_id}
or die 'No access key id';
@@ -265,6 +331,10 @@ sub new {
$marketplace_id{id $self} = $opts->{marketplace_id}
or die 'No marketplace id';
+ $debugging{id $self} = $opts->{debug} || $opts->{'debugging'} || 0;
+
+ $throttling{id $self} = { };
+
return $self;
}
@@ -305,7 +375,7 @@ define_api_method GetFeedSubmissionList =>
};
define_api_method GetFeedSubmissionListByNextToken =>
- parameters => {
+ parameters => {
NextToken => {
type => 'string',
required => 1,
@@ -344,7 +414,7 @@ define_api_method CancelFeedSubmissions =>
define_api_method GetFeedSubmissionResult =>
raw_body => 1,
parameters => {
- FeedSubmissionId => {
+ FeedSubmissionId => {
type => 'string',
required => 1,
},
@@ -383,7 +453,7 @@ define_api_method GetReportRequestList =>
define_api_method GetReportRequestListByNextToken =>
parameters => {
- NextToken => {
+ NextToken => {
required => 1,
type => 'string',
},
@@ -460,8 +530,8 @@ define_api_method GetReportCount =>
define_api_method GetReport =>
raw_body => 1,
parameters => {
- ReportId => {
- type => 'nonNegativeInteger',
+ ReportId => {
+ type => 'string',
required => 1,
}
};
@@ -474,7 +544,7 @@ define_api_method ManageReportSchedule =>
},
respond => sub {
my $root = shift;
- convert($root, ScheduledDate => 'datetime');
+ convert_ReportSchedule($root);
return $root;
};
@@ -511,7 +581,7 @@ define_api_method GetReportScheduleCount =>
define_api_method UpdateReportAcknowledgements =>
parameters => {
- ReportIdList => {
+ ReportIdList => {
type => 'IdList',
required => 1,
},
@@ -558,7 +628,7 @@ module.
=head3 endpoint
-Where MWS lives. Defaults to 'https://mws.amazonaws.com/'.
+Where MWS lives. Defaults to 'https://mws.amazonservices.com/'.
=head3 access_key_id
@@ -606,14 +676,14 @@ this exception will be thrown. The response can be found in $e->response.
The following methods may be called on objects of this class. All concerns
(such as authentication) which are common to every request are handled by this
-class.
+class.
Enumerated values may be specified as strings or as constants from the
-Amazon::MWS::Enumeration packages for compile time checking.
+Amazon::MWS::Enumeration packages for compile time checking.
All parameters to individual API methods may be specified either as name-value
pairs in the argument string or as hashrefs, and should have the same names as
-specified in the API documentation.
+specified in the API documentation.
Return values will be hashrefs with keys as specified in the 'Response
Elements' section of the API documentation unless otherwise noted.
View
4 lib/Amazon/MWS/Enumeration/FeedProcessingStatus.pm
@@ -5,12 +5,12 @@ use warnings;
use base qw(Amazon::MWS::Enumeration);
-__PACKAGE__->define qw(
+__PACKAGE__->define (qw(
_SUBMITTED_
_IN_PROGRESS_
_CANCELLED_
_DONE_
-);
+));
1;
View
4 lib/Amazon/MWS/Enumeration/FeedType.pm
@@ -5,7 +5,7 @@ use warnings;
use base qw(Amazon::MWS::Enumeration);
-__PACKAGE__->define qw(
+__PACKAGE__->define (qw(
_POST_PRODUCT_DATA_
_POST_PRODUCT_RELATIONSHIP_DATA_
_POST_ITEM_DATA_
@@ -21,7 +21,7 @@ __PACKAGE__->define qw(
_POST_FLAT_FILE_FULFILLMENT_DATA_
_POST_FLAT_FILE_PAYMENT_ADJUSTMENT_DATA_
_POST_FLAT_FILE_INVLOADER_DATA_
-);
+));
1;
View
4 lib/Amazon/MWS/Enumeration/ReportProcessingStatus.pm
@@ -5,13 +5,13 @@ use warnings;
use base qw(Amazon::MWS::Enumeration);
-__PACKAGE__->define qw(
+__PACKAGE__->define (qw(
_SUBMITTED_
_IN_PROGRESS_
_CANCELLED_
_DONE_
_DONE_NO_DATA_
-);
+));
1;
View
56 lib/Amazon/MWS/Enumeration/ReportType.pm
@@ -5,20 +5,28 @@ use warnings;
use base qw(Amazon::MWS::Enumeration);
-__PACKAGE__->define qw(
+__PACKAGE__->define (qw(
+ _GET_AFN_INVENTORY_DATA_
+ _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
+ _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
+ _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_
+ _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
+ _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_
_GET_FLAT_FILE_OPEN_LISTINGS_DATA_
+ _GET_FLAT_FILE_ORDER_REPORT_DATA_
+ _GET_FLAT_FILE_ORDERS_DATA_
+ _GET_MERCHANT_CANCELLED_LISTINGS_DATA_
_GET_MERCHANT_LISTINGS_DATA_
_GET_MERCHANT_LISTINGS_DATA_LITE_
_GET_MERCHANT_LISTINGS_DATA_LITER_
- _GET_MERCHANT_CANCELLED_LISTINGS_DATA_
_GET_NEMO_MERCHANT_LISTINGS_DATA_
- _GET_AFN_INVENTORY_DATA_
- _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_
_GET_ORDERS_DATA_
- _GET_FLAT_FILE_ORDER_REPORT_DATA_
- _GET_FLAT_FILE_ORDERS_DATA_
- _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
-);
+ _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_
+ _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2_
+ _GET_V2_SETTLEMENT_REPORT_DATA_XML_
+ _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
+ _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_
+));
1;
@@ -32,29 +40,45 @@ Amazon::MWS::Enumeration::ReportType
=over 4
+=item _GET_AFN_INVENTORY_DATA_
+
+=item _GET_AMAZON_FULFILLED_SHIPMENTS_DATA_
+
+=item _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
+
+=item _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_
+
+=item _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_LAST_UPDATE_
+
+=item _GET_FLAT_FILE_ALL_ORDERS_DATA_BY_ORDER_DATE_
+
=item _GET_FLAT_FILE_OPEN_LISTINGS_DATA_
+=item _GET_FLAT_FILE_ORDER_REPORT_DATA_
+
+=item _GET_FLAT_FILE_ORDERS_DATA_
+
+=item _GET_MERCHANT_CANCELLED_LISTINGS_DATA_
+
=item _GET_MERCHANT_LISTINGS_DATA_
=item _GET_MERCHANT_LISTINGS_DATA_LITE_
=item _GET_MERCHANT_LISTINGS_DATA_LITER_
-=item _GET_MERCHANT_CANCELLED_LISTINGS_DATA_
-
=item _GET_NEMO_MERCHANT_LISTINGS_DATA_
-=item _GET_AFN_INVENTORY_DATA_
+=item _GET_ORDERS_DATA_
-=item _GET_FLAT_FILE_ACTIONABLE_ORDER_DATA_
+=item _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_
-=item _GET_ORDERS_DATA_
+=item _GET_V2_SETTLEMENT_REPORT_DATA_FLAT_FILE_V2_
-=item _GET_FLAT_FILE_ORDER_REPORT_DATA_
+=item _GET_V2_SETTLEMENT_REPORT_DATA_XML_
-=item _GET_FLAT_FILE_ORDERS_DATA_
+=item _GET_XML_ALL_ORDERS_DATA_BY_LAST_UPDATE_
-=item _GET_CONVERGED_FLAT_FILE_ORDER_REPORT_DATA_
+=item _GET_XML_ALL_ORDERS_DATA_BY_ORDER_DATE_
=back
View
4 lib/Amazon/MWS/Enumeration/Schedule.pm
@@ -5,7 +5,7 @@ use warnings;
use base qw(Amazon::MWS::Enumeration);
-__PACKAGE__->define qw(
+__PACKAGE__->define (qw(
_15_MINUTES_
_30_MINUTES_
_1_HOUR_
@@ -21,7 +21,7 @@ __PACKAGE__->define qw(
_15_DAYS_
_30_DAYS_
_NEVER_
-);
+));
1;
View
4 t/enumeration.t
@@ -7,11 +7,11 @@ BEGIN {
package TestEnum;
use base 'Amazon::MWS::Enumeration';
- TestEnum->define qw(
+ TestEnum->define (qw(
Foo
Bar
Baz
- );
+ ));
}
package indie;
Something went wrong with that request. Please try again.