Extending Web Services Using Other Web Services

How to create a useful new Web service by tapping in to the power of two other freely available Web services.
Combining the Search Results

The above program works just fine, and it provides an easier way to query the Skokie library catalog than the standard Web pages. But, I'm interested in knowing how much the book would cost if I were to buy it from Amazon, as well as whether it's available from the library. With all of this information, I can then decide if I want to buy the book, check it out of the library or neither.

Last month, we saw how we could use a REST-style request (that is, HTTP GET with arguments) to retrieve information from Amazon. Now we will write a program that performs that retrieval and then pulls out the relevant XML data.

As you might remember, we can retrieve Web services information from Amazon by sending an HTTP request to webservices.amazon.com, asking for the document /onca/xml, and then specifying the Service, Operation and AWSAccessKeyId name-value pairs. If we are interested in learning about new and used prices for that ISBN, we then pass the ItemId parameter, and indicate that we want the ResponseGroup known as OfferSummary.

Because Amazon returns XML in all of its responses, including those invoked with REST, we can parse through the XML to find the lowest prices for our book. Ruby comes with the REXML-parsing library, which works with XML in a number of different ways; we will use it to scan through Amazon's response for the appropriate code.

Finally, we can rework our existing code, such that it will search the Skokie library for the ISBN and produce a textual summary. Listing 2 contains a program (combined-lookup.rb) that produces such combined output.

combined-lookup.rb begins in almost the same way as skokie-lookup.rb, although it imports the rexml/document module along with the net/http module. It then iterates through ISBNs that were passed on the command line, ignoring those that don't fit the strict definition.

The main addition to this program begins with the creation of a string named amazon_params. In theory, we could have built this string in a number of different ways, many of them less complicated than the combination of methods I chose. But, I felt that using a hash in this way would make it easier to modify the code later on, even if it requires a bit more time to understand at first.

The basic idea is as follows: we create a hash, in which the keys are the AWS REST parameter names, and the values are the corresponding parameter values. In order to get these parameters into the standard format of param1=value1&param2=value2, we use map to create an array from the keys and values of the hash. Our array will contain strings, each of which is in the param=value format, joined together with an equal sign. Finally, we use join to combine all of those pairs with & signs between them, producing a string that we assign to amazon_params.

With our parameters in place, we use Net::HTTP.get_response, just as we did before in skokie-lookup.rb. The hostname will be different, and the requested URL on that host will also be quite different, incorporating the parameters that we just assigned to amazon_params. But, the request is sent in the same way, and we retrieve the response in the same way as well.

However, whereas the Skokie library sends its response in HTML, Amazon replies using XML. So, we fire up REXML, creating a new instance of REXML::Document with the contents of the Amazon response. We then use the elements method on the response's root node to find the lowest new, used and collectible prices. (Amazon provides each of these prices separately, which I admit is a bit annoying.) If the text within that node is nil, no such price exists, and we indicate that to the user. Otherwise, we can assume we got a price back—and a price formatted with a dollar sign and decimal point, at that—and we display it for the user.