PROPOSAL - A JSON-RPC API for OpenSocial.

33 views
Skip to first unread message

Louis Ryan

unread,
Jul 17, 2008, 1:47:54 PM7/17/08
to opensocial-an...@googlegroups.com

Hi

I would like to propose the following to the OpenSocial community as an additional standard API that containers MAY support.  The genesis for the proposal is that the existing RESTful proposal is difficult to consume for developers that are primarily focused on JSON.   This proposal reduces the need for the different encoding strategies required to generate and parse RESTful requests. In particular the RESTful API requires parsing of URLs, Atom or JSON & Multi-part for batch. The proposal tries to use JSON as the one syntax for representing the full structure of individual and batch requests. This proposal leans heavily on the JSON-RPC specification which in turn was derived from XML-RPC.  The intent is to maintain the same capability as the RESTful proposal and any differences should be explicitly called out.

Some highlights:
  • Batch requests are a MUST in this specification because of the latency benefits it implies and the difficulty for developers if batching support varies by containers
  • Signing for full request bodies is supported
  • A simple URL addressing scheme is supported for convenience to facilitate browser-based testing of the API
I would particularly like to get feedback from gadget developers on this specification and their thoughts on whether they would prefer this style of API or the RESTful one.

The proposal is rough so if you have any questions on how you can work with it or parts seem unclear I will try to quickly turn-around clarifications.

Cheers,

-Louis Ryan


Proposal for an OpenSocial JSON-RPC API (Rough draft)

1. Overview.

This proposal defines a JSON-RPC 2.0 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 JSON-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:
  1. The JSON format of the OpenSocial data types for Person, Activity, Group & AppData are identical.
  2. HTTP status codes are used to represent common error cases where applicable
  3. OAuth is supported for authentication and authorization in addition to other authorization schemes a container may choose to support. A single OAuth token can be used for a batch of operations or each operation can specify its own OAuth token.
  4. Concurrency control is optional.  

It differs from the RESTful spec in the following ways:

  1. All structural elements in the protocol are represented using the JSON notation
  2. Batch support is required
  3. HTTP is used primarily as a transport and not to indicate the semantics of the request
  4. Automated discovery happens in two steps. XRDS can be used to discover the JSON-RPC endpoint. The service can then be introspected by executing an RPC for "system.listMethods" to discover the list of available services and operations using conventions defined by XML-RPC Introspection Within a running gadget the address of the JSON-RPC endpoint should be available as a Javascript variable (name and mechanism TBD). In general it is expected that a large percentage of consumers will bypass automated discovery and rely on simply hardcoding references to the endpoints of various popular containers.    

2. Examples


2.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:
  • The params groupid and userid are included here for completeness even though they are the default parameter values for the operation.
  • The "method" field identifies both the service "people" and the operation "get" which has the same semantics as an HTTP GET request in the RESTful spec. 
  • "json-rpc" as defined in JSON-RPC is not passed in the request but requests MUST be interpreted as if "json-rpc" : "2.0" was supplied
  • "id" is any identifier the caller may choose to associate with the call, this field is propagated to the response 
  • The "result" field in the response is the returned object, in this case an opensocial.Person object.

