1. Overview.This proposal defines an RPC alternative to the RESTful API specification.
The intent is to support the same data and operations as the RESTful
API in a form that is more natural for the JSON data format. Any batch
of RPCs should be programatically convertible to a sequence or batch of
RESTful calls and maintain the same semantics, any exception to this
will be called out explicitly. It shares the following with the RESTful spec:
It differs from the RESTful spec in the following ways:
This document presents all of its examples in the JSON format but the RPC protocol is equally applicable to an XML format. The data structure returned is equivalent in both formats; the only difference is in the encoding of the data. Singular fields are encoded as string key/value pairs in JSON and tags with text content in XML, e.g. "field": "value" and <field>value</field> respectively. Plural fields are encoded as arrays in JSON and repeated tags in XML, e.g. "fields": [ "value1", "value2" ] and<fields>value1</field><fields>value2</field> respectively. Nodes with multiple sub-nodes are represented as objects in JSON and tags with sub-tags in XML, e.g. "field": { "subfield1": "value1", "subfield2": "value2" } and<field><subfield1>value1</subfield1><subfield2>value2</subfield2></field> respectively. 2. Examples2.1 Single element request & response for the Person associated with principal in the OAuth token
This maps to fetching /people/@me/@self using the RESTful API. Notes:
POST /rpc HTTP/1.1 Host: api.example.org Authorization: <Auth token> Content-Type: application/json { "method" : "people.get", "id" : "myself"
"params" : { "userId" : "@me",
"groupId" : "@self" }
} HTTP/1.x 207 Multi-Status Content-Type: application/json { "id" : "myself"
"result" : { "id" : "example.org:34KJDCSKJN2HHF0DW20394", "name" : { "unstructured" : "Jane Doe"}, "gender" : "female" } } 2.2 Multi-element batch request & response for person associated with principal in the OAuth token and their friends
This maps to fetching /people/@me/@self & /people/@me/@friends using the RESTful API Notes:
POST /rpc HTTP/1.1
Host: api.example.org Authorization: <Auth token> Content-Type: application/json [ {
"method" : "people.get", "id" : "myself"
}, { "method" : "people.get", "id" : "myfriends"
"params: {
"groupId" : "@friends" }
} ] HTTP/1.x 207 Multi-Status Content-Type: application/json [ {
"id" : "myself",
"result" : { "id" : "example.org:34KJDCSKJN2HHF0DW20394", "name" : { "unstructured" : "Jane Doe"}, "gender" : "female" } }, { "id" : "myfriends"
"error" : { "code" : 401
}
} ] 2.3 Multi-element batch request & response with per-request auth-token override
This maps to fetching /people/@me/@self using the 1st auth-token and fetching /people/@me/@friends using the 2nd auth-token with RESTful API Notes:
POST /rpc HTTP/1.1
Host: api.example.org Authorization: <1st Auth token> Content-Type: application/json [ {
"method" : "people.get", "id" : "a9fd76"
}, { "method" : "people.get", "id" : "e453a",
"params: {
"groupId" : "@friends", "auth" : "<2nd Auth token>"
}
} ] HTTP/1.x 207 Multi-Status Content-Type: application/json [ {
"result" : { ... } }, { "result" : { ...
}
} ]
2.4 Request & response for collection of Person objects
This example maps to fetching /people/@me/@friends with the RESTful API Notes:
POST /rpc HTTP/1.1
Host: api.example.org Authorization: <auth token> Content-Type: application/json { "method" : "people.get",
"id" : "myfriends" "params: {
"userId" : "@me", "groupId" : "@friends" }
} HTTP/1.x 207 Multi-Status Content-Type: application/json { "result" : {
"totalResults" : 100, "startIndex" : 0, "itemsPerPage" : 10, "list" : [ {..}, {..}, ] }
}
2.5 Updating application app-data for a user
This example maps to PUTing or POSTing a set of key-values to /appdata/@me/@self/app12345 with the RESTful API. This is a fairly simple example of an update. Notes:
POST /rpc HTTP/1.1
Host: api.example.org Authorization: <auth token> Content-Type: application/json { "method" : "appdata.update",
"id" : "setMyData" "params: {
"appId" : "app12345", "data" : { "pokes" : 3, "lastPoke" : "2008-02-13T18:30:02Z" } }
} HTTP/1.x 207 Multi-Status Content-Type: application/json { "result" : {
} }
2.6 Creating a friend relationship to another user. This example maps to PUTing an opensocial.Person object /people/@me/@friends with the RESTful API. This example is a use-case not covered by the OpenSocial JS API and is given here to show how additional operations that a container may want to support can be defined within this framework. This effect of this call may be different based on how the container models the social graph. This may be interpreted as send an invite to the specified person to create a bi-drection friend relationship which may be accepted or rejected later. Depending on the semantics of the group the result of the call can vary and the person added may not be immediately visible in a subsequent person.get call on the same group. Notes:
POST /rpc HTTP/1.1
Host: api.example.org Authorization: <auth token> Content-Type: application/json { "method" : "people.create",
"id" : "createFriend" "params: {
"userId" : "@me", "groupId" : "@friends", "person" : { "id" : "example.org:FF256337" } }
} HTTP/1.x 207 Multi-Status Content-Type: application/json { "result" : {
} } 2.7 Failed attempt to update profile information due to conflicting change
This example maps to POSTing an opensocial.Person object /people/@me/@self with the RESTful API. This example again is not one covered by the OpenSocial JS but may be supported by containers. The example shown demonstrates the use of etags to detect an incompatible update to the profile of the user identified in the auth token. The request is attempting to update the 'books' field and passes the etag the client received when it retrieved the person object prior to update. The etag is added to the update request shown below but an inervening update from another client has made the change incompatible and so the update is rejected. Notes
POST /rpc HTTP/1.1 Host: api.example.org Authorization: <auth token> Content-Type: application/json { "method" : "person.update",
"id" : "setMyData" "params: { "userId" : "@me", "groupId" : "@self", "person" : { "etag" : "767ffdef7", "books" : ["The Prince", "Hotel New Hampshire"] } } } HTTP/1.x 207 Multi-Status
Content-Type: application/json {
"id" : "myself", "error" : { "code" : 409 "message" : "Conflict detected", "data" : { "etag" : "8543de12", "books" : ["The Right Stuff", "About a boy"] } } } A multipart form-data request mechanism ("Content-Type: multipart/form-data") enables upload of content. Such a request has the POST body segmented into several sections (fields) with each field identified by a name. The field name "request" is reserved and contains request operation details (same as the contents on the POST body in the vanilla POST operation). The fields with any other name contain content to be uploaded to the OpenSocial container. To allow batching and multiple file uploads in a single call, multiple fields (identified with unique field names) are allowed. The OpenSocial request can refer to the content being uploaded by setting the URL to "@field:<fieldname>". The container may host the uploaded file and replace the URL field with a fully qualified URL for future access. For a successful upload, the response code of the request will be associated with the response code of the action request on the resource. However, in case of a failure due to a file size exceeding limits, an error code of 413 ("Request Entity Too Large") MUST be returned. The requested action associated with the file upload MUST NOT be executed. Example: <usual headers> Content-type: multipart/form-data; boundary=------------abcdef012345xyZ Content-length: <contentLength> ------------abcdef012345xyZ Content-Disposition: form-data; name="request" [{ "method":"activities.create", "params": { "userId":["@viewer"], "groupId":"@self", "appId":"@app", "activity": { "title": "hello world!", "mediaItems": [ { "mimeType":"image", "url":"@field:image1" } ] } }, "id":"key" } ------------abcdef012345xyZ Content-Disposition: form-data; name="image1" GIF89....<image data> ------------abcdef012345xyZ 3. Naming ConventionsIn addition to conforming with the JSON-RPC specification for a container to be an OpenSocial RPC provider they MUST conform to the following conventions3.1 RPC Methods The value of the "method" field in an OpenSocial RPC request uses a format of "<service-name>.<operation>". <service-name> is a string [A-Za-z0-9_]+ that identifies one of the services listed in [Services] and <operation> is a string [A-Za-z0-9_]+ that identifies an operation supported by that service. All OpenSocial services MUST expose a "get" [TBD query, fetch are valid alternatives] operation.
Operations with the names get, create, update and delete should map to
the HTTP equivalent operations in the RESTful API of GET, PUT, POST
& DELETE 3.2 Services The names of the RPC services MUST match the names used in the OpenSocial namespace E.g http://ns.opensocial.org/messages/0.8 4. BatchesCalls to the RPC endpoint for batch execution MUST only be supported for HTTP POST requests. A batch of RPC's is an ordered array of requests and the return is an ordered array of responses in the same order. I.e
Many
service operations return a list of OpenSocial resources. Lists are
always returned in the "list" field of the result. Lists can either be
the full set of resources or a pageable subset. If the returned list
represents the entire set of resources the response is simply
{
"result" : {
"list" : [ ... ]
}
}
If
the operation supports random access indexing of the full list it will
support the "startIndex" and "count" parameters which control what
sublist of the full list is returned. If the response contains a
sublist obtained via random access indexing it will have the general
form:
{
"result" : {
"totalResults" : <total number of elements in the full list>,
"startIndex" : <0 based offset in the full-list of the returned sublist>,
"itemsPerPage" : <total number of elements in the returned sublist>,
"list" : [ ... the sublist ...]
}
}
If
the operation supports access through an indexable list of pages of the
full list it will support the "startPage" parameter that controls which
page is selected. A response that contains a sublist obtained via
indexed pages has the form:
{
"result" : {
"itemsPerPage" : <total number of elements in the returned sublist>,
"firstPage" : <index of the first page. This field is optional but recommended>,
"prevPage" : <index of the previous page. If this is the first page this field is omitted, otherwise it is required>,
"nextPage" : <index of the next page. If this is the last page this field is omitted, otherwise it is required>,
"lastPage" : <index of the last page. This field is optional but recommended>
"list" : [ ... the sublist ...]
}
}
The
paging mechanisms described here are based on the OpenSearch standard
with the additional requirement that all indexes are 0 based.
6. URL AddressingContainers MUST give access to the RPC services using a URL addressing scheme to invoke a single RPC via HTTP GET. This encoding can also be used to support RPC execution via form posts including file-uploads as parameters to RPCs. It is recommended that only services that are idempotent are exported in this manner. Containers MUST use the following canonical transformation between the RPC structure and its URL encoding
Some examples:
7. Request Authentication and Authorization ContextThis specification has the same requirements as the RESTful API for authentication and authorization. In addition Containers SHOULD also support a request context compatible with use by a gadget running the OpenSocial JS in the containers UI.When OAuth is used for authorization on a single RPC the OAuth signature uses the URL Addressing representation of the request without the auth param to calculate the signature base string. When OAuth is used for authorization on a batch of RPCs the generation of the OAuth signature base string uses the same technique asURL Addressing to convert a sub-request into a list of query parameters with the addition of adding the method field into the query parameter list. Each parameter in the query string is prepended with the id of the sub-request for which it was generated and '.' . The conversion for Example 2.2 would look like: a9fd76.method=people.get&a9fd76.id=a9fd76&a9fd76.params.userId=@me&e453a.method=people.get&e453a.id=e453a&e453a.params.userId=@me&e453a.params.groupId=@friends The remainder of the signing process is as per the OAuth spec i.e the query parameters are lexicographically sorted and a nonce and timestamp are added before the signing This allows for singing the full content of the request. In the case where a sub-request specifies its own Auth then that whole subrequest is omitted from the OAuth signature and a separte signature calculated for that sub-request 8. ServicesWe use a hybrid method call syntax to document the operation supported by a service. The types of parameters are described using the same syntax used by the OpenSocial JavaScript documentation. Most parameter defaults are literal values such as "@me" and some are derived from the calling context and have the following definitions:
8.1 People
service-name = "people" Operations summary:
8.1.1 get Retrieve a single person or list of opensocial.Person objects. Supports returning a limited set of fields on returned Person objects and control of the order and paging in a returned list. 8.1.2 create Support creating opensocial.Activity objects as the targets of a relationship with the specified user, this is a generalization of many use cases including invitation, contact creation etc. Implementation of this method is not required to be OpenSocial compliant 8.1.3 udpate Support updating the properties of an opensocial.Person object identified by its relationship to the specified user.Implementation of this method is not required to be OpenSocial compliant 8.1.4 delete Support removing the relationship between an opensocial.Person and the specified user. Implementation of this method is not required to be OpenSocial compliant 8.2 Activities service-name = "activity" Operations summary:
8.2.1 get Retrieve a one or list of opensocial.Activity objects. Supports returning a limited set of fields on returned Activity objects and control of the order and paging in a returned list. 8.2.2 create Support creating
opensocial.Activity objects as the targets of a relationship with the
specified user, the specifically supported use case is posting a new
activity to the stream of a single user i.e
activity.create(userId="@me",groupId="@self", <some activity>)
Support for other parameterizations is not required to be OpenSocial
compliant. 8.2.3 udpate Support updating opensocial.Activity objects as the targets of a relationship with the specified user. Implementation of this method is not required to be OpenSocial compliant 8.2.4 delete Support removing the relationship between an opensocial.Activity and the specified user. Implementation of this method is not required to be OpenSocial compliant 8.3 AppData
service-name = "appdata" Operations summary:
8.3.1 get Retrieve a map of key-value pairs for the list of specified keys. 8.3.2 udpate Add or replace key-value pairs stored in a users appdata with the key-vaues in the data parameter. 8.3.3 delete Remove the specifed keys from a users appdata and returned the values associated with those removed keys. 8.4 Messaging service-name = "messages" Operations summary:
Sample JSON for Message as one is not defined in the RESTful spec yet. {
"recipients" : ["example.org:AD38B3886625AAF", "example.org:997638BAA6F25AD"],
"title" : "You have an invitation from Joe",
"body" : "Some content",
"type" : "EMAIL"
}
8.3.1 send Send an opensocial.Message object to its defined recipients. 8.5 System The system service is used to introspect the endpoint for the set of available services and operations and for metadata about those services. service-name = "system" Operations summary:
8.6 Album service-name = "albums" Operations summary:
service-name = "mediaItems" Operations summary:
8.5.1 listMethods Containers MUST imlpement
this operation which takes no parameters and returns an array of all
methods supported by the endpoint including the system methods. For a
container which only supports read access to people & read and
create access for activities the result would be ["people.get", "activities.get", "activities.create", "system.listMethods", "system.methodSignatures", "system.methodHelp"] 8.5.2 methodSignatures Containers MUST implement
this operation that returns a method signature describing the types of
the parameters, their default values and the type of the return value
for a given operation. Note that this scheme does not match that used
for XML-RPC definition of method signatures which only specifies a
mapping for positional parameters. Type definitions used here match the
scheme used in the OpenSocial Javascript API. The example below is the
Signature response for people.get { "return" : ["opensocial.Person", "Array.<opensocial.Person>"], "auth" : { "default" : null, "type" : "AuthToken" }, "userId" : { "default" : "@me", "type" : ["String", "Array.<String>"] }, "groupId" : { "default" : "@self", "type" : "String" }, "fields" : { "default" : ["id","name","thumbnailUrl","profileUrl"], "type" : "Array.<String>" }, "count" : { "type" : "int", "required" : false }, "startIndex" : { "type" : "int", "required" : false } "startPage" : { "type" : "int", "required" : false } } The "return" field indicates the type of the result. If the service can return more than one type then the value is an array of the possible return types. Each named field in the response maps to a parameter of the operation and contains information about the types and values accepted for that parameter. The existence of "default" in the parameter detail indicates the parameter has a default value. The value of a default may be null which means that its not introspectable and usually means that it is some complex derivation. Parameters are assumed to be required unless otherwise indicated by the existence of the "required" field with a value of false. 8.5.3 methodHelp Containers MAY implement this operation that returns a textual description of the operation identified by the methodName parameter. A container can choose to return either plaintext or HTML as the response. 9. Standard ErrorsA common set of error codes are used to represent failure modes that apply generally to operations and so are reserved for use by this specification
10. Optimistic concurrencyContainers MAY choose to implement an optimistic concurrency scheme for updates. If this facility is supported by the container an "etag" field will be embedded in the updatable object when it is retrieved. When the container receives a request to update an object and an etag is present in the request the container MAY choose to deny the request because it has detected a conflicting update. Containers are free to allow updates even in the presence of conflicting etags at their own discretion but SHOULD document when such behavior is allowed.11. Alternative Response TypesIn situations where containers support the URL Addressing scheme to perform an RPC the container MAY return arbitrary content types in the response other than application/json. The system.methodHelp operation relies on this to provide browseable documentation.12. Multi-part UploadsA container MAY choose to support file uploads from browsers to the RPC endpoint and have the uploaded files be passed as parameters to the RPC. This is achieved by browsers posting a form which contains the files to the endpoint and including a form-parameter called "rpc" which contains the form-encoded RPC request. The form input element names for the uploaded files will be bound to the corresponding parameter names on execution. |