Ugly Stool Rotating Header Image

December 17th, 2010:

WCF REST Client for SmugMug – 2 of n

In the last post I gave a brief overview of the SmugMug API, and what I wanted WCF to do.  To summarize, I wanted WCF to provide two things: strongly typed objects, and exceptions.

My first attempt to avoid XML parsing was to create an XML schema.  I had hoped to use an XML schema from SmugMug, but unfortunately I could not find one.  Furthermore, the SmugMug API wiki does not provide enough detail to create an XML schema.  SmugMug does a good, but not a great job of documenting the XML responses for all of the API methods.  (Granted, this is a tedious and laborious task, so it is understandable.)

The easiest way I found to generate an XML schema was to get example XML data directly from SmugMug by hand-crafting URI’s and using cURL.

The skeleton of my first attempt at an XML schema is shown below.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Albums"/>
  <xs:complexType name="Images"/>
  <xs:complexType name="ImageExif"/>
  <xs:complexType name="Response">
    <xs:sequence>
      <xs:element name="method" type="xs:string"/>
      <xs:choice>
        <xs:element name="Albums" type="Albums"/>
        <xs:element name="Images" type="Images"/>
        <xs:element name="Image" type="ImageExif"/>
      </xs:choice>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="rsp" type="Response"/>
</xs:schema>

SmugMug wraps all objects in a response (<rsp/>) element, but only one object is returned in a response, where an object is Albums, Images, or Image.  This is modeled in the schema with a choice element.  The xsd.exe tool does not generate desirable code based on the XML schema shown above.  The generated code is shown below.

[XmlElementAttribute("Albums", typeof(Albums)]
[XmlElementAttribute("Image", typeof(ImageExif)]
[XmlElementAttribute("Images", typeof(Images)]
public object Item {
    get {
        return this.itemField;
    }
    set {
        this.itemField = value;
    }
}

The type information for Albums, Images, and Image has been condensed to a C# object ((System.Object) because it is the only type common to Albums, Images, an Image.  (I potentially could have solved this issue using derivation in the schema if there was commonality across all of SmugMug types I cared about, but I had not bothered to investigate.)

Instead of using a choice element to model the fact that only one object is returned, I could have used a mixture of minOccurs and maxOccurs.  An example schema is shown below.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="Albums"/>
  <xs:complexType name="Images"/>
  <xs:complexType name="ImageExif"/>
  <xs:complexType name="Response">
    <xs:sequence>
      <xs:element name="method" type="xs:string"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Albums" type="Albums"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Images" type="Images"/>
      <xs:element minOccurs="0" maxOccurs="1" name="Image" type="ImageExif"/>
    </xs:sequence>
  </xs:complexType>
  <xs:element name="rsp" type="Response"/>
</xs:schema>

The schema duplicates the behavior of the choice element, but forces the xsd.exe tool to generate type information for the SmugMug objects.  The generated code is shown below.

[XmlElementAttribute]
public Albums Albums {
    get {
        return this.albumsField;
    }
    set {
        this.albumsField = value;
    }
}
[XmlElementAttribute]
public Images Images {
    get {
        return this.imagesField;
    }
    set {
        this.imagesField = value;
    }
}
[XmlElementAttribute]
public ImageExif Image {
    get {
        return this.imageField;
    }
    set {
        this.imageField = value;
    }
}

This code is closer to what I want.  The type is explicit – no casting required.  The objection is the fact that there are potentially many dead types to choose from.  If I am fetching Albums, why should the code ever have to consider anything but the list of Albums.  The calling code has to keep track of the method invoked, and then switch based on the SmugMug method to determine what property to interrogate.  Potentially, the client code devolves into the following.

Response response = this.InvokeSmugMugMethod();
switch (response.method)
{
    case "smugmug.albums.get":
        // do something with albums
        break;
    case "smugmug.images.get";
        // do something with images
}

In my next post I will describe the solution I used to solve these issues.

WCF REST Client for SmugMug – 1 of n

I created an open source project called Obra.  Obra is a PowerShell drive for SmugMug, which basically means you can treat SmugMug as if it was your C:.  You can execute directory listing, rename files, delete files, and look at file metadata.  In the case of SmugMug metadata means the album’s description, image dimensions, EXIF, etc.

I wrote Obra to learn about technologies I have not really used like WCF and PowerShell.  I had previously written a read-only ruby client for SmugMug, but it stalled when I started to implement write-support.  I did not want to lose people’s pictures, and I couldn’t figure out a good way to test my client without re-implementing the SmugMug REST service.  (This was me, not ruby.)  Fast-forward a couple of years, and I was now employed as a C# developer and learning about Nunit and NMock.  I wanted to incorporate these tools into the project as well.

When I started Obra I could not find much material about writing WCF REST clients.  Excluding REST, most of the material seemed biased towards WCF servers, not clients.  Hopefully, this series of posts rectifies that.

The SmugMug API supports REST, JSON, serialized PHP, and provides an XML-RPC service.  In SmugMug’s case REST really means POX, which is the interface Obra uses.  (I assume SmugMug did not re-invent the wheel when they designed their API, and rather based it on the Flickr API; they are very similar.) 

Client requests are HTTP GET’s (with the exception of uploading, which is a POST).  A request encodes the method and parameters in the URI.  For example, the following URI calls a method to get the albums for the session with ID 1ac.

http://api.smugmug.com/hack/rest/1.2.0/?method=smugmug.albums.get&SessionID=1ac

Responses all follow the same basic form.  The root element is <rsp/>, the first child node is <method/> and contains the name of the method called, and the second child node is the response element.  In the case of the method smugmug.albums.get the node is <Albums/>.

<rsp stat="ok">
   <method>smugmug.albums.get</method>
   <albums>
      <album id="0" title="2007-01-01" key="smgms">
         <category id="41" name="Airplanes" />
      </album>
   </albums>
</rsp>

Other methods return different elements for the second child node, such as <Categories/>, <Images/>, and <Login/>.

If the method call resulted in an error an error response is sent instead.  An example error response is shown below.

<rsp stat="fail">
  <err code="17" msg="invalid method" />
</rsp>

I wanted to wrap up this POX API in a tidy package.  I did not want any of the library code to parse XML.  I wanted the code to only operate on strongly typed objects, i.e. Album, Image, etc.  If a method call to SmugMug resulted in an error I wanted an exception to be thrown – avoid checking flags.  More importantly, I did not want to have to write any of this.  I rightly assumed that I code use WCF (and .NET) to handle all of the heavy lifting.  I’ll cover how in the following posts.

Page optimized by WP Minify WordPress Plugin