Thursday, March 13, 2014

ROWS and IDLs

This is part of a series of posts. You can find the context of this exploration here.

This week I was looking into different approaches for dealing with input constraints and requirements and I found this great talk by Mike:





That prompted me into digging into each of these approaches and trying to understand what they look like in real life. I wanted examples, because reading specifications can be quite confusing while comparing/contrasting.

I'm 100% sure that there are plenty of errors/mistakes/typos in these examples. If/when you notice them, just let me know and I'll correct them (I tried as much as possible to get from the official specification, but I edited them sometimes when space was a constrain).

Just a side note: apart from ALPS, most of the approaches start from API documentations or API entry points. If you know how they could be embedded into the human web (e.g. HTML), I'd love to add those examples here too!

So, lets get started: show me the code!

Hydra (200?)

Hydra is the specification I probably relate to the most. That's probably because Markus is super engaged with our design and has even kindly created a proposal of his own.

It just makes sense to me. It is a bit verbose and I agree it can be simplified, but all the information that's needed is there. The semantics of the request parameters are kept.

{
  "@id": "/hydra/api-demo/vocab#EntryPoint/issues",
  "@type": "http://purl.org/hydra/core#Link",
  "description": "The collection of all issues",
  "label": "issues",
  "operation": [{
    "@type": "CreateResourceOperation",
    "label": "Creates a new Issue entity"
    "method": "POST",
    "expects": {
      "@id": "/hydra/api-demo/vocab#Issue",
      "@type": "hydra:Class",
      "description": "An Issue tracked by the system.",
      "label": "Issue"
      "supportedProperties": [{
          "property": "/hydra/api-demo/vocab#Issue/title",
          "readonly": false,
          "writeonly": false
        }, {
          "property": "/hydra/api-demo/vocab#Issue/description",
          "readonly": false,
          "writeonly": false
         }, {
          "property": "/hydra/api-demo/vocab#isOpen",
          "readonly": false,
          "writeonly": false
         }, {
          "property": "/hydra/api-demo/vocab#Issue/raisedBy",
          "readonly": true,
          "writeonly": false
         }]
    },
}

Siren (2012)

Second to Hydra is Siren. Actions are taken on resources, but both the semantics of the actions (e.g. Add Item) as well as the parameter names (e.g. "orderNumber) are lost.

{
  "class": [ "order" ],
  "properties": { 
      "orderNumber": 42, 
      "itemCount": 3,
      "status": "pending"
  },
  "actions": [{
    "name": "add-item",
    "title": "Add Item",
    "method": "POST",
    "href": "http://api.x.io/orders/42/items",
    "type": "application/x-www-form-urlencoded",
    "fields": [{
        "name": "orderNumber", "type": "hidden", "value": "42"
      }, {
        "name": "productCode", "type": "text"
      }, {
        "name": "quantity", "type": "number"
    }]
  }]
}

Swagger (2009)

Very close to Siren comes Swagger, which also has the notion of the operations taken on the resource, but with the semantics of the parameters being lost too.

"apis":[
  {
    "path": "/pet.{format}/{petId}",
    "description": "Operations about pets",
    "operations": [
      {
        "parameters":[
          {
            "paramType": "path",
            "name": "petId",
            "description": "ID of pet that needs to be fetched",
            "dataType": "integer",
            "format": "int64",
            "required": true,
            "minimum": 0,
            "maximum": 10
          }
        ],
        ...

RSDL (2013)

RSDL, like WADL, feels a bit awkward to embed in a JSON payload. In addition to that, it seems like you lose the application level semantic of the PUT is lost. It is important to note that the semantics of the parameters are kept though.
  <link rel="update" href="/api/clusters/{cluster:id}">
  <request>
    <http_method>PUT</http_method>
    <headers>
      <header required="true">
        <name>Content-Type</name>
        <value>application/xml|json</value>
      </header>
    </headers>
    <body>
      <type>Cluster</type>
      <parameters_set>
        <parameter type="xs:string" required="false">
          <name>cluster.name</name>
        </parameter>
        <parameter type="xs:string" required="false">
          <name>cluster.description</name>
        </parameter>
        <parameter type="xs:string" required="false">
          <name>cluster.cpu.id</name>
        </parameter>
        <parameter type="xs:boolean" required="false">
          <name>cluster.gluster_service</name>
        </parameter>
        <parameter type="xs:boolean" required="false">
          <name>cluster.threads_as_cores</name>
        </parameter>
      </parameters_set>
    </body>
  </request>
  <response>
    <type>Cluster</type>
  </response>
  </link>

WADL (2009)

WADL is a bit confusing to me because I'm not entirely sure how to embed in my messages. It feels like this is a document I'd find that would globally describe my API, rather than control that I'd find at the message level. Perhaps someone can point me to a better example?

<?xml version="1.0"?> 
 <application> 
   <grammars> 
     <include href="NewsSearchResponse.xsd"/> 
     <include href="Error.xsd"/> 
   </grammars> 
    <resources base="http://api.search.yahoo.com/NewsSearchService/V1/"> 
     <resource path="newsSearch"> 
       <method name="GET" id="search"> 
         <request> 
           <param name="query" type="xsd:string" 
             style="query" required="true"/> 
           <param name="type" style="query" default="all"> 
             <option value="all"/> 
             <option value="any"/> 
             <option value="phrase"/> 
           </param> 
           <param name="language" style="query" type="xsd:string"/> 
         </request> 
         <response status="200"> 
           <representation mediaType="application/xml" 
             element="yn:ResultSet"/> 
         </response> 
       </method> 
     </resource> 
   </resources> 
  </application>

JSON Home (2013)

JSON Home looks a lot like a "pre-computed OPTIONS" language, giving you "hints" of which HTTP operations you can perform. Cool, but not exactly at the level that I'm looking for.

   "http://example.org/rel/widget": {
     "href-template": "/widgets/{widget_id}",
     "href-vars": {
       "widget_id": "http://example.org/param/widget"
     },
     "hints": {
       "allow": ["GET", "PUT", "DELETE", "PATCH"],
       "representations": ["application/json"],
       "accept-patch": ["application/json-patch"],
       "accept-post": ["application/xml"],
       "accept-ranges": ["bytes"]
     }
   }

ALPS

What I love about ALPS is that their example starts with a HTML page! With a form! Now we are talking!

If I understand this correctly, rel="profile" is a magic keyword that informs you that elements with class="search" will be tied to a specific application level semantic.

<!-- sample HTML document -->                    
<html>
    <head>
        <link rel="profile" href="http://alps.io/documents/search" />
    </head>
    <body>
        <form class="search" action="..." method="get">
            <input type="text" name="search" value="..." />
            <select name="resultType">
                <option value="summary" />
                <option value="detailed" />
            </select>
            <input type="submit" />
        </form>
    </body>
</html>                           

You'd find the following in this URL http://alps.io/documents/search, which would tell you that that form lets you search via parameters like "search".

<?xml version="1.0"?>
<alps version="1.0">
    <doc href="http://example.org/samples/full/doc.html" />

    <descriptor id="search" 
        type="safe">
        <doc format="text">
            A search form with two inputs.
        </doc>
        <descriptor href="#resultType" />
        <descriptor id="value"
            name="search" 
            type="semantic">
            <doc>input for search</doc>
        </descriptor>
    </descriptor>

    <descriptor id="resultType"
        type="semantic">
        <doc>results format</doc>
        <ext 
            href="http://alps.io/ext/range" 
            value="summary,detail" />
    </descriptor>
</alps>

Atom Service Documents (2007)

Atom service documents work like JSON HOME, in the sense that you can express your affordances via the HTTP methods that you Accept as well as the Content-Types that you'd expect.

  <service xmlns="http://www.w3.org/2007/app"
           xmlns:atom="http://www.w3.org/2005/Atom">
    <workspace>
      <atom:title>Main Site</atom:title>
      <collection
          href="http://example.org/blog/pic" >
        <atom:title>Pictures</atom:title>
        <accept>image/png</accept>
        <accept>image/jpeg</accept>
        <accept>image/gif</accept>
      </collection>
    </workspace>
  </service>
The content of an "app:accept" element value is a media range as defined in [RFC2616]. The media range specifies a type of representation that can be POSTed to a Collection.
      HTTP/1.1 201 Created
      ...
      <entry xmlns="http://www.w3.org/2005/Atom">
        <title>Atom-Powered Robots Run Amok</title>
        <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
        <content>Some text.</content>
        <link rel="edit"
            href="http://example.org/edit/first-post.atom"/>
      </entry>

JSON Hyperschema (2013)

While JSON Hyperschema don't express exactly the "affordances", they do express the "restrictions", via a "required" property.

{
    "title": "Written Article",
    "type": "object",
    "properties": {
        "id": {
            "title": "Article Identifier",
            "type": "number"
        },
        "title": {
            "title": "Article Title",
            "type": "string"
        },
        "authorId": {
            "type": "integer"
        },
        "imgData": {
            "title": "Article Illustration (small)",
            "type": "string",
            "media": {
                "binaryEncoding": "base64",
                "type": "image/png"
            }
        }
    },
    "required" : ["id", "title", "authorId"]
}

WSDL (2001)

WSDL describes a SOA service, via describing the messages, the parameters and the methods. Not great.

<definitions name="HelloService"
   targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl"
   xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 
   <message name="SayHelloRequest">
      <part name="firstName" type="xsd:string"/>
   </message>
   <message name="SayHelloResponse">
      <part name="greeting" type="xsd:string"/>
   </message>

   <portType name="Hello_PortType">
      <operation name="sayHello">
         <input message="tns:SayHelloRequest"/>
         <output message="tns:SayHelloResponse"/>
      </operation>
   </portType>

   <binding name="Hello_Binding" type="tns:Hello_PortType">
   <soap:binding style="rpc"
      transport="http://schemas.xmlsoap.org/soap/http"/>
   <operation name="sayHello">
      <soap:operation soapAction="sayHello"/>
      <input>
         <soap:body
            encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
            namespace="urn:examples:helloservice"
            use="encoded"/>
      </input>
      <output>
         <soap:body
            encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
            namespace="urn:examples:helloservice"
            use="encoded"/>
      </output>
   </operation>
   </binding>

   <service name="Hello_Service">
      <documentation>WSDL File for HelloService</documentation>
      <port binding="tns:Hello_Binding" name="Hello_Port">
         <soap:address
            location="http://www.examples.com/SayHello/">
      </port>
   </service>
</definitions>

RESTDESC (2010)

I don't think I follow RESTDESC entirely, but I think it defines the requests that can be made (via the _:request property).

Example from here and here.

@prefix book: <http://example.org/book#>.
{
  ?book book:reviewForm ?reviewForm.
}
=>
{
  _:request http:methodName "POST";
  http:requestURI ?reviewForm;
  http:body [tmpl:formData ("text=" ?text)];
  http:resp [tmpl:represents ?review].
  ?book book:review ?review.
  ?review book:reviewText ?text.
}.

API Blueprint (2012)

I don't think I follow API Blueprint either, but if I had to guess, you'd write a document in the format below and that would generate an API description that a client could bind to?

If so, I'm not entirely sure how that's going to be embedded in the message.

## Star [/gists/{id}/star{?access_token}]
Star resource represents a Gist starred status. 
The Star resource has the following attribute:
- starred
+ Parameters
    + id (string) ... ID of the gist in the form of a hash
    + access_token (string, optional) ... Gist Fox API access token.    
+ Model (application/hal+json)
    HAL+JSON representation of Star Resource.
    + Headers
            Link: <http:/api.gistfox.com/gists/42/star>;rel="self"
    + Body
            {
                "_links": {
                    "self": { "href": "/gists/42/star" },
                },
                "starred": true
            }

Related work

Few other ones that I couldn't find good examples (searching the web or reading their specs) to embed. If you are familiar with one of these and want to have them included here, feel free to send me an example:
Here are some of the IDLs that I didn't mention because I was already quite familiar with :) But if you are not, it is worth a read:

How do they relate to one another?

This is probably an arbitrary classification, but it helps me organize them in classes that are important to me (they probably wont make that much sense to an arbitrary reader).

The RO column is easy to understand.

The IN column is easy too, but there is a lot of data that may be missing/incorrect (if you know better, feel free to drop me a line and I'll get that corrected). It refers to the notion of whether the control information is embedded with the message or is found in a global master document.

The AS column and PS column are slightly harder to explain, I think.

The AS column refers to whether the language "explains natively" what action is being taken at an "application level" (as opposed to at a "http protocol" level). For example, while a HTML form doesn't explain to a machine what is being done, hydra's ReplaceResourceOperation does.

The PS column refers to whether the language "enforces natively" what the parameters should look like (e.g. the types and properties that are required).

Having said that, lets move on to the table:

YR = Year
RO = Resource Oriented
IN = Action control inlined with the message *
AS = Application level semantics pre-defined **
PS = Parameter class/property semantics/requirements ***

as opposed to a global description of the entire API.
**  as opposed to HTTP-level semantics of the actions. A pre-defined set of actions explains the "what" (semantics) in addition to the "how" (http methods).
*** as opposed to the usage of mime types or opaque parameters.

? are for fields that I'm still collecting

+-------------------------------------------------+
| NAME                 |  YR  | RO | IN | AS | PS |
+-------------------------------------------------+
| Hydra                | 200? | Y  | YN | Y  | Y? |
| ATOM                 | 2007 | Y  | Y  | Y  | Y  |
+-------------------------------------------------+
|     Realm of IDLs with user/opaque actions      |
+-------------------------------------------------+
| ALPS?                | 201? | Y  | Y? | ?  | Y? |
| Siren                | 2012 | Y  | Y  | ?  | Y? |
+-------------------------------------------------+
|   Realm of IDLs with user/opaque parameters     |
+-------------------------------------------------+
| Swagger              | 2009 | Y  | Y  | N  | N  |
| JSON-HOME            | 2013 | Y  | Y  | N  | N  |
+-------------------------------------------------+
|        Realm of IDLs that are not inlined       |
+-------------------------------------------------+
| RSDL                 | 2013 | Y  | N  | N  | Y  |
| WADL                 | 2009 | Y  | N  | N  | Y  |
| BLUEPRINT            | 2012 | Y  | N  | N  | Y  |
| RESTDESC?            | 2010 | Y  | ?  | N  | Y  |
+-------------------------------------------------+
|         Realm of IDLs that are not RO           |
+-------------------------------------------------+
| WSDL                 | 2001 | N  | N  | N  | N  |
+-------------------------------------------------+

Where do we go from here?

Stay tuned. More to follow!