NAME
RT::Atom - The RT-Atom API
VERSION
This document describes version 0.03 of RT::Atom, released May 19, 2004.
DESCRIPTION
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.
The RT-Atom URI space
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:
/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
Sample exchange
Create an autrijus
user, then add it to all groups that has root
as member.
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.)
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:
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.)
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:
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
:
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
:
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>
Authentication
The authentication algorithm uses the WWW-Authenticate
, Authorization
, and X-WSSE
headers as specified in the Atom Authentication Protocol.
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
.
Identity Switching
Once authenticated, the server should check for the X-RT-CurrentUser
header. If this header is present, it takes one of the following actions:
- If the authenticated user does not have the
SuperUser
right -
The server returns 401 Authorization Required without processing the request body.
- If the server cannot find the new user
-
The server returns 406 Forbidden without processing the request body.
- If the user has the
SuperUser
right, and a new user is found -
The client assumes the identity of the new user specified in the header. The request proceeds as usual.
Content Negotiation
The server understands a number of HTTP headers for content negotiation:
- Accept
-
Specifies the content type the client is willing to process. A RT-Atom client must include
application/atom+xml
in itsAccept
list. - Accept-Charset
-
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.If none of the requested encodings are supported by the server, a 406 Not Acceptable error is returned.
- Accept-Language
-
The languages to use in human-readable
CDATA
parts, notably response texts.
RESOURCE TYPES
Container
...
ResultSet
...
Object
...
Property
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:
% 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/
.
OPERATIONS
Here is a list of all operations supported by this API, including their possible response status codes and meanings:
Search - GET FeedURI
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.
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.
Get - GET EditURI
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.
Set - PUT EditURI
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 - DELETE EditURI
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.
Describe - OPTIONS [ PostURI | EditURI | FeedURI ]
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.
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.
Add - POST PostURI (Container)
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.
Update - POST PostURI (Object)
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.
LINK DISCOVERY
Methods, object membership and properties are all discovered via the link
tag inside AtomFeed and AtomEntry constructs.
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.
The href
attribute is the target URI. The rel
attribute determines the type of the link target:
- service.feed
-
A Container.
- service.edit
-
An Object or Property.
- service.post
-
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>
SEE ALSO
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
AUTHORS
Autrijus Tang <autrijus@autrijus.org>
COPYRIGHT
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.