RT::Atom - The RT-Atom API
This document describes version 0.03 of RT::Atom, released May 19, 2004.
This RT extension implements a REST-style web service interface, based on the Atom draft specification, version 0.3.
For more information on Atom and REST, please consult the references in the "SEE ALSO" section.
Some example canonical URIs are:
/Atom/0.3 # FeedURI (Container) /Atom/0.3/RT-Tickets # FeedURI (Container) /Atom/0.3/RT-Tickets # PostURI (Container) /Atom/0.3/RT-Tickets/15 # EditURI (Object) /Atom/0.3/RT-Tickets/15,16,17 # PostURI (ResultSet) /Atom/0.3/RT-Tickets/15.Subject # EditURI (Property) /Atom/0.3/RT-Tickets/15/Transactions # FeedURI (Container)
Note that the 15 above is the Id; if you want element indices, use these URIs instead:
15
Id
/Atom/0.3/RT-Users/*0 # EditURI (Object) /Atom/0.3/RT-Users/*-1 # EditURI (Object) /Atom/0.3/RT-Users.Count # EditURI (Property)
A RT-Atom server may also supply alias URIs. Whenever an user request such a URI, it is redirected to the canonical URL with a 301 Moved Permanently.
Here are some example aliases:
/Atom # /Atom/0.3 /Atom/0.3/tickets # /Atom/0.3/RT-Tickets /Atom/0.3/Tickets # /Atom/0.3/RT-Tickets /Atom/0.3/Ticket/15 # /Atom/0.3/RT-Tickets/15 /Atom/0.3/Users/somename # /Atom/0.3/Users/1234
Create an autrijus user, then add it to all groups that has root as member.
autrijus
root
First, the RT::Client code using its OO interface:
my $rt = RT::Client->new('http://guest:guest@localhost/'); my $user = $rt->Users->add( Name => 'autrijus', EmailAddress => 'autrijus@example.com', ); $rt->Groups->search(Members => { Name => 'root' })->update( Members => { add => $user }, );
And now the actual requests and responses. First, the Authentication part: (The Accept header will be omitted for brevity from now on.)
Accept
HEAD /Atom/0.3 Accept: application/atom+xml,*/* 401 Authorization Required WWW-Authenticate: WSSE realm="localhost", profile="UsernameToken"
Log in and probe the Users collection:
Users
OPTIONS /Atom/0.3/Users X-WSSE: UsernameToken Username="guest", ... 301 Moved Permanently Location: /Atom/0.3/RT-Users
Try again: (The X-WSSE header is generated anew; it will be omitted for brevity from now on.)
X-WSSE
OPTIONS /Atom/0.3/Users X-WSSE: UsernameToken Username="guest", ... 200 OK <entry> <content type="text/xml" mode="xml"> <body Name="" EmailAddress="" ...> ... </body> </content> </entry>
Now create a user. RT-Atom supports two type of payloads for this, distinguished by their Content-Type headers. First is AtomEntry:
Content-Type
POST /Atom/0.3/Users Content-Type: application/atom+xml <entry> <content type="text/xml" mode="xml"> <body Name="autrijus" EmailAddress="autrijus@example.com" /> </content> </entry>
Another one is a form post. Clients should use multipart/form-data:
multipart/form-data
POST /Atom/0.3/Users Content-type: multipart/form-data; boundary=--- --- Content-Disposition: form-data; name="Name" autrijus --- Content-Disposition: form-data; name="EmailAddress" autrijus@example.com
However, the server may also support application/x-www-form-urlencoded:
application/x-www-form-urlencoded
POST /Atom/0.3/Users Content-Type: application/x-www-form-urlencoded Name=autrijus&EmailAddress=autrijus%4dexample.com
In all cases above, the server will return:
303 See Other Location: /Atom/0.3/RT-Users/20
Now we can learn something about the freshly created user:
GET /Atom/0.3/RT-Users/20 200 OK <entry> <content type="text/xml" mode="xml"> <body Name="autrijus" EmailAddress="autrijus@example.com" ...> ... </body> </content> </entry>
Next we learn something about Groups:
OPTIONS /Atom/0.3/Groups 301 Moved Permanently Location: /Atom/0.3/RT-Groups OPTIONS /Atom/0.3/RT-Groups 200 OK <entry> <content type="text/xml" mode="xml"> <body Name="" ...> <Members /> </body> </content> </entry>
Now we make a query on Groups:
HEAD /Atom/0.3/RT-Groups?Members-name=root&rows=all 200 OK Content-Location: /Atom/0.3/RT-Groups/1,2,3,5,8,13
Before we perform an update on the result set, we again probe for its representation:
OPTIONS /Atom/0.3/RT-Groups/1,2,3,5,8,13 200 OK <entry> <content type="text/xml" mode="xml"> <body> <Members /> </body> </content> </entry>
Finally, the modification and its response:
POST /Atom/0.3/RT-Groups/1,2,3,5,8,13 Content-Type: application/x-www-form-urlencoded Members-add=30
The server may respond with this:
207 Multiple Status <entry> <content type="multipart/parallel" mode="xml"> <body> <response status="200">Member added.</head> <response status="200">Member added.</head> </body> </content> </entry>
The authentication algorithm uses the WWW-Authenticate, Authorization, and X-WSSE headers as specified in the Atom Authentication Protocol.
WWW-Authenticate
Authorization
However, instead of using plaintext as the shared password between client and server, RT-Atom uses this digest function:
md5_hex(join(':', $username, $realm, md5_hex($password)));
The RT server may choose to support other authentication methods, such as Basic or Digest.
Basic
Digest
Once authenticated, the server should check for the X-RT-CurrentUser header. If this header is present, it takes one of the following actions:
X-RT-CurrentUser
SuperUser
The server returns 401 Authorization Required without processing the request body.
The server returns 406 Forbidden without processing the request body.
The client assumes the identity of the new user specified in the header. The request proceeds as usual.
The server understands a number of HTTP headers for content negotiation:
Specifies the content type the client is willing to process. A RT-Atom client must include application/atom+xml in its Accept list.
application/atom+xml
The character encoding expected by the client. If unspecified, defaults to UTF-8. If the requested encoding cannot represent certain codepoints in the response, the server must use XML character references (ꯍ) instead.
UTF-8
ꯍ
If none of the requested encodings are supported by the server, a 406 Not Acceptable error is returned.
The languages to use in human-readable CDATA parts, notably response texts.
CDATA
...
A scalar attached to a single Object; accepts only Get/Set operations (i.e. GET/PUT methods).
The MIME type for both operations is text/plain, with a mandatory extra newline at the end. For example:
text/plain
% lwp-request -m GET .../Tickets/15.Status new % echo resolved | lwp-request -m PUT .../Tickets/15.Status resolved
The ... above stands for something like http://example.com/Atom/0.3/.
http://example.com/Atom/0.3/
Here is a list of all operations supported by this API, including their possible response status codes and meanings:
Search for objects within an container.
Possible query parameters: rows (mandatory), page, query, columns. Additional query parameters may also be available.
If entries are found, the Content-Location header is set to a URL pointing to the ResultSet.
Content-Location
200: Success. Body is the result serialized as an AtomFeed. 400: Request failed. Body is the error message. 404: There is no container matching the specified URI.
Retrieve a representation of an object or property.
Possible query parameters: expand.
200: Success. Body is the serialized item. 400: Request failed. Body is the error message. 404: There is no object matching the specified URI.
Modifies an object or property with the serialization in the request body.
200: Success. Body is the serialized item again. 400: Request failed. Body is the error message. 404: There is no object matching the specified URI.
Clients without PUT support may use POST EditURI!PUT instead.
Remove the specified object.
200: Successfully deleted. Body is the success message. 400: Request failed. Body is the error message. 404: There is no object matching the specified URI.
Clients without DELETE support may use POST EditURI!DELETE instead.
On a container's PostURI, returns the schema of objects acceptable by this container.
On an object's PostURI, returns the schemata acceptable by it, differentiated with the type attribute.
type
On EditURI, returns the schema of the object or the property, which is a GET without actual contents.
On FeedURI, returns the schema of available query parameters and their types.
200: Success. Body is the requested schema. 400: Request failed. Body is the error message. 404: There is no container matching the specified URI.
Clients without OPTIONS support may use GET AnyURI!OPTIONS instead.
Create a new object from the AtomEntry in the request's body.
200: Created, but the new object has no EditURI. Body is the success message. 303: Created. The 'Location' header is set to the new object's EditURI (for subsequent Get/Update). Body is the success message. 400: Request failed. Body is the error message. 404: There is no container matching the specified URI.
Updates an object.
207: Updated. Body is the status code and messages for each update. 400: Request failed. Body is the error message. 404: The specific object is not found, or supports no such post type.
Methods, object membership and properties are all discovered via the link tag inside AtomFeed and AtomEntry constructs.
link
The design goal is to facilitate lazy loading - the client need not to follow the link to retrieve any representations immediately; it can wait until the first operation is performed on that object, and follow the link responsible for that operation.
Whenever the client receives an Atom construct, it may look at each link tag. The title attribute is the member name of the link; but if it begins with _, then it is a backlink.
title
_
The href attribute is the target URI. The rel attribute determines the type of the link target:
href
rel
A Container.
An Object or Property.
An operation supported by the object that shares the same title. If no such object is found, this operation applies to the object itself.
For example, an object's Atom representation may have the following links:
<link title="Groups" rel="service.feed" href="/Atom/0.3/RT-Groups" /> <link title="Groups" rel="service.post" href="/Atom/0.3/RT-Groups" />
The client may then infer these relationships:
$obj has a member named Groups.
$obj->Groups is a Container.
$obj->Groups may be called to add an object inside it.
$obj->Groups->add( key => 'value' ) should be translated to this:
POST /Atom/0.3/RT-Groups Content-Type: application/x-www-form-urlencoded key=value
Or this:
POST /Atom/0.3/RT-Groups Content-Type: application/atom+xml <entry> <content type="text/xml" mode="xml"> <body key="value" /> </content> </entry>
RT::Client, XML::Atom.
Atom Tutorial: http://www.atomenabled.org/developers/tutorials/api-quick-guide.php
Atom API Specification (definitions of FeedURI, EditURI and PostURI): http://www.atomenabled.org/developers/api/atom-api-spec.php
Atom Format Specification (definitions of AtomFeed and AtomEntry): http://www.atomenabled.org/developers/syndication/atom-format-spec.php
Atom Authentication Protocol: http://www.xml.com/pub/a/2003/12/17/dive.html
HTTP 1.1 Status codes: http://www.w3.org/Protocols/rfc2616/rfc2616.html.
Paul Prescod's REST resources: http://www.prescod.net/rest/
The Atom Wiki: http://www.intertwingly.net/wiki/pie/FrontPage
The REST Wiki: http://internet.conveyor.com/RESTwiki/moin.cgi/FrontPage
Autrijus Tang <autrijus@autrijus.org>
Copyright 2004 by Best Practical Solutions, LLC.
(Except where explicitly superseded by other copyright notices)
This work is made available to you under the terms of Version 2 of the GNU General Public License. A copy of that license should have been provided with this software, but in any event can be snarfed from www.gnu.org.
This work is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
To install RT::Atom, copy and paste the appropriate command in to your terminal.
cpanm
cpanm RT::Atom
CPAN shell
perl -MCPAN -e shell install RT::Atom
For more information on module installation, please visit the detailed CPAN module installation guide.