The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 its Accept 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 (&#xABCD;) 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

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

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.