I recently played around with the Amazon Web Services (AWS). My idea was to use Perl and SOAP, so SOAP::Lite in fact to access the AWS to fetch some book data.

Although I know that Amazon offers a REST I wanted to have something for real men ™, since REST looked too easy to me. How naive I was…

I first searched for some cool examples, but unfortunately they didn’t work. So I started over to develop something myself. After some time I got it, it was working, BUT… I haven’t noticed that I used ECS 3.0 and not ECS 4.0 which is out since quite some time.

So when I tried to use my script beginning of this month I realized that it didn’t work any more. Back luck for me though. Luckily I’m not the only one and Amazon published a Migration guide. So I followed the rules and got it back to work again.

So I thought it might be helpful to somebody out there hence I’m publishing the script here.

For SOAP one of the most important things is the WSDL. It describes the service, complex data types and so on. The AWS WSDL is one of the more complex ones.

However, we have to cope with this one to know how to connect to the Webservice.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/perl
# amazon.pl
# A typical Amazon Web API Perl script that uses the SOAP::Lite Module.
# Usage: perl amazon.pl <keyword>
#
# This example is based on the ones you find all over the net.
# Unfortunately these examples have one minor drawback: they simply don't
# work out... 
#
# Besides that I wasn't able to find a working example for ECS 4.0.
#
# Author: DerGrobe <dergrobe(nospam) at schnuckelig.eu>

use strict;
use Data::Dumper;
#Use the SOAP::Lite Perl module
use SOAP::Lite +trace => 'debug';

# Your Amazon access key ID
my $access_key = "0E8E93NQK53Q05MNDJ02";
my $associate_tag = "schnuckelig-20";

#Location of the Amazon WSDL file
my $amazon_wdsl =
"http://webservices.amazon.com/AWSECommerceService/DE/AWSECommerceService.wsdl";

# This is the target namespace found in the WSDL
my $serviceNs = "http://webservices.amazon.com/AWSECommerceService/2008-04-07";

# Address location of the service we'd like to use, 'AWSECommerceService' in this case
# found in the WSDL as well (at the bottom of the file)
my $serviceUrl =
"http://soap.amazon.de/onca/soap?Service=AWSECommerceService";

#Take the query from the command-line
my $keyword = shift @ARGV or die "Usage:perl amazon.pl <keyword>\n";

# Create a new SOAP object
my $soap = SOAP::Lite->uri( $serviceNs )->proxy( $serviceUrl );

my $AWSAccessKeyId = SOAP::Data->name('AWSAccessKeyId')
                                      ->value($access_key);
my $AssociateTagElement = SOAP::Data->name('AssociateTag')
                                    ->value($associate_tag);

my $SearchIndex = SOAP::Data->name("SearchIndex")->value("Books");
my $Keywords = SOAP::Data->name("Keywords")->value($keyword);

my $ItemSearch = SOAP::Data->value($AWSAccessKeyId, $AssociateTagElement);

my $RequestElementType = SOAP::Data->value($SearchIndex, $Keywords);
my $RequestElement = SOAP::Data->name("Request")->value($RequestElementType);

my $ItemSearchRequest = SOAP::Data->value($ItemSearch, $RequestElement);

my $som = $soap->ItemSearch($ItemSearchRequest);

print "RequestProcessingTime = ";
print $som->dataof('//RequestProcessingTime')->value, "\n";
print "\n";

print "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService"
    . "&AWSAccessKeyId=$access_key"
    . "&Operation=ItemSearch&Keywords=$keyword"
    . "&SearchIndex=Books"
    . "&Sort="
    . "\n";


if ( $som->fault() ) {
    printf("Code: %s\nString: %s\nDetail: %s\nActor: %s\n",
        $som->faultcode(), $som->faultstring(), $som->faultdetail(), $som->faultactor() );
} else {
    foreach my $result (@{$som->paramsout->{Item}} ){
        # Print out the main bits of each result
        print
            $result->{ItemAttributes}->{Title} || "no title", 
            "\nby "; 
        if (ref $result->{ItemAttributes}->{Author} eq 'ARRAY') {
            print join (', ', @{$result->{ItemAttributes}->{Author}});
        } else {
            print $result->{ItemAttributes}->{Author};
        }
        print "\nASIN: $result->{Asin}",
            "\n\n";

    }
}

OK, let’s look at the perl code to get some insight here.

I want to use the keyword search for books, this was a ‘KeywordSearch’ for ECS 3.0 and is now an ‘ItemSearch’ for ECS 4.0. A look at the WSDL reveals that we have to send a ItemSearchRequestMsg and get an ItemSearchResponeMsg as answer. (In fact the service ‘AWSECommerceService’ defines a port ‘AWSECommerceServicePort’ which in turn defines the operation ‘ItemSearch’).

Let’s have a look at the message definition for ItemSearch. There we find

xs:complexType xs:sequence /xs:sequence /xs:complexType /xs:element

So it seems that all these parameter are optional, but in fact according to the API description some of these paremeter are mandatory (AWSAccessKeyId, AssociateTag and the ItemSearchRequest apparently).

On line 41 and 43 we put the first to parameter into SOAP data. The ItemSearchRequest is a very large complex type, but we want to search for keywords (line 46) in the books index (line 45). The rest is simply putting the pieces together. (lines 48-53). On line 55 we finally fire the request.

Line 61ff prints the REST alternative to STDOUT for comparison reason. The rest of the script is some very basic output of the data and that’s it basically.

The problem is that the WSDL should guide us directly, but it seems to me that you have to read the API documentation from Amazon additionally or you’ll be lost otherwise.

I hope that this helps you to understand the SOAP thing a bit better and getting in touch with the Amazon web services.