POST /jsonrpc 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 200 OK
Content-Type: application/json
{
   "id" : "myself"
   "result" : {
     "id" : "example.org:34KJDCSKJN2HHF0DW20394",
     "name" : { "unstructured" : "Jane Doe"},
     "gender" : {"displayvalue" : "女性", "key" : "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:
  • A batch request is detected by examining the request to see if it is a JSON array of requests. All request id's in a batch must be unique.
  • The request for "myself" succeeded but the one for "myfriends" failed because the OAuth token does not grant access to this resource. The failure of the "friends" request is indicated by the existence of an "error" object which contains a "code" field which uses the numerical value of the HTTP status code UNAUTHORIZED.  See JSON-RPC.Error 
  • The batch response elements are in the same order as the corresponding batch request elements but are returned in an associative array rather than an array to facilitate lookup. The "id" of the corresponding request in the batch request is the field which contains the result in the batch response.     
  • In Javascript this batch response structure allows for both ordered iteration of response elements using for each or for in loops as well as path style lookups like response.myself
  • In this example parameters are omitted where they match the defaults. If the call has no parameters the "params" field can be omitted completely.

POST /jsonrpc 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 200 OK
Content-Type: application/json
{
  "myself" : {
    "id" : "myself",
    "result" : {
      "id" : "example.org:34KJDCSKJN2HHF0DW20394",
      "name" : { "unstructured" : "Jane Doe"},
      "gender" : {"displayvalue" : "女性", "key" : "FEMALE" }
    }
  },
  "myfriends" : {
    "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:
  • The auth token in the header is used for requests which do not specify an "auth" parameter explicitly.

POST /jsonrpc 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 200 OK
Content-Type: application/json
{
  "a9fd76" : {
    "result" : {
      ...
    }
  },
  "e453a" : {
    "result" : {
      ...
    }
  }


2.4 Request & response for collection of Person objects

This example maps to fetching /people/@me/@friends with the RESTful API

Notes:
  • A response that contains a list of information will contain a "list" field in the result.
  • The result will also  include details about the number of elements returned in the "list" field as well as information about how to request additional elements using either paging or indexing. The example shown is for a list that is addressed by indexing.
  • Indices are always 0 based, so valid indices in the list are 0..totalResults-1

POST /jsonrpc 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 200 OK
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:
  • The userid and groupid params are omitted because they match the defaults
  • The appId param can be omitted if the auth-token used for the call can be used to derive the value for the appId.
  • The return value on success is VOID, represented by an empty JSON structure.

POST /jsonrpc 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 200 OK
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:
  • The return value on success is VOID, represented by an empty JSON structure. 
  • userid & groupid could be omitted as they match the operation default. Shown for completeness

POST /jsonrpc HTTP/1.1
Host: api.example.org
Authorization: <auth token>
Content-Type: application/json
{
    "method" : "person.create",
    "id" : "setMyData"
    "params: {
      "userid" : "@me",
      "groupid" : "@friends",
      "person" : {
        "id" : "example.org:FF256337"
      }
    }
}

HTTP/1.x 200 OK
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
  • The error code 409 is the HTTP Conflict status code
  • The error object "data" field contains a limited representation of the latest version of the person object including an up-to-date etag value. Containers may choose to omit this data from conflict error responses.

POST /jsonrpc 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 200 OK
Content-Type: application/json
{
   "id" : "myself",
   "error" : {
     "code" : 409
     "message" : "Conflict detected",
     "data" : {
       "etag" : "8543de12",
       "books" : ["The Right Stuff", "About a boy"]
     }
   }
}

3. Naming Conventions

In addition to conforming with the JSON-RPC specification for a container to be an OpenSocial JSON-RPC provider they MUST conform to the following conventions

3.1 JSON-RPC Methods


The value of the "method" field in an OpenSocial JSON-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 JSON-RPC services MUST match the names used in the OpenSocial namespace E.g http://ns.opensocial.org/messages/0.8


4. Batches

Calls to the JSON-RPC endpoint for batch execution MUST only be supported for HTTP POST requests.

A batch of OpenSocial JSON-RPC requests is of the general form: 

[ <json-rpc-request>, <json-rpc-request>, ...]

Each request MUST have an id that is unique among all the requests in the batch.

A batch of OpenSocial JSON-RPC responses is of the general form: 

{ "<request-id>" : <json-rpc-response>, "<request-id>" : <json-rpc-response>, ...}

The <request-id> is the value of the "id" field of the corresponding request in the batch-request.  Each response SHOULD occur in the output in the same order they occurred in the batch-request.


5. Lists

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 Addressing

Containers MAY give access to the JSON-RPC services using a URL addressing scheme to invoke a single RPC via HTTP GET. It is recommended that only services that are idempotent are exported in this manner. Containers MUST use the following canonical transformation between the JSON-RPC structure and its URL encoding


  • The service and operation are mapped to a subpath of the JSON-RPC endpoint. E.g /jsonrpc/people.get/
  • All other elements are encoded using <field path dot notation>=<scalar value> | <comma separated array of scalars>. When a field represents an array of non-scalars then elements of the array are identified by enclosing the index in parenthesis. Strings which match [0-9]+ MUST be single-quoted, Strings which match [A-Za-z_]+ MUST be unquoted, all other strings retain their original quoting.
    • { "field" : "value" } -> field=value
    • { "field" : [1,2,3,4,5]} -> field=1,2,3,4,5
    • { "field" : "12" } -> field='12'
    • { "field" : [identifier,anotheridentifier]} -> field=identifier,anotheridentifier
    • { "field" : ["value","another value"]} -> field=value,"another value"
    • { "field" : ['value','another value']} -> field=value,'another value'
    • {"field" : { "nested" : "value" }} -> field.nested=value
    • {"field" : [ { "nested1" : "value1" }, { "nested2" : "value2" }]} -> field(0).nested1=value1&field(1).nested2=value2


This scheme is intentionally verbose and is designed for simple representation of requests with a limited set of parameters. While more compact URL friendly encodings of JSON exist (e.g RISON ) this encoding seems more suitable for the intended use case.

Some examples:

7. Request Authentication and Authorization Context

This 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 JSON-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 JSON-RPCs the generation of the OAuth signature base string uses the same technique as URL 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. Services

We 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:

  • HttpRequest.Authorization - A parsed version of the authorization information stored in the HTTP Authorization header or that is associated with the request in a container specific manner
  • auth.AppId - The application id is derived from the authorization token if it is available.
  • User Ids - userid parameters to service operations MUST match the format described in http://code.google.com/apis/opensocial/docs/0.8/spec.html#people  or MUST be one of the reserved userid placeholders. All containers must support the @me placeholder value which is used to denote the user identified by the authorization token associated with the request. Any userid value that begins with '@' is reserved by the specification for future use. 
  • Group Ids - groupid parameters to service operations MAY be a simple string or one of the reserved groupid placeholders. All containers must support the @self & @friends placeholders values. Any groupid value that begins with '@' is reserved by the specification for future use as a placeholder. 


8.1 People

service-name = "people"

Operations summary:
Operation
Parameters
Return
get
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self", Array.<String> fields, int count,  int startIndex,  int startPageopensocial.Person or Array.<opensocial.Person>
createAuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@friends", opensocial.Person person
opensocial.Person
update AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self", opensocial.Person person opensocial.Person
delete
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self"Void

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:
Operation
Parameters
Return
get
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  Array.<String> fields,  int count,  int startIndex,  int startPageopensocoal.Activity or Array.<opensocial.Activity>
create
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  opensocial.Activity activityopensocial.Activity
update
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  opensocial.Activity activity How do you handle concurrency issues? -Joseph Gregorio 7/9/08 10:14 PM TODO - Look at ATOM collections with Etags -Louis Ryan 7/10/08 4:22 PM  opensocial.Activity
delete
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  String activityIdVoid


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 userImplementation 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:
Operation
Parameters
Return
get
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  String appId = auth.AppId, Array.<String> keys
Map.<String, String>
update
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  String appId = auth.AppId, Map.<String, String> data
Void
delete
AuthToken auth = HttpRequest.Authorization,  String userid = "@me", String groupid = "@self",  String appId = auth.AppId, Array.<String> keysMap.<String, String>

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:
Operation
Parameters
Return
sendAuthToken auth = HttpRequest.Authorization,  String userid = "@me", opensocial.Message message Void

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:
Operation
Parameters
Return
listMethods
Array.<String>
methodSignatures
String methodName
Signature
methodHelp
String methodName
Content


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"

     },

     "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 Errors

A 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

Code
Meaning
-32700 (Parse error)
 Invalid JSON. An error occurred on the server while parsing the JSON text.
-32600 (Invalid Request)
The received JSON not a valid JSON-RPC or batch of JSON-RPCs
-32601 (Method not found)The requested remote-procedure does not exist / is not available.
-32602 (Invalid params)
Invalid method parameters.
-32603 (Internal server error)
Internal server error
-32099..-32000 Reserved for implementation-defined server-errors.
401 (Unauthorized)
Access token does not grant access to the resource
404 (Not Found)
The requested resource was not found
409 (Conflict)
The requested update conflicts with a prior change
0-1000
Reserved for future use by this specification
 


10. Optimistic concurrency

Containers 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 Types

In 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 Uploads

A container MAY choose to support file uploads from browsers to the JSON-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 JSON-RPC request. The form input element names for the uploaded files will be bound to the corresponding parameter names on execution.







    
     




Louis Ryan

unread,
Jul 17, 2008, 2:52:54 PM7/17/08
to opensocial-an...@googlegroups.com
Heres a link to the doc

http://docs.google.com/View?docid=dhjrqr8t_4cwzqq7gh

2008/7/17 Louis Ryan <lr...@google.com>:

Subbu Allamaraju

unread,
Jul 18, 2008, 3:08:59 PM7/18/08
to opensocial-an...@googlegroups.com

On Jul 17, 2008, at 10:47 AM, Louis Ryan wrote:

> The genesis for the proposal is that the existing RESTful proposal
> is difficult to consume for developers that are primarily focused on
> JSON. This proposal reduces the need for the different encoding
> strategies required to generate and parse RESTful requests. In
> particular the RESTful API requires parsing of URLs, Atom or JSON &
> Multi-part for batch. The proposal tries to use JSON as the one
> syntax for representing the full structure of individual and batch
> requests.

Could you elaborate further? If there is such complexity, may the
RESTful API need fixing.

Subbu

Chris Chabot

unread,
Jul 18, 2008, 3:42:26 PM7/18/08
to opensocial-an...@googlegroups.com

I think the assumption of this spec is that there are some problems
with the complexity of the current RESTful proposal, but that it is
easier to add, then to take away from a spec that's already published.


Louis Ryan

unread,
Jul 18, 2008, 3:49:48 PM7/18/08
to opensocial-an...@googlegroups.com
I think there are a number of issues:

1. To interpret the contents of a RESTful JSON batch response a client must parse HTTP multi-part, then parse URLs and then parse JSON This is a VERY BIG burden on app developers and IMHO a barrier to adoption. To create a batch request they must be able to construct these forms. Any protocol which requires 3 parsing steps to understand the semantics of a message incurs a cost in transparency.
2.  Batching is optional in the RESTful spec which means that app developers will have to write different code based on whether a container implements batch support or not.
3. The expectation that client libraries will exist to shield application developers from 1&2 and that even if they did exist that they would be universally used is unfounded and unrealistic. Most application developers will roll their own as containers will release the platform before the libraries are available and because it will have to fit within their existing infrastructure
4.  Containers will want app developers to use batch for performance reasons and so many of the implied client-side caching benefits in a RESTful API are lost for browser clients and platforms that dont have good multi-part handling.
5. Even if 4 were not an issue techincally most app deveoper backends that talk to the RESTful API will store data by use-case in SQL databases rather than implementing large HTTP caches and would prefer to rely on the throughput and low-latency of the container to keep their implementations simple and avoid complex cache management logic.

I hope that reasonably summarizes things.

Cheers

-Louis

Subbu Allamaraju

unread,
Jul 18, 2008, 4:06:57 PM7/18/08
to opensocial-an...@googlegroups.com

On Jul 18, 2008, at 12:49 PM, Louis Ryan wrote:

> I think there are a number of issues:
>
> 1. To interpret the contents of a RESTful JSON batch response a
> client must parse HTTP multi-part, then parse URLs and then parse
> JSON This is a VERY BIG burden on app developers and IMHO a barrier
> to adoption. To create a batch request they must be able to
> construct these forms. Any protocol which requires 3 parsing steps
> to understand the semantics of a message incurs a cost in
> transparency.

I did bring this up with John several months ago. But the assumption,
I was told, was that the this complexity will be hidden underneath
some JS APIs. If that is not the case, the batch proposal needs to be
re re-evaluated. Batching is a pipe-dream and be best solved by
creating compound/aggregate resources, as Roy Fielding recently
commented

"Just define a resource that maps to all of the resources you intend
to operate upon. Resources often overlap."

If batch is the only painful aspect, then I don't think it justifies
an RPC-over-HTTP API (which is intrinsically bad).

Subbu

Louis Ryan

unread,
Jul 18, 2008, 4:29:10 PM7/18/08
to opensocial-an...@googlegroups.com
Some thoughts inline below...

On Fri, Jul 18, 2008 at 1:06 PM, Subbu Allamaraju <su...@subbu.org> wrote:


On Jul 18, 2008, at 12:49 PM, Louis Ryan wrote:

> I think there are a number of issues:
>
> 1. To interpret the contents of a RESTful JSON batch response a
> client must parse HTTP multi-part, then parse URLs and then parse
> JSON This is a VERY BIG burden on app developers and IMHO a barrier
> to adoption. To create a batch request they must be able to
> construct these forms. Any protocol which requires 3 parsing steps
> to understand the semantics of a message incurs a cost in
> transparency.

I did bring this up with John several months ago. But the assumption,
I was told, was that the this complexity will be hidden underneath
some JS APIs. If that is not the case, the batch proposal needs to be
re re-evaluated. Batching is a pipe-dream and be best solved by
creating compound/aggregate resources, as Roy Fielding recently
commented 


"Just define a resource that maps to all of the resources you intend
to operate upon. Resources often overlap."

This is an argument that will have to be made concretely in the context of OpenSocial to be consumable. Attempts to create aggregation services over sets of atomic services in a coherent and composable fashion are not for the faint of heart. This work needs to be composable both on the API implementor side within existing frameworks such as Shindig and from the consumer side without the aid of a library. There are certainly examples of batch APIs in the wild that work (SQL & Facbook spring to mind).


If batch is the only painful aspect, then I don't think it justifies
an RPC-over-HTTP API (which is intrinsically bad).

That seems a little strong? There are plenty of RPC based APIs that work well, and HTTP can be a reliable-enough transport otherwise people wouldnt build services on it. If my concerns about consumability, transparency and common use-cases can be addressed by changes to the RESTful API I would be all for it.
 


Subbu




Subbu Allamaraju

unread,
Jul 18, 2008, 4:33:57 PM7/18/08
to opensocial-an...@googlegroups.com

On Jul 18, 2008, at 1:29 PM, Louis Ryan wrote:

> This is an argument that will have to be made concretely in the
> context of OpenSocial to be consumable. Attempts to create

I agree, and that would reduce the complexity with the generic batch
mechanism as proposed.

Subbu

Louis Ryan

unread,
Jul 18, 2008, 4:37:09 PM7/18/08
to opensocial-an...@googlegroups.com
Can you write this up with some examples? Feel free to contact me directly to discuss if that would help

John Panzer

unread,
Jul 20, 2008, 5:47:17 PM7/20/08
to opensocial-an...@googlegroups.com
I would really like to get feedback from implementors (particularly gadget servers that would use the REST API, and gadget authors who would like to use the REST API from JS) to see what they think on this topic.

At the moment I'm -1 on this proposal, for the following reasons:

1. It's a major departure from the existing framework, which is a negative simply due to time constraints.
2. It's not clear to me that it adds any _compelling_ advantages, except for simplifying batching, which I agree is compelling.
3. It's not necessary to switch from REST to RPC to get simple batch semantics; I think there's a smaller, simpler proposal which will address the need, be easily translated to AtomPub, and would avoid the need for a completely (conceptually) separate API.  And if I can find the time I'll try to post it.

(All of that said, if everyone involved with writing servers and clients heard all of the pros and cons and then said "no, we really do want to do RPC over HTTP instead of REST over HTTP" then I'd work to try to make this the best darn JSON RPC API ever.)

John Panzer (http://abstractioneer.org)

2008/7/17 Louis Ryan <lr...@google.com>:

Krishna Sankar (ksankar)

unread,
Jul 20, 2008, 6:29:04 PM7/20/08
to opensocial-an...@googlegroups.com

I agree with John on this. Batching shouldn’t be THE argument for RPC. For some reason, I cannot envision Open Social with RPC. Again, in the name of popular demand, I can also be persuaded ;o)

 

Cheers

<k/>

P.S : Am in Portland this week OSCON/XMPP summit. Didn’t see an OpenSocial presentation.

 

From: opensocial-an...@googlegroups.com [mailto:opensocial-an...@googlegroups.com] On Behalf Of John Panzer
Sent: Sunday, July 20, 2008 2:47 PM
To: opensocial-an...@googlegroups.com
Subject: Re: PROPOSAL - A JSON-RPC API for OpenSocial.

 

I would really like to get feedback from implementers (particularly gadget servers that would use the REST API, and gadget authors who would like to use the REST API from JS) to see what they think on this topic.

Louis Ryan

unread,
Jul 21, 2008, 12:25:55 PM7/21/08
to opensocial-an...@googlegroups.com
John,

If the alternative you're referring to in 3. is the one currently used for the JS in Shindig I'll happily post it, if its something else I REALLY think you need to post some examples just so folks know what theyre voting for when/if we get around to that.

-Louis

2008/7/20 John Panzer <jpa...@google.com>:

John Panzer

unread,
Jul 21, 2008, 3:20:47 PM7/21/08
to opensocial-an...@googlegroups.com

John Panzer (http://abstractioneer.org)

2008/7/21 Louis Ryan <lr...@google.com>:

John,

If the alternative you're referring to in 3. is the one currently used for the JS in Shindig I'll happily post it, if its something else I REALLY think you need to post some examples just so folks know what theyre voting for when/if we get around to that.

It's not, and I am :).
 

Robert Evans

unread,
Jul 29, 2008, 1:47:31 PM7/29/08
to opensocial-an...@googlegroups.com
I am not sure if this is the right thread to +1 the JSON-RPC proposal,
but +1, and please direct me to the right thread otherwise.

I do have a couple of questions about this proposal. Small things.

Section 2.1 - Should the json-rpc version be included explicitly in
the request so as to future-proof it?

Section 2.2 - In the example response, it says:
{
"myself" : {
"id":"myself",
"result" : {

...
}
},
"myfriends" : {
"id" : "myfriends",
"result" : {
...
}
}
}

It seems like marking each entry with a separate container keyed by
the request id is redundant. Why not just have a collection of tuples,
where the first field in each tuple is the id, that contains the
request id field passed in the request?

Section 7 - Question 1 - Is the use of URL addressing optional for the
single request, i.e., could it also use the batch style?
Comment on subrequests - In the case where a subrequest defines its
own OAuth, the whole subsigning process seems a bit cumbersome. Not
sure if there is a better way.

Otherwise, it looks good.

Bob

2008/7/17 Louis Ryan <lr...@google.com>:

> createAuthToken auth = HttpRequest.Authorization, String userid = "@me",

> startPageopensocoal.Activity or Array.<opensocial.Activity>

> keysMap.<String, String>


>
> 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:
> Operation
> Parameters
> Return

> sendAuthToken auth = HttpRequest.Authorization, String userid = "@me",

> -32601 (Method not found)The requested remote-procedure does not exist / is

Louis Ryan

unread,
Jul 29, 2008, 4:25:14 PM7/29/08
to opensocial-an...@googlegroups.com


2008/7/29 Robert Evans <bobe...@google.com>


I am not sure if this is the right thread to +1 the JSON-RPC proposal,
but +1, and please direct me to the right thread otherwise.

I do have a couple of questions about this proposal. Small things.

Section 2.1 - Should the json-rpc version be included explicitly in
the request so as to future-proof it?

I think we can go with a policy of letting the container interpret it as 'current' if one isnt specified. The version of the json-rpc protocal is VERY unlikely to change. What would be more interesting is the version of the OpenSocial API which will probably be represented in the endpoint itself.




Section 2.2 - In the example response, it says:
{
 "myself" : {
     "id":"myself",
      "result" : {
         ...
      }
  },
  "myfriends" : {
     "id" : "myfriends",
     "result" : {
         ...
     }
  }
}

It seems like marking each entry with a separate container keyed by
the request id is redundant. Why not just have a collection of tuples,
where the first field in each tuple is the id, that contains the
request id field passed in the request?

This could certainly be converted into an array instead of an associative array without much difficult, however a collection of tuples seems less useful when consuming. E.g.

[ {id:x, response:{}}, ...] is reasonable I think but you dont get to use the required unique id to access the value in the array. In the associative variant you can do response.<id>.result.
 


Section 7 - Question 1 - Is the use of URL addressing optional for the
single request, i.e., could it also use the batch style?
Comment on subrequests - In the case where a subrequest defines its
own OAuth, the whole subsigning process seems a bit cumbersome. Not
sure if there is a better way.

I agree sub-signing bodies is probably overkill when there is trusted communication, just added for completeness. What actually gets done in practice should be more succinct. Suggestions very welcome here.
 

Robert Evans

unread,
Jul 29, 2008, 4:57:00 PM7/29/08
to opensocial-an...@googlegroups.com
2008/7/29 Louis Ryan <lr...@google.com>:

>
>
> 2008/7/29 Robert Evans <bobe...@google.com>
>>
>> I am not sure if this is the right thread to +1 the JSON-RPC proposal,
>> but +1, and please direct me to the right thread otherwise.
>>
>> I do have a couple of questions about this proposal. Small things.
>>
>> Section 2.1 - Should the json-rpc version be included explicitly in
>> the request so as to future-proof it?
>
> I think we can go with a policy of letting the container interpret it as
> 'current' if one isnt specified. The version of the json-rpc protocal is
> VERY unlikely to change. What would be more interesting is the version of
> the OpenSocial API which will probably be represented in the endpoint
> itself.

Good point, I think having an initial request send of the opensocial
version from the client would be a great idea.

>
>
>>
>>
>> Section 2.2 - In the example response, it says:
>> {
>> "myself" : {
>> "id":"myself",
>> "result" : {
>> ...
>> }
>> },
>> "myfriends" : {
>> "id" : "myfriends",
>> "result" : {
>> ...
>> }
>> }
>> }
>>
>> It seems like marking each entry with a separate container keyed by
>> the request id is redundant. Why not just have a collection of tuples,
>> where the first field in each tuple is the id, that contains the
>> request id field passed in the request?
>
> This could certainly be converted into an array instead of an associative
> array without much difficult, however a collection of tuples seems less
> useful when consuming. E.g.
>
> [ {id:x, response:{}}, ...] is reasonable I think but you dont get to use
> the required unique id to access the value in the array. In the associative
> variant you can do response.<id>.result.
>

Another good point, I guess you could get rid of the nested property
for id, but then that would be more work to deserialize properly. OK,
seems fine :-)

Reply all
Reply to author
Forward
0 new messages