Very briefly and over-simplified:
An acronym for “REpresentational State Transfer”
an architectural style for designing APIs
REST is not a technology or programming language
the architecture of the Web adapted as a platform for any kind of application
resources addressable by URIs
representations with media types
RESTful APIs require a contract and documentation
Inherently a tedious and repetitive task
Documentation was doomed get out of sync with the service
Not like documenting a Java API where you can just use Javadoc
We needed a domain specific language to describe the API
We needed an open source toolchain we could share with OpenStack
We look at several options (Swagger, Mashery, Apigee, creating our own DSL, etc), but also discovered WADL...
1 <?xml version="1.0" encoding="UTF-8"?>
2
<application xmlns="http://wadl.dev.java.net/2009/02"
4 xmlns:xs="http://www.w3.org/2001/XMLSchema">
<resources base="http://localhost/">
6 <resource path="path/to/record/{date}">
<param style="template" type="xs:date" name="date"/>
8 <method name="GET"/>
</resource>
10 </resources>
</application>
Tree structure (can also be mixed path and tree):
1 <application xmlns="http://wadl.dev.java.net/2009/02"
2 xmlns:xs="http://www.w3.org/2001/XMLSchema">
<resources base="http://localhost/">
4 <resource path="path">
<resource path="to">
6 <resource path="record">
<resource path="{date}">
8 <param style="template" type="xs:date" name="date"/>
<method name="GET"/>
10 </resource>
</resource>
12 </resource>
</resource>
14 </resources>
</application>
1 <application xmlns="http://wadl.dev.java.net/2009/02"
2 xmlns:wapi="http://widget/api/v1">
<grammars>
4 <include href="xsd/widget.xsd"/>
</grammars>
6 <resources base="https://test.api.openstack.com">
<resource path="widgets">
8 <method href="#getMetadata"/>
</resource>
10 <resource path="gadgets">
<method href="#getMetadata"/>
12 </resource>
</resources>
14 <method name="GET" id="getMetadata">
<response status="200 203">
16 <representation mediaType="application/xml" element="wapi:metadata"/>
</response>
18 </method>
</application>
Resources can also be of one or more resource_type where a resource_type is an element that contains resources, params, and methods.
Methods and resource_types can be referred to within wadls and between wadls.
1 <method name="GET" id="versionDetails">
2 <response status="200 203">
<representation mediaType="application/xml" element="common:version">
4 <param name="location" style="plain" type="xsd:anyURI"
required="true"
6 path="/common:version/atom:link[@rel='self']/@href">
<link resource_type="#VersionDetails" rel="self"/>
8 </param>
</representation>
10 </response>
</method>
Inside the representation of the response that this is a valid xpath and the type must be of this type. Note that required="true".
1 <section>
2 <title>Volume Lists</title>
<para>
4 These operations provide a list of volumes associated
with a particular tenant. Volumes contain a status
6 attribute that can be...
</para>
8 <resources xmlns="http://wadl.dev.java.net/2009/02">
<resource href="os-block-storage-1.0.wadl#Volumes">
10 <method href="listVolumes"/>
</resource>
12 </resources>
</section>
1 ...
2 <method name="GET" id="getMetadata">
<wadl:doc xml:lang="EN" title="Get metadata"
4 xmlns="http://docbook.org/ns/docbook">
<para>Ipsum lorem:
6 <itemizedlist>
<listitem><para>Ipsum</para></listitem>
8 <listitem><para>Lorem</para></listitem>
</itemizedlist>
10 </para>
<wadl:doc>
12 <response status="200 203">
<representation mediaType="application/xml" element="wapi:metadata"/>
14 </response>
</method>
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!-- Volume Methods -->
<method name="GET" id="listVolumes">
4 <doc xml:lang="EN" title="List Volumes">
<db:para role="shortdesc">
6 List all volumes (IDs, names, links).
</db:para>
8 <db:para>
A list of volumes. Each volume contains IDs, names, and
10 links -- other attributes are omitted.
</db:para>
12 </doc>
<request>
14 <param name="changes-since" style="query" required="false" type="xsd:dateTime"/>
<param name="type" style="query" required="false" type="osapi:UUID"/>
16 <param name="backup" style="query" required="false" type="osapi:UUID"/>
<param name="name" style="query" required="false" type="xsd:string"/>
18 <param name="marker" style="query" required="false" type="osapi:UUID"/>
<param name="limit" style="query" required="false" type="xsd:int"/>
20 </request>
<response status="200 203">
22 <representation mediaType="application/xml" element="bs:volumes">
<doc xml:lang="EN">
24 <xsdxt:code href="samples/core/volumes-sparse.xml" />
</doc>
26 </representation>
<representation mediaType="application/json">
28 <doc xml:lang="EN">
<xsdxt:code href="samples/core/volumes-sparse.json" />
30 </doc>
</representation>
32 </response>
<!-- Common Faults -->
34 <response>
<representation mediaType="application/xml" element="bs:blockstorageFault"/>
36 <representation mediaType="application/json"/>
</response>
38 <response status="503">
<representation mediaType="application/xml" element="bs:serviceUnavailable"/>
40 <representation mediaType="application/json"/>
</response>
42 <response status="401">
<representation mediaType="application/xml" element="bs:unauthorized"/>
44 <representation mediaType="application/json"/>
</response>
46 <response status="403">
<representation mediaType="application/xml" element="bs:forbidden"/>
48 <representation mediaType="application/json"/>
</response>
50 </method>
Coming soon: api.rackspace.com
Usually, REST service starts as a document
Implementation (sometimes in the form of a mock is created)
The implementaion and clients provide feedback, this leads to changes.
Docs / impl must remain in synch, through out the process.
Challenging / Error prone.
Eventually, the implementation stabalizes and the document becomes a contract.
Used by clients and alternate implementations.
It's important to test that the implementaiton and the docs that describe it conform to one another.
However…
QA/QE teams are not focused on document conformance. They consume the docs to write tests.
Focused almost exclusively on the functionality of the service.
In our experience, tests and implementation tend to drift together (inadvertently) unless there is detail review.
We usually don't find errors until alternate implementations and new clients come on the scene.
Fixing errors at this point is hard!
A validator needs to be able to tell the difference between an HTTP message that
…meets all the criteria defined in the documentation
…violates the criteria docs
The validator should be able to categorize messages that are not valid based on the expected error code, so that the response code generate by the service can be verified
Accepting messages that meet some criteria is a common problem in Computer Science…
…one techinque for solving the problem of accepting messages is to utilize an automaton
An automaton is a state machine that…
Transitions from a start state to other states based on current input
If after reading the input the machine is in an accept the message is accepted, otherwise the message is rejected
The idea is to translate a WADL either…
stand alone
or from RackBook document
1 <?xml version="1.0" encoding="UTF-8"?>
2
<application xmlns="http://wadl.dev.java.net/2009/02"
4 xmlns:xs="http://www.w3.org/2001/XMLSchema">
<resources base="http://localhost/">
6 <resource path="path/to/record/{date}">
<param style="template" type="xs:date" name="date"/>
8 <method name="GET"/>
</resource>
10 </resources>
</application>
Initially, the WADL is translated into checker format an XML representation of the automaton
1 <?xml version="1.0" encoding="UTF-8"?>
2 <checker xmlns="http://www.rackspace.com/repose/wadl/checker"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
4 <step id="S0" type="START" next="d18e4 SE1 d21e2u"/>
<step type="URL_FAIL" id="d21e2u" notMatch="path"/>
6 <step type="URL" id="d18e4" match="path" next="d18e5 SE1 d21e3u"/>
<step type="URL_FAIL" id="d21e3u" notMatch="to"/>
8 <step type="URL" id="d18e5" match="to" next="d18e6 SE1 d21e4u"/>
<step type="URL_FAIL" id="d21e4u" notMatch="record"/>
10 <step type="URL" id="d18e6" match="record" next="d18e7 SE1 d21e5u"/>
<step type="URL_FAIL" id="d21e5u" notTypes="xs:date"/>
12 <step type="URLXSD"
id="d18e7"
14 match="xs:date"
label="date"
16 next="d18e9 d21e6m SE0"/>
<step type="METHOD_FAIL" id="d21e6m" notMatch="GET"/>
18 <step type="METHOD" id="d18e9" match="GET" next="SA"/>
<step id="SE0" type="URL_FAIL"/>
20 <step id="SE1" type="METHOD_FAIL"/>
<step id="SA" type="ACCEPT"/>
22 </checker>
Each element maps to a state in the machine
1 <?xml version="1.0" encoding="UTF-8"?>
2 <checker xmlns="http://www.rackspace.com/repose/wadl/checker"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
4 <step id="S0" type="START" next="d18e4 SE1 d21e2u"/>
<step type="URL_FAIL" id="d21e2u" notMatch="path"/>
6 <step type="URL" id="d18e4" match="path" next="d18e5 SE1 d21e3u"/>
<step type="URL_FAIL" id="d21e3u" notMatch="to"/>
8 <step type="URL" id="d18e5" match="to" next="d18e6 SE1 d21e4u"/>
<step type="URL_FAIL" id="d21e4u" notMatch="record"/>
10 <step type="URL" id="d18e6" match="record" next="d18e7 SE1 d21e5u"/>
<step type="URL_FAIL" id="d21e5u" notTypes="xs:date"/>
12 <step type="URLXSD"
id="d18e7"
14 match="xs:date"
label="date"
16 next="d18e9 d21e6m SE0"/>
<step type="METHOD_FAIL" id="d21e6m" notMatch="GET"/>
18 <step type="METHOD" id="d18e9" match="GET" next="SA"/>
<step id="SE0" type="URL_FAIL"/>
20 <step id="SE1" type="METHOD_FAIL"/>
<step id="SA" type="ACCEPT"/>
22 </checker>
All steps contain @id of type xs:ID
1 <?xml version="1.0" encoding="UTF-8"?>
2 <checker xmlns="http://www.rackspace.com/repose/wadl/checker"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
4 <step id="S0" type="START" next="d18e4 SE1 d21e2u"/>
<step type="URL_FAIL" id="d21e2u" notMatch="path"/>
6 <step type="URL" id="d18e4" match="path" next="d18e5 SE1 d21e3u"/>
<step type="URL_FAIL" id="d21e3u" notMatch="to"/>
8 <step type="URL" id="d18e5" match="to" next="d18e6 SE1 d21e4u"/>
<step type="URL_FAIL" id="d21e4u" notMatch="record"/>
10 <step type="URL" id="d18e6" match="record" next="d18e7 SE1 d21e5u"/>
<step type="URL_FAIL" id="d21e5u" notTypes="xs:date"/>
12 <step type="URLXSD"
id="d18e7"
14 match="xs:date"
label="date"
16 next="d18e9 d21e6m SE0"/>
<step type="METHOD_FAIL" id="d21e6m" notMatch="GET"/>
18 <step type="METHOD" id="d18e9" match="GET" next="SA"/>
<step id="SE0" type="URL_FAIL"/>
20 <step id="SE1" type="METHOD_FAIL"/>
<step id="SA" type="ACCEPT"/>
22 </checker>
Next steps are connected via @next type xs:IDREFs
1 <?xml version="1.0" encoding="UTF-8"?>
2 <checker xmlns="http://www.rackspace.com/repose/wadl/checker"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
4 <step id="S0" type="START" next="d18e4 SE1 d21e2u"/>
<step type="URL_FAIL" id="d21e2u" notMatch="path"/>
6 <step type="URL" id="d18e4" match="path" next="d18e5 SE1 d21e3u"/>
<step type="URL_FAIL" id="d21e3u" notMatch="to"/>
8 <step type="URL" id="d18e5" match="to" next="d18e6 SE1 d21e4u"/>
<step type="URL_FAIL" id="d21e4u" notMatch="record"/>
10 <step type="URL" id="d18e6" match="record" next="d18e7 SE1 d21e5u"/>
<step type="URL_FAIL" id="d21e5u" notTypes="xs:date"/>
12 <step type="URLXSD"
id="d18e7"
14 match="xs:date"
label="date"
16 next="d18e9 d21e6m SE0"/>
<step type="METHOD_FAIL" id="d21e6m" notMatch="GET"/>
18 <step type="METHOD" id="d18e9" match="GET" next="SA"/>
<step id="SE0" type="URL_FAIL"/>
20 <step id="SE1" type="METHOD_FAIL"/>
<step id="SA" type="ACCEPT"/>
22 </checker>
The format currently supports steps that check all aspects of the HTTP request, including …
URI Path Segment (Regex, XSD 1.1 simple type)
Required Headers (Regex, XSD 1.1 simple type)
Method (Regex)
Content Type
Well formness, XML and JSON
XML content via W3C XML Schema 1.1 Impl (Xerces, Saxon)
XPath assertions for XML (XPath 2.0)
Root element at a URI
Required plain parameters
Transformation is useful when using plain parameters to extend or a restrict a type based on context.
1 <!-- WADL -->
2 <resource path="widget"
xmlns="http://wadl.dev.java.net/2009/02"
4 xmlns:widget="http://rackspace.com/sample/widget">
<method name="GET">
6 <response>
<representation mediaType="application/xml"
8 element="widget:widget"/>
</response>
10 </method>
<method name="POST">
12 <request>
<representation mediaType="application/xml"
14 element="widget:widget">
<param name="widget" style="plain"
16 path="/widget:widget"
type="widget:WidgetForCreate"/>
18 </representation>
</request>
20 </method>
</resource>
The translation form WADL to checker format is not concerned with optimizing the number of steps — it's only concerned with the accuracy of the machine created.
Optimization stages are allowed.
An optimization stage is an XSLT that takes a checker format converts to one with less steps, but which accepts the same input
Optimization stages can be chained together
Various aspects of the pipeline are configurable
The strictness of the validation, what kinds of steps are added to the checker
Specialized options for each individual steps
XPath version (1.0/2.0)
XSD implementation to use (Saxon EE, Xerces)
XSLT implementation (Xalan, XSLTC, Saxon)
What optimization stages to use, if any
Requests that would otherwise result in an error condition, can be culled before they reach the backend service — this potentially save processing time
In this case, the validator can form much better error messages. For example:
404 Not found /path/to/{widgets}, got widgets but expecting widget | gadget
OpenStack APIs are extensible so XSD 1.0 is not an option, we chose XSD 1.1
Since OpenStack is an open and free platform, we have the goal of ensuring that everything that we develop remains open
We make sure we support Xerces (free opensource), and Saxon EE (proprietary)
During the development of the pipeline we have encountered a number of errors with the Xerces implementation
To date we've been unable to use Xerces successfully in production (In fairness XSD 1.1 support is still beta, most bugs resolved quickly, but still finding bugs, performance issues)
We've come to rely on XSD 1.1 features when there is yet a full, production ready, and free open source XSD 1.1 implementation -- looking at Schematron, RelaxNG
WADL uses XPath (… and JSONPath) — no easy way to handle these in XSLT, where as QNames are easily handled
Keeping the namespace prefixes correct after various levels of transformation can be challenging
The current implementation is copying all relevant namespace nodes when it sees an XPath
There are still problems esp. when there is contention for the default namespace
We're looking at parsing XPath 2.0 in XSLT — maybe via an extension
WADL Tools XSLs, schema, Oxygen framework, command line tools, java libraires for normalizing WADLs:
Cloud Docs Maven Plugin Maven plugin to take DocBook and WADL to generate API documentation to generate PDF and web output:
Repose A programable HTTP proxy that contains a WADL validator component built in:
API Checker The implementation of the validation pipeline and the runtime validator itself: