In my last post I started to run with an earlier idea that I had about extracting the logic for the various product search services I was using (Amazon, Yahoo, and Ebay) out of the UI and encapsulating them in a component that would treat them all the same from a consumer point of view.
One of the difficulties with using the APIs for each of the mentioned services is that they each return their own objects that describe their data when performing a query. That makes sense, of course, because Ebay’s “Item” is far different from Amazon or Yahoo’s. However, for their usage in WIWM I really just want the basics: title, short description, url, price, and a few other properties. Dealing with all the extra stuff wouldn’t have been too big of an issue if I was just using one of the searches but I plan on using all of them at some place or another in the site and I plan on perhaps using all of them concurrently sometimes. Having to use their objects with their different properties and methods in my UI code was becoming counterproductive and a bit of a pain.
I finally decided to do something about it. I pulled the logic for all the various searches out of my web application, created a separate library to encapsulate the logic for performing the searches with those APIs, and made it return one standard (for me) result object that only makes use of the basic properties I mentioned above. I provided an update in my last post saying that I had started the implementation but at that point had only implemented one provider, Amazon, and one method, GetItemByASIN (which has since become GetItemByID). Since that time I have implemented the providers for Yahoo and Ebay as well as Microsoft’s Live Search (in hopes that their product search will soon become part of their API). Froogle does not have an API available and since Google has decided to close their services and focus on the “drop a javascript script reference on your page” approach, they will not be included. I have looked for some other sites that offer a product search API and affiliate program and I found Shopping.com seems to be the other major player in the area. I am in the process of signing up for an account with them now. Once that is approved I will add their API as a provider as well.
Besides the GetItemByID method that I previously implemented the only other method that I found necessary to add currently is the GetItemsByKeyword(keyword) method - which is a basic search. I think those two methods will cover most anything that I need to do on WIWM, though I’ll probably add support for retrieving product reviews for the APIs that support that.
So what does all this mean? Well, for the WIWM project it means that I don’t have to know the inner workings of the Amazon API or the Yahoo API or any other search API whenever I want to search them. I’ve written that code once in the ProductSearch code and I’ll never have to look at it again, well at least not while wearing my WIWM hat. All I had to do was add a reference in my project to the DanHounshell.ProductSearch assembly and I was able to change code that looked like this for a Yahoo Search:
YahooSearchService yahoo = new YahooSearchService();
ResultSet resultSet = yahoo.productSearch("myyahooid", "pampers");
List<OfferType> offers = new List<OfferType>();
List<CatalogType> catalogitems = new List<CatalogType>();
foreach (ResultType result in resultSet.Result){
if (result.Item.ToString() == "Yahoo.API.ProductSearch.OfferType"){
OfferType offer = (OfferType) result.Item;
offers.Add(offer);
}
else{
CatalogType catalogitem = (CatalogType) result.Item;
catalogitems.Add(catalogitem);
}
}
dgCatalog.DataSource = catalogitems;
dgCatalog.DataBind();
dgOffers.DataSource = offers;
dgOffers.DataBind();
to this:
ProductSearch ps = new ProductSearch(SearchProviderType.Yahoo);
List<Item> items = ps.GetItemsByKeyword(keyword);
dg.DataSource = items;
dg.DataBind();
And code that looked like this for the Ebay Search:
ApiAccount apiacct = new ApiAccount("MyDevid", "MyAppid", "MyCertid");
eBayAccount ebayacct = new eBayAccount("MyUserID", "MyPassword");
ApiCredential cred = new ApiCredential();
cred.ApiAccount = apiacct;
cred.eBayAccount = ebayacct;
cred.eBayToken = "MyUserToken";
ApiContext context = new ApiContext();
context.ApiCredential = cred;
context.EPSServerUrl = "someurl.ebay.com/...";
context.SignInUrl = "someurl.ebay.com/...";
context.SoapApiServerUrl = "someurl.ebay.com/...";
context.XmlApiServerUrl = "someurl.ebay.com/...";
eBay.Service.Call.GetSearchResultsCall call = new eBay.Service.Call.GetSearchResultsCall(context);
call.Version = "485";
SearchResultItemTypeCollection items = call.GetSearchResults("pampers")
// A whole bunch of stuff to convert the Ebay objects to a DataSet
dg.DataSource = dataset;
dg.DataBind();
to this:
ProductSearch ps = new ProductSearch(SearchProviderType.Ebay);
List<Item> items = ps.GetItemsByKeyword(keyword);
dg.DataSource = items;
dg.DataBind();
I do plan on releasing the ProductSearch component into the wild at some point. It will probably be on some sort of Open Source basis, though I doubt if I will take the time to setup a CodePlex project or anything like that for it. While this little project will make a world of difference in my work, I think it really has a limited audience and it’s not all that ground-breaking. I will probably wait until the completion of the WIWM most project to do so, though. Waiting will allow me to put the code through its paces and tweak the functionality as needed, like by adding a property to define the number of results I want returned and anything else I haven’t thought of at this early point.
One final thing: notice that in the two examples above I set just one provider type when I instantiated my searches. That constructor actually takes a param array of provider types. When more than one provider type is specified the returned list of items is actually a combined list of results from each search service provided. So I could search Yahoo, Amazon, and Ebay at the same time and get back one list of products! Optionally there is another search method that returns the results as an array of lists so you can work with each result set independently. Keeping them separate could allow me to show each result set in its own tab of a tabbed pane of search results, something similar to this:
Performance-wise it may be better to search each service individually rather than wait for results that calls each one sequentially, but that will have to be determined. It may not be that big of a difference when retrieving a small result set and when caching is being utilized well.