application/atom+xml representation:{
'id' : 'orkut.com:34KJDCSKJN2HHF0DW20394',
'name' : {'unstructured' : 'Jane Doe'}}
<entry xmlns="http://www.w3.org/2005/Atom>Person mapping rules:
<content type="text/xml">
<name>
<unstructured>Jane Doe</unstructured>
<name>
</content>
<title/>
<updated>2003-12-13T18:30:02Z</updated>
<author/>
<id>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</id>
</entry>
Activity application/atom+xml:{
'id' : 'http://example.org/activities/example.org:87ead8dead6beef/self/af3778',
'title' : { 'type' : 'html',
'value' : '<a href="foo">some activity</a>'
}
'updated' : '2008-02-20T23:35:37.266Z',
'content' : 'Some details for some activity',
'received' : '2008-02-20T23:35:37.266Z'}
<entry xmlns="http://www.w3.org/2005/Atom>Activity mapping rules:
<category term="status"/>
<id>http://example.org/activities/example.org:87ead8dead6beef/self/af3778</id>
<title type="html"><a href="foo">some activity</a></title>
<content>Some details for some activity</content>
<updated>2008-02-20T23:35:37.266Z</updated>
<link rel="self" type="application/atom+xml" href="http://www.google.com/activity/feeds/.../af3778"/>
<osa:received>2008-02-20T23:35:37.266Z</osa:received>
</entry>
application/atom+xml representation:{
'pokes' : 3,
'last_poke' : '2008-02-13T18:30:02Z'
}
<entry xmlns="http://www.w3.org/2005/Atom>AppData mapping rules:
<content type="text/xml">
<pokes>3</poke>
<last_poke>2008-02-13T18:30:02Z</last_poke>
</content>
<title/>
<updated>2003-12-13T18:30:02Z</updated>
<author><url>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</url></author>
<id>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</id>
</entry>
application/atom+xml representation:{
'orkut.com:34KJDCSKJN2HHF0DW20394' : {'pokes' : 3, 'last_poke' : '2008-02-13T18:30:02Z' },
'orkut.com:58UIDCSIOP233FDKK3HD44' : {'pokes' : 2, 'last_poke' : '2007-12-16T18:30:02Z' }
}
<feed xmlns="http://www.w3.org/2005/Atom>AppData mapping rules:
<id>...</id>
<title>...</title>
<entry>
<content type="text/xml">
<pokes>3</poke>
<last_poke>2008-02-13T18:30:02Z</last_poke>
</content>
<title/>
<updated>2008-02-13T18:30:02Z</updated>
<author><url>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</url></author>
<id>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</id>
</entry>
<entry>
<content type="text/xml">
<pokes>2</poke>
<last_poke>2007-12-16T18:30:02Z</last_poke>
</content>
<title/>
<updated>2007-12-16T18:30:02Z</updated>
<author><url>uurn:guid:orkut.com:58UIDCSIOP233FDKK3HD44</url></author>
<id>urn:guid:orkut.com:58UIDCSIOP233FDKK3HD44</id>
</entry>
</entry>
/groups/{uid}/self -- Collection of groups owned by the user,
which always contains 'all' and 'friends' and may contain more.
(Details TBD)
All
resources use the HTTP/AtomPub ETag-based optimistic concurrency
mechanism. Servers provide ETags for resources when they are
retrieved, and clients supply If-Match: headers with the etag when they
perform modifying operations. If the resource has been modified
between the initial retrieval and the update, the update fails with a
conflict HTTP code.
A server which chooses not to support optimistic concurrency should
omit ETags on its responses.
POST /batch_proxy HTTP/1.1
Host: container.org
Content-Type: multipart/mixed; boundary=batch-a73hdj3dy3mm347ddjjdf
-batch-a73hdj3dy3mm347ddjjdf
X-Batch-Operation: POST /people/me/all HTTP/1.1
Host: container.org
Content-Type: application/json
...representation of new Person elided...
-batch-a73hdj3dy3mm347ddjjdf
X-Batch-Operation: GET /people/me/friends?startPage=5&count=10&format=json-c
Host: container.org
If-None-Match: "837dyfkdi39df"
HTTP/1.1 200 OKIn this case, the friend add succeeded and the response to the retrieval of page 5 of the friends list indicates that page 5 didn't change (the friend was added below that page's last entry). If the resource had changed, of course, you'd see a list of the friends in the response.
Content-Type: multipart/mixed; boundary=batch-a73hdj3dy3mm347ddjjdf
-batch-a73hdj3dy3mm347ddjjdf
X-Batch-Status: 201 Created
Location: http://container.org/people/me/all/container.org:5ea3gh838kjk34834
ETag: "993hhhgjgkkd"
-batch-a73hdj3dy3mm347ddjjdf
X-Batch-Status: 304 Not Modified
ETag: "837dyfkdi39df"
All,
I'd like to propose a starting point for an OpenSocial RESTful API specification. The draft below is the result of a lot of individual discussions, a bunch of research and experiments, and, over the past couple of days, a whole lot of coffee. Having said that, I regard this as a way to spark discussion rather than something being put forward for approval. Please reply to the list with your opinions, reactions, and alternatives.
One suggestion: There are a lot of pieces to this (still under 10 pages though). If you start a response thread, it could be useful to edit the subject line to advertise what you're discussing -- the section name, for example. Otherwise discussions could easily be missed. ---
Proposal for an OpenSocial RESTful API (DRAFT)
The primary goal of this proposal is to serve as a common protocol understood by all OpenSocial clients and servers. If this draft is adopted, it would replace the previously proposed GData-based OpenSocial data API.
Overview
This API defines a language- and platform- neutral protocol for OpenSocial clients and container servers to interact outside of gadgets on a web page. As a protocol, it is intended to be reasonably easy to implement in any language and on any platform. It should also be usable across a range of clients, from gadgets operating within a web page to servers communicating to synchronize data about a user.
The protocol operates primarily in terms of resources and operations on them. It is defined on top of the HTTP protocol, and uses the standard HTTP methods (GET, POST, PUT, DELETE, etc.) to retrieve and change server state.
No single data representation is ideal for every client. This protocol defines dual representations for each resource in two widely supported representations, JSON [RFC4627] and Atom [RFC4287][RFC5023], using a set of generic mapping rules. The mapping rules allow a server to write to a single interface rather than implementing the protocol twice.
An Activity Example
Activity application/json:
{
'id' : 'http://example.org/activities/example.org:87ead8dead6beef/self/af3778',
'title' : { 'type' : 'html',
'value' : '<a href="foo">some activity</a>'
}
'updated' : '2008-02-20T23:35:37.266Z',
'content' : 'Some details for some activity',
'received' : '2008-02-20T23:35:37.266Z'}
Activity application/atom+xml:
<entry xmlns="http://www.w3.org/2005/Atom>Activity mapping rules:
<category term="status"/>
<id>http://example.org/activities/example.org:87ead8dead6beef/self/af3778</id>
<title type="html"><a href="foo">some activity</a></title>
<content>Some details for some activity</content>
<updated>2008-02-20T23:35:37.266Z</updated>
<link rel="self" type="application/atom+xml" href="http://www.google.com/activity/feeds/.../af3778"/>
<osa:received>2008-02-20T23:35:37.266Z</osa:received>
</entry>
atom:entry/content == content (type:text) Map entry/content to content field in JSON representation, and vice versa.
atom:entry/summary == summary (type:text) Same for summary.
atom:entry/id == id Map ids (representation is the same)
atom:entry/title == title (type:text) Map titles over; if not plain text, use a structured type/value object to represent data.
atom:entry/updated == updated S.O.P.
osa:received == received Map received field, stripping the namespace. In the JSON to Atom conversion, received always means osa:received.
An AppData Example
The first example is of a collection of key/value pairs for a particular application/user pair:
application/json representation:
application/atom+xml representation:{
'pokes' : 3,
'last_poke' : '2008-02-13T18:30:02Z'
}
<entry xmlns="http://www.w3.org/2005/Atom>AppData mapping rules:
<content type="text/xml">
<pokes>3</poke>
<last_poke>2008-02-13T18:30:02Z</last_poke>
</content>
<title/>
<updated>2003-12-13T18:30:02Z</updated>
<author><url>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</url></author>
<id>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</id>
</entry>
atom:entry/content == * Flatten entry/content into top level of JSON representation. In the inverse mapping, all JSON fields are collected into atom:entry/content.
This generally sounds pretty good to me. It looks like you're using
clever mapping rules to make the JSON representation look simple and
json-y, and not using it as a tacked-on second-class citizen, so nice
job having your cake and eating it too. :)
The things that worry me are the pieces that require special HTTP
mechanics to use--specifically the use of PUT/DELETE and the batch
syntax. IMO, these are a non-trivial barrier to adoption for many
developers, and in particular they remove one of the most useful and
productive features of simple REST APIs, namely that you can try URLs
in the browser and see the results immediately. Amazon and others have
talked about how this alone has lead to their REST APIs being used way
more than SOAP or any other formats that you can't just "play with" as
easily. I understand the elegance of this design, but I think
engineering for adoptability is even more important than engineering
for parsimony and elegance. For instance, would you consider at least
providing a way to specify the HTTP verb as a GET query var, e.g.
&action=DELETE&... so you could simulate the same effect in a web
browser?
And for the batching, would you consider a JSON
representation that could be sent up in normal post data, e.g.
"commands:" [ { /* command1 json */ }, { ... }, ... ] again so you can
just get URL+JSON and you're done, rather than also having to know how
to read and write custom HTTP headers, which you can't do when living
in a web browser or even a JavaScript gadget.
Finally, as for the URL-
patterns for the commands, I think it looks good but I hope the
container can at least specify a URL-prefix, as we used to be able to
do. IMO, if you can let containers pick their "base url", they can
easily emulate sub-patterns, but it's harder to ask every site to be
able to do domain.com/opensocial/... for instance, and I don't see any
reason why there couldn't be some very simple/standard way for the
container to specify an alternative root in the JavaScript or as a
meta-tag in the head of / (for server-side curling ala yadis) or
something.
> Data Representations
>is the ordering based on the way they are retrieved from the backend
>
> Each activity, person, and appdata is represented as a hierarchical tree
> of elements. Ordering of elements within a parent element may or may
> not be significant, depending on the context. Mapping consists of
where they are persisted?
Or, does the OpenSocial API server have to sort the data retrieved
from the backend based on certain query parameters? If so, what are
those query params?
> converting from the internal hierarchy to a JSON data structure or an
> application/atom+xml representation:The above 2 fields are NOT in JSON.
>
> <entry xmlns="http://www.w3.org/2005/Atom>
> <content type="text/xml">
> <name>
> <unstructured>Jane Doe</unstructured>
> <name>
> </content>
> <title/>
> <updated>2003-12-13T18:30:02Z</updated>
JSON output has less info than Atom/XML does.
This lossy conversion to JSON is not a cause for concern?
Atom allows id in URI format? how is it translated into JSON?
> <author/>
> <id>urn:guid:orkut.com:34KJDCSKJN2HHF0DW20394</id>
> </entry>
I only see conversion for "id" starting with "urn:guid:"
> Note: The atom:summary element is the appropriate place to put a text or
> In this example, we have chosen to flatten the "content" element by
> moving all its child elements to the top level. If there were a
> conflict, the rules could include a renaming for conflicting fields.
> This renaming scheme, as well as the choice to flatten "content", is
> driven by metadata defined above for the "Person" service.
>
> HTML representation of the structured data present in the content element;
> there should be a standard wayfor a client to request such a human readable
> representation, and servers may provide it if they decide to support it.
It says there "should" be a standard way. but what is it? do we need
to specify?
General question: Atom/XML defines pagination links Next/Prev.
In JSON, are those supposrted? if so, how do they look?
I've asked for it before:
http://groups.google.com/group/opensocial-and-gadgets-spec/msg/4ba84052a42c8d7d
-joe
--
Joe Gregorio http://bitworking.org
Subbu
Subbu
Subbu
Subbu
My question is related to the contents of the atom:content element,
i.e #1 above, for which it is better to leave things unordered (in the
lines of interleave in RNG). I agree with John's comments about
collections. It is better to clarify these two cases in the draft
proposal.
Subbu
Subbu