Home Index Changes Prefs Log in »

Proposal: Finalize Web Services APIs

Author:Dave Johnson
Target:October milestone

Introduction

Now that the OpenSocial REST API and the Shindig implementation is stabilizing, we can finalize the SocialSite REST API – which is OpenSocial plus our extensions that support friending, groups, enchanced search and gadget management. This proposal presents a way to do that.

Currently, our SocialSite extensions are implemented in an adhoc fashion: a simple Servlet that parses the incoming URL and uses a large and unweildy if-else statement to decide which POJOs to create, retrieve, update or delete and to return JSON data. Only JSON is supported, unlike OpenSocial APIs which support JSON, XML and Atom. On the client-side, the SocialSite JavaScript API is different from the OpenSocial web services in many ways and does not use the Shindig infrastructure for supporting batching of calls.

Benefits

By abandoning our adhoc implementation and instead implementing our web services via Shindig ~DataRequestHandlers on the server-side and using standard OpenSocial request facilities the server-side we get these benefits:

  • Support for OpenSocial style REST and JSON-RPC on the server-side
  • Support for JSON-RPC batching of requests on client-side
  • Comply with OpenSocial requirements for extensions

The rest of this document presents a design for our new web services in terms of URIs and HTTP actions/verbs POST, GET, PUT and DELETE as well as notes on how to implement these new web services using the framework provided by Shindig.

References

The proposal is based on these specifications and codebases:

The REST API handlers and URIs

On the server-side, each URI root such as '/people' or '/activities' is handled by a ~DataRequestHandler with methods for POST, GET, PUT and DELETE. These methods accept and return Java beans and Shindig specific collection classes that hold Java beans. Shindig takes care of converting POJOS to and from XML, Atom and JSON formats.

Note that, though we are talking about REST and URIs and HTTP verbs, these same handlers are used for JSON-RPC implementation and by implementing them we get support for both REST and JSON-RPC interaction -- all taken care of by Shinding.

This section lays out the SocialSite extensions to OpenSocial by explaining each of the handlers that we will implement or modify to add the URIs and actions we need.

PersonHandlerImpl

We will extend the Shindig PersonHandlerImpl, which supports the '/people' APIs discussed in the. We will add support for the new URIs and actions below.

POJO type: returns and accepts Profiles

	/people/@all [?search={searchString}]
		GET - get all people

	/people/{userId}/{groupId}
		GET - members of a group

	/people/{userId}/@union/{groupId1}/{groupId2}
		GET - union of members in two groups
To create a friend connection, you post a Profile representation of the person you wish to friend to your friends collection:
	/people/{yourUserId}/@friends
To get a list of people who have invited you to be a friend, do a get on:
	/people/{yourUserId}/@requests
To accept a friendship, post a Profile representation of the requesting person to your friend collection:
	/people/{yourUserId}/@friends
To ignore a friendship, do a delete on:
	/people/{yourUserId}/@requests/{userId}

GroupDefinitionHandler

POJO type: returns GroupDefinition

	/groupdef
		GET - get group definition	

GroupHandler

POJO type: Returns and accepts Groups

Types of groups:

  • Public: visible to all, anybody can ask to join
  • Closed: not visible to all, join by invitation only
  • Private: visible to only one user who can add/remove folks as needed

NOTE that we will implement only public groups for October milestone

	/groups/@public [?search={searchString}]
		GET - get list of all public groups defined in system
		POST - create a new public group

	/groups/@public/{groupId}
		GET - get public group
		PUT - update a public group that you are the admin of
		DELETE - delete a public group that you are the admin of

	/groups/{userId}
		GET - get list all groups (public, private and closed) associated with a user

	/groups/{userId}/@friends
		GET - get list of groups (public, private and closed) that friends belong to

GroupMemberHandler

POJO type: returns and accepts Profiles

Do this inside the people handler!

	/members/{groupId}
		GET - get list of members in group
		POST - add a new member to the group

	/members/{groupId}/{userId}
		PUT - update a member in group 
		DELETE - delete a member in group 
	
To ask to join a group, post a sufficient Profile representation of yourself to:
	/members/{groupId}
To get a list of incoming group membership requests:
	/members/@requests
To accept an incoming group request, post sufficient Group representation to:
	/members/{groupId}
To reject an incoming group request, do a delete on the request:
	/members/{groupId}/@requests/{userId}

MessageHandler

POJO type: Returns and accepts MessageContents

	/messages/{userId}/outbox
		POST - create a new message, recipient(s) should be specified in content
		GET - get sent messages for user [added by Bobby]

	/messages/{userId}/inbox
		GET - get messages for user

	/messages/{userId}/inbox/{messageId}
		GET - get individual message
		PUT - update message for user (i.e. mark it as read)
		DELETE - delete a message

SearchHandler

POJO type: Returns SearchResults

	/search/{searchString}
		GET - get combined search results

GadgetHandler

POJO type: Returns and accepts InstalledGadgets

	/gadgets/@all  [?search={searchString}]
		GET - all gadgets known to system, with optional search string

	/gadgets/@user/{userId} - [?destination={destinationId}]
		GET - all user's installed gadgets

	/gadgets/@user/{userId}
		POST - install a gadget for user (destination specified in content)

	/gadgets/@group/{groupId} - [?destination={destinationId}]
		GET - all group's installed gadgets

	/gadgets/@group/{groupId}
		POST - install a gadget for group (destination specified in content)

Implementing the SocialSite extensions

For each handler you implement:

On the server-side

Define a POJO to represent the data item handled

Define a simple POJO to represent that type and provide a way to serialize and de-serialize the POJO to XML and JSON. Shindig expects us to return POJO objects, which Shindig will automatically convert to either JSON or XML, depending on what was requested.

In some cases, we can use our existing POJOs for this -- but I expect that some of our POJOs expose more information than we want to return to the client. In those cases, we might have to develop simple wrapper POJOs that expose just the things we want.

Implement the DataRequestHandler interface

Implement the get(), post(), put() and delete() methods as needed. Define a URI parsing template that can be applied to incoming RequestItems, use the RequestItem to get data from the request in the form of POJOs. See our existing handler implementationsfor examples.

Add your handler to those in CustomHandlerProvider

Guice is used to inject handlers into the Shindig Servlet, so you will need to add your handlers to our CustomHandlerProvider.

On the client side

Define JavaScript object to match your POJO

Define a JavaScript object to match your Java POJO and code thatcan construct this from JSON data. See sectionprivacy.js for an example of how to do this.

Define JavaScript request methods to support your handler

Define JavaScript methods to create needed requests and process the responses. If you support full CRUD for your data type then you'll need four methods like so (see socialsite.js for examples):

  • socialsite.newCreateXXXRequest()
  • socialsite.newFetchXXXRequest()
  • socialsite.newUpdateXXXRequest()
  • socialsite.newDeleteXXXRequest()

Modify your Gadgets to use the new APIs

Modify your existing SocialSite Gadgets to use your new methods instead of the old deprecated JS APIs.

Clean up

Delete your old and no longer needed code from the SocialSiteGadgetDataServlet and socialsite.js.

Appendix A: Mapping the old REST API to the new

This section lists URI patterns from the old REST API and, in highlights, explains which parts of the new REST API to use instead.

Profiles

/profiles
	getOnProfilesUrl
	DONE

/profiles/{ownerId}
	getOnProfilesAndOwnerUrl
	putOnProfileUrl
	DONE

/allprofiles
	getOnAllProfilesUrl
	Use PersonHandlerImpl /people/@all instead

/profiles/{ownerId}/friendsgroups
	getOnFriendsGroupsUrl - returns - Collection of Groups POJOs in JSON form
	Use GroupHandler /groups/@friends insead
	
/profiles/{ownerId}/request/{friendId}
	putOnRequestFriendUrl
	use POST to /people/{userId}/@friends 

/profiles/{ownerId}/accept/{friendId}
	putOnAcceptFriendUrl
	use POST to /people/{userId}/@friends instead

/profiles/{ownerId}/reject/{friendId}
	See notes above 

Groups

/groups
	getOnGroupsUrl - returns - GroupDefinition in JSON form
	Use GroupDefinitionHandler /groupdef instead

/groups/{groupHandle}
	getOnGroupsAndGroupIdUrl - returns - Group POJO in JSON form
	putOnGroupUrl - updates - Group POJO from JSON data
 	Use GroupHandler and /groups/@public and /groups/@public/{groupId} instead

/groups/{groupHandle}/members
	getOnGroupMembersGroupsUrl - returns - Group POJOs 
	Use PersonHandlerImpl /people/{userId}/{groupId} instead

/groups/{groupHandle1}/{groupHandle2}
	getOnCommonGroupMembersUrl - Group POJOs
	Use PersonHandlerImpl /people/{userId}/@union/{groupId1}/{groupId2} instead

/groups/{groupHandle}/activities
	getOnGroupActivities - Activity POJOs
	Use standard OpenSocial /activities/{userId}/{groupId} instead

/groups/{groupHandle}/myfriends/{ownerId}
	getMyFriendsInGroupUrl - returns - Profile POJOs
	Use standard OpenSocial /people/{userId}/{groupId} instead

/groups/{groupHandle}/admin/{ownerId}
	getOnGroupAdminsUrl - returns - type value of MEMBER or ADMIN
 	Instead, use a field in the group to indicate user's standing

/currentgroup
	getOnCurrentGroupUrl - Group POJO for current group 
	Instead, passing groupId to gadget and get group by ID

/mygroups
	getOnMyGroupsUrl - returns - Group POJOs
	Use standard OpenSocial API instead /groups/{userId}

/mygroups/add/{ownerId}
	putOnAddRemoveGroupUrl
	Instead, use POST to /members/{groupId}

/mygroups/remove/{ownerId}
	putOnAddRemoveGroupUrl
	Instead, use DELETE on /members/{groupId}/{userId}

/allgroups
	getOnAllGroupsUrl
	Use /groups/@public instead

/groups/{groupHandle}/create/{ownerId}
	putOnCreateGroup
	Use POST to /groups/@public instead

/groups/{groupHandle}/invite/{ownerId}
	putOnInviteFriendToGroupUrl
	Use POST to /members/{groupId} instead

/groups/{groupHande}/{ownerId}/accept/{memberId}
	putOnAcceptGroupMemberUrl
	See notes above on joining groups

/groups/{groupHande}/{ownerId}/reject/{memberId}
	putOnRejectGroupMemberUrl
	See notes above on joining groups

Notifications

/notifications/{ownerId}
	getOnNotificationsAndOwnerUrl
	Use GET on /messages/{userId}/inbox instead

/notifications/id/{notficationId}
	getOnNotificationId
	Use GET on /message/{userId}/inbox/{messageId} instead

/notifications/person/{fromId}/{toId}/{subject}
	putOnMessageToPersonUrl
	Use POST to /messages/{userId}/outbox with person recipient in data instead

/notifications/group/{fromId}/{toId}/{subject}
	putOnMessageToGroupUrl
	Use POST to /messages/{userId}/outbox with group recipient in data instead

/notifications/id
	deleteOnNotificationUrl
	Use GET on /message/{userId}/inbox/{messageId} instead
/search/{searchString}
	getOnSearch
	Use SearchHandler instead with same URL

/search/profile/{searchString}
	getOnProfileSearch
	Use PersonHandler and  /people/@all [?search={searchString}] instead

/search/group/{searchString}
	getOnGroupSearch
	Use GroupHandler and  /groups/@public [?search={searchString}] instead

/search/gadget/{searchString}
	getOnGadgetSearch
	Use GadgetHandler and  /gadgets [?search={searchString}] instead

Gadgets

/installedgadgets/user/{ownerId}/{destination}
	putOnCreateInstalledGadgetForUser
	Use POST to /gadgets/@user/{userId} instead

/installedgadgets/group/{groupId}/{destination}
	putOnCreateInstalledGadgetForGroup
	Use POST /gadgets/@group/{groupId} instead

/installedgadgets/id
	deleteOnInstalledGadgetUrl
	Use DELETE on either /gadgets/@user/{userId}/{gadgetId} or the group equivalent

« Home Attachments Info Index Changes Prefs
This page (revision-16) was last changed on 24-Jul-09 17:16 PM, -0700 by snoopdave