Author: chabotc
Date: Wed Jun 25 04:21:42 2008
New Revision: 671511
URL: http://svn.apache.org/viewvc?rev=671511&view=rev
Log:
SHINDIG-404 Support Batching in RESTful interface (in json format)
Modified:
incubator/shindig/trunk/php/src/socialrest/ActivityHandler.php
incubator/shindig/trunk/php/src/socialrest/AppDataHandler.php
incubator/shindig/trunk/php/src/socialrest/DataRequestHandler.php
incubator/shindig/trunk/php/src/socialrest/PersonHandler.php
incubator/shindig/trunk/php/src/socialrest/http/RestServlet.php
Modified: incubator/shindig/trunk/php/src/socialrest/ActivityHandler.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/ActivityHandler.php?rev=671511&r1=671510&r2=671511&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/socialrest/ActivityHandler.php (original)
+++ incubator/shindig/trunk/php/src/socialrest/ActivityHandler.php Wed Jun 25
04:21:42 2008
@@ -19,6 +19,8 @@
class ActivityHandler extends DataRequestHandler {
private $service;
+
+ private static $ACTIVITY_ID_PATH =
"/activities/{userId}/{groupId}/{activityId}";
public function __construct()
{
@@ -26,7 +28,7 @@
$this->service = new $service();
}
- public function handleDelete($params, $token)
+ public function handleDelete(RestRequestItem $requestItem)
{
return new ResponseItem(BAD_REQUEST, "You can't delete
activities. ", null);
}
@@ -39,20 +41,17 @@
* /activities/john.doe/@self
* /activities/john.doe/@friends
*/
- public function handleGet($params, $token)
+ public function handleGet(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- $optionalActivityId = null;
- if (count($params) > 3) {
- $optionalActivityId = $params[3];
- }
+ $requestItem->parseUrlWithTemplate(self::$ACTIVITY_ID_PATH);
+ $parameters = $requestItem->getParameters();
+ $optionalActivityId = in_array('activityId', $parameters) ?
$parameters['activityId'] : null;
// TODO: Filter by fields
// TODO: do we need to add pagination and sorting support?
if ($optionalActivityId != null) {
- return $this->service->getActivity($userId, $groupId,
$optionalActivityId, $token);
+ return
$this->service->getActivity($requestItem->getUser(), $requestItem->getGroup(),
$optionalActivityId, $requestItem->getToken());
}
- return $this->service->getActivities($userId, $groupId, $token);
+ return $this->service->getActivities($requestItem->getUser(),
$requestItem->getGroup(), $requestItem->getToken());
}
/**
@@ -62,20 +61,10 @@
* /activities/john.doe/@self
* - postBody is an activity object
*/
- public function handlePost($params, $token)
+ public function handlePost(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- // TODO: Should we pass the groupId through to the service?
- if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
- throw new Exception("Empty raw post data");
- }
- $jsonActivity = $GLOBALS['HTTP_RAW_POST_DATA'];
- if (get_magic_quotes_gpc()) {
- $jsonActivity = stripslashes($jsonActivity);
- }
- $activity = $this->convertToObject($jsonActivity);
- return $this->service->createActivity($userId, $activity, $token);
+ //TODO: do we need to add groups here?
+ return $this->service->createActivity($requestItem->getUser(),
$requestItem->getPostData(), $requestItem->getToken());
}
/**
@@ -85,8 +74,8 @@
* /activities/john.doe/@self
* - postBody is an activity object
*/
- public function handlePut($params, $token)
+ public function handlePut(RestRequestItem $requestItem)
{
- return $this->handlePost($token);
+ return $this->handlePost($requestItem);
}
-}
\ No newline at end of file
+}
Modified: incubator/shindig/trunk/php/src/socialrest/AppDataHandler.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/AppDataHandler.php?rev=671511&r1=671510&r2=671511&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/socialrest/AppDataHandler.php (original)
+++ incubator/shindig/trunk/php/src/socialrest/AppDataHandler.php Wed Jun 25
04:21:42 2008
@@ -1,4 +1,5 @@
<?php
+
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -18,6 +19,7 @@
*/
class AppDataHandler extends DataRequestHandler {
+ private static $APP_DATA_PATH = "/people/{userId}/{groupId}/{appId}";
private $service;
public function __construct()
@@ -38,13 +40,10 @@
* be pulled from the values and set on the person object. If there are
no
* fields vars then all of the data will be overridden.
*/
- public function handleDelete($params, $token)
+ public function handleDelete(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- $appId = $this->getAppId($params[3], $token);
- $fields = isset($_GET['fields']) ? explode(',',
$_GET['fields']) : null;
- return $this->service->deletePersonData($userId, $groupId,
$fields, $appId, $token);
+ $requestItem->parseUrlWithTemplate(self::$APP_DATA_PATH);
+ return
$this->service->deletePersonData($requestItem->getUser(),
$requestItem->getGroup(), $requestItem->getFields(), $requestItem->getAppId(),
$requestItem->getToken());
}
/**
@@ -55,13 +54,10 @@
* /appdata/john.doe/@friends/app?fields=count
* /appdata/john.doe/@self/app
*/
- public function handleGet($params, $token)
+ public function handleGet(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- $appId = $this->getAppId($params[3], $token);
- $fields = isset($_GET['fields']) ? explode(',',
$_GET['fields']) : null;
- return $this->service->getPersonData($userId, $groupId,
$fields, $appId, $token);
+ $requestItem->parseUrlWithTemplate(self::$APP_DATA_PATH);
+ return $this->service->getPersonData($requestItem->getUser(),
$requestItem->getGroup(), $requestItem->getFields(), $requestItem->getAppId(),
$requestItem->getToken());
}
/**
@@ -76,21 +72,10 @@
* be pulled from the values and set on the person object. If there are
no
* fields vars then all of the data will be overridden.
*/
- public function handlePost($params, $token)
+ public function handlePost(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- $appId = $this->getAppId($params[3], $token);
- $fields = isset($_GET['fields']) ? explode(',',
$_GET['fields']) : null;
- if (!isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
- throw new Exception("Empty raw post data");
- }
- $jsonActivity = $GLOBALS['HTTP_RAW_POST_DATA'];
- if (get_magic_quotes_gpc()) {
- $jsonActivity = stripslashes($jsonActivity);
- }
- $values = $this->convertToObject($jsonActivity);
- return $this->service->updatePersonData($userId, $groupId,
$fields, $values, $appId, $token);
+ $requestItem->parseUrlWithTemplate(self::$APP_DATA_PATH);
+ return
$this->service->updatePersonData($requestItem->getUser(),
$requestItem->getGroup(), $requestItem->getFields(),
$requestItem->getPostData(), $requestItem->getAppId(),
$requestItem->getToken());
}
/**
@@ -105,8 +90,8 @@
* be pulled from the values and set on the person object. If there are
no
* fields vars then all of the data will be overridden.
*/
- public function handlePut($params, $token)
+ public function handlePut(RestRequestItem $requestItem)
{
- return $this->handlePost($params, $token);
+ return $this->handlePost($requestItem);
}
}
\ No newline at end of file
Modified: incubator/shindig/trunk/php/src/socialrest/DataRequestHandler.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/DataRequestHandler.php?rev=671511&r1=671510&r2=671511&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/socialrest/DataRequestHandler.php (original)
+++ incubator/shindig/trunk/php/src/socialrest/DataRequestHandler.php Wed Jun
25 04:21:42 2008
@@ -18,32 +18,32 @@
*/
abstract class DataRequestHandler {
-
- public function handleMethod($method, $params, $token)
+
+ public function handleMethod(RestRequestItem $requestItem)
{
- if ($method == 'POST') {
- $response = $this->handlePost($params, $token);
- } elseif ($method == 'GET') {
- $response = $this->handleGet($params, $token);
- } elseif ($method == 'DELETE') {
- $response = $this->handleDelete($params, $token);
- } elseif ($method == 'PUT') {
- $response = $this->handlePut($params, $token);
+ if ($requestItem->getMethod() == 'POST') {
+ $response = $this->handlePost($requestItem);
+ } elseif ($requestItem->getMethod() == 'GET') {
+ $response = $this->handleGet($requestItem);
+ } elseif ($requestItem->getMethod() == 'DELETE') {
+ $response = $this->handleDelete($requestItem);
+ } elseif ($requestItem->getMethod() == 'PUT') {
+ $response = $this->handlePut($requestItem);
} else {
$response = new ResponseItem(BAD_REQUEST, "Unserviced
Http method type", null);
}
return $response;
}
-
+
static public function getAppId($appId, SecurityToken $token)
{
- if ($appId == '@app') {
+ if ($appId == '@app') {
return $token->getAppId();
} else {
return $appId;
}
}
-
+
static public function convertToObject($string)
{
//TODO should detect if it's atom/xml or json here really.
assuming json for now
@@ -54,11 +54,8 @@
return $decoded;
}
- abstract public function handleDelete($params, $token);
-
- abstract public function handleGet($params, $token);
-
- abstract public function handlePost($params, $token);
-
- abstract public function handlePut($params, $token);
-}
\ No newline at end of file
+ abstract public function handleDelete(RestRequestItem $requestItem);
+ abstract public function handleGet(RestRequestItem $requestItem);
+ abstract public function handlePost(RestRequestItem $requestItem);
+ abstract public function handlePut(RestRequestItem $requestItem);
+}
Modified: incubator/shindig/trunk/php/src/socialrest/PersonHandler.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/PersonHandler.php?rev=671511&r1=671510&r2=671511&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/socialrest/PersonHandler.php (original)
+++ incubator/shindig/trunk/php/src/socialrest/PersonHandler.php Wed Jun 25
04:21:42 2008
@@ -19,6 +19,8 @@
class PersonHandler extends DataRequestHandler {
private $service;
+ private static $PEOPLE_PATH = "/people/{userId}/{groupId}/{personId}";
+ protected static $DEFAULT_PERSON_FIELDS = array("id", "name",
"thumbnailUrl");
public function __construct()
{
@@ -34,38 +36,31 @@
* /people/john.doe/@friends
* /people/john.doe/@self
*/
- public function handleGet($params, $token)
+ public function handleGet(RestRequestItem $requestItem)
{
- $userId = UserId::fromJson($params[1]);
- $groupId = GroupId::fromJson($params[2]);
- $optionalPersonId = false;
- if (count($params) > 3) {
- $optionalPersonId = $params[3];
- }
- $fields = !empty($_GET['fields']) ? explode(',',
$_GET['fields']) : null;
- if ($optionalPersonId || $groupId->getType() == 'self') {
+ $requestItem->parseUrlWithTemplate(self::$PEOPLE_PATH);
+ $parameters = $requestItem->getParameters();
+ $optionalPersonId = in_array('personId', $parameters) ?
$parameters['personId'] : null;
+ $fields =
$requestItem->getFieldsWithDefaultValue(self::$DEFAULT_PERSON_FIELDS);
+ if ($optionalPersonId || $requestItem->getGroup()->getType() ==
'self') {
//FIXME same logic as the java code here, but doesn't
seem to do much with the optionalPersonId which seems odd
- return $this->service->getPerson($userId, $groupId,
$fields, $token);
+ return
$this->service->getPerson($requestItem->getUser(), $requestItem->getGroup(),
$fields, $requestItem->getToken());
}
- $sort = !empty($_GET['orderBy']) && in_array($_GET['orderBy'],
PeopleService::$sortOrder) ? $_GET['orderBy'] : '';
- $filter = !empty($_GET['filterBy']) &&
in_array($_GET['filterBy'], PeopleService::$filterType) ? $_GET['filterBy'] :
'';
- $first = !empty($_GET['startIndex']) &&
is_numeric($_GET['startIndex']) ? $_GET['startIndex'] : 0;
- $max = !empty($_GET['count']) && is_numeric($_GET['count']) ?
$_GET['count'] : 0;
- return $this->service->getPeople($userId, $groupId, $sort,
$filter, $first, $max, $fields, $token);
+ return $this->service->getPeople($requestItem->getUser(),
$requestItem->getGroup(), $requestItem->getOrderBy(),
$requestItem->getFilterBy(), $requestItem->getStartIndex(),
$requestItem->getCount(), $fields, $requestItem->getToken());
}
- public function handleDelete($params, $token)
+ public function handleDelete(RestRequestItem $requestItem)
{
- return new ResponseItem(BAD_REQUEST, "You can't delete people.",
null);
+ return new ResponseItem(BAD_REQUEST, "You can't delete
people.", null);
}
-
- public function handlePost($params, $token)
+
+ public function handlePost(RestRequestItem $requestItem)
{
- return new ResponseItem(NOT_IMPLEMENTED, "You can't edit people
right now.", null);
+ return new ResponseItem(NOT_IMPLEMENTED, "You can't edit people
right now.", null);
}
- public function handlePut($params, $token)
+ public function handlePut(RestRequestItem $requestItem)
{
- return new ResponseItem(NOT_IMPLEMENTED, "You can't add people
right now.", null);
+ return new ResponseItem(NOT_IMPLEMENTED, "You can't add people
right now.", null);
}
}
\ No newline at end of file
Modified: incubator/shindig/trunk/php/src/socialrest/http/RestServlet.php
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/php/src/socialrest/http/RestServlet.php?rev=671511&r1=671510&r2=671511&view=diff
==============================================================================
--- incubator/shindig/trunk/php/src/socialrest/http/RestServlet.php (original)
+++ incubator/shindig/trunk/php/src/socialrest/http/RestServlet.php Wed Jun 25
04:21:42 2008
@@ -39,19 +39,21 @@
require 'src/socialrest/UserId.php';
require 'src/socialrest/ResponseItem.php';
require 'src/socialrest/RestfulCollection.php';
+require 'src/socialrest/http/RestRequestItem.php';
/*
* See:
*
http://sites.google.com/a/opensocial.org/opensocial/Technical-Resources/opensocial-specification----implementation-version-08/restful-api-specification
- * OpenSocial uses standard HTTP methods: GET to retrieve, PUT to update in
place, POST to create new, and DELETE to remove.
+ * OpenSocial uses standard HTTP methods: GET to retrieve, PUT to update in
place, POST to create new, and DELETE to remove.
* POST is special; it operates on collections and creates new activities,
persons, or app data within those collections,
* and returns the base URI for the created resource in the Location: header,
per AtomPub semantics.
- *
+ *
* Error status is returned by HTTP error code, with the error message in the
html's body
*/
//NOTE TO SELF: delete should respond with a 204 No Content to indicate
success?
+
/*
* Internal error code representations, these get translated into http codes
in the outputError() function
*/
@@ -65,51 +67,109 @@
class RestException extends Exception {}
class RestServlet extends HttpServlet {
+
+ private static $JSON_BATCH_ROUTE = "jsonBatch";
public function doPost($method = 'POST')
{
$this->setNoCache(true);
$this->noHeaders = true;
- try {
- // use security token, for now this is required
- // (later oauth should also be a way to specify this
info)
- $token = $this->getSecurityToken();
- $params = $this->getListParams();
- switch ($params[0]) {
- case 'people':
- $class = 'PersonHandler';
- break;
- case 'activities':
- $class = 'ActivityHandler';
- break;
- case 'appdata':
- $class = 'AppDataHandler';
- break;
- //TODO add 'groups' here
- default:
- $response = new
ResponseItem(NOT_IMPLEMENTED, "{$params[0]} is not implemented");
- break;
- }
- if (class_exists($class, true)) {
- $class = new $class(null);
- $response = $class->handleMethod($method,
$params, $token);
+ // use security token, for now this is required
+ // (later oauth should also be a way to specify this info)
+ $token = $this->getSecurityToken();
+ $req = null;
+ if ($this->isBatchUrl()) {
+ $req = $this->handleBatchRequest($token);
+ echo json_encode(array("responses" => $req, "error" =>
false));
+ } else {
+ $responseItem = $this->handleSingleRequest($token,
$method);
+ echo json_encode($responseItem);
+ }
+ }
+
+ private function handleSingleRequest($token, $method)
+ {
+ $params = $this->getListParams();
+ $requestItem = new RestRequestItem();
+ $url = $this->getUrl();
+ $requestParam = $this->getRequestParams();
+ $requestItem->createRequestItem($url, $token, $method, $params,
$requestParam);
+ $responseItem = $this->getResponseItem($requestItem);
+ return $responseItem;
+ }
+
+ private function getRouteFromParameter($pathInfo)
+ {
+ $pathInfo = substr($pathInfo, 1);
+ $indexOfNextPathSeparator = strpos($pathInfo, "/");
+ return $indexOfNextPathSeparator != - 1 ? substr($pathInfo, 0,
$indexOfNextPathSeparator) : $pathInfo;
+ }
+
+ private function getResponseItem(RestRequestItem $requestItem)
+ {
+ $path = $this->getRouteFromParameter($requestItem->getUrl());
+ switch ($path) {
+ case 'people':
+ $class = 'PersonHandler';
+ break;
+ case 'activities':
+ $class = 'ActivityHandler';
+ break;
+ case 'appdata':
+ $class = 'AppDataHandler';
+ break;
+ //TODO add 'groups' here
+ default:
+ $response = new ResponseItem(NOT_IMPLEMENTED,
"{$path} is not implemented");
+ break;
+ }
+ if (class_exists($class, true)) {
+ $class = new $class(null);
+ $response = $class->handleMethod($requestItem);
+ }
+ if ($this->getOutputFormat() == 'json') {
+ if ($this->isBatchUrl()) {
+ return $response;
+ } else {
+ //If the method exists, its an ResponseItem
Error
+ if ($response->getError() != null) {
+ $this->outputError($response);
+ }
+ return $response->getResponse();
}
- } catch (Exception $e) {
- $response = new ResponseItem(INTERNAL_ERROR,
$e->getMessage(), null);
+ } else { // output atom format
+ }
+ }
+
+ private function getRequestParams()
+ {
+ $post = in_array('request', $_POST) != null ? $_POST['request']
: null;
+ $requestParam = isset($GLOBALS['HTTP_RAW_POST_DATA']) ?
$GLOBALS['HTTP_RAW_POST_DATA'] : $post;
+ if (get_magic_quotes_gpc()) {
+ $requestParam = stripslashes($requestParam);
}
- if (!is_object($response) || $response->getError()) {
- // error responses are the same for json and atom (ie:
http error codes + message in body)
- if (!$response instanceof ResponseItem) {
- // just incase ..
- $response = new ResponseItem(INTERNAL_ERROR,
"Internal Server Error, return value is not a response item");
+ $requests = json_decode($requestParam);
+ if ($requests == (isset($GLOBALS['HTTP_RAW_POST_DATA']) ?
$GLOBALS['HTTP_RAW_POST_DATA'] : $post)) {
+ return new ResponseItem(BAD_REQUEST, "Malformed json
string");
+ }
+ return $requests;
+ }
+
+ private function handleBatchRequest($token)
+ {
+ // we support both a raw http post (without
application/x-www-form-urlencoded headers) like java does
+ // and a more php / curl safe version of a form post with
'request' as the post field that holds the request json data
+ if (isset($GLOBALS['HTTP_RAW_POST_DATA']) ||
isset($_POST['request'])) {
+ $requests = $this->getRequestParams();
+ $responses = array();
+ foreach ($requests as $key => $value) {
+ $requestItem = new RestRequestItem();
+
$requestItem->createRequestItemWithRequest($value, $token);
+ $responses[$key] =
$this->getResponseItem($requestItem);
}
- $this->outputError($response);
+ return $responses;
} else {
- if ($this->getOutputFormat() == 'json') {
- echo
json_encode($response->getResponse());
- } else {
- // output atom format
- }
+ throw new Exception("No post data set");
}
}
@@ -127,7 +187,7 @@
{
$this->doPost('DELETE');
}
-
+
private function outputError(ResponseItem $response)
{
$errorMessage = $response->getErrorMessage();
@@ -151,7 +211,7 @@
default:
$code = '500 Internal Server Error';
break;
-
+
}
header("HTTP/1.0 $code", true);
echo "$code - $errorMessage";
@@ -174,7 +234,7 @@
private function getOutputFormat()
{
- return isset($_GET['format']) && $_GET['format'] == 'atom' ?
'atom' : 'json';
+ return isset($_POST['format']) && $_POST['format'] == 'atom' ?
'atom' : 'json';
}
private function getListParams()
@@ -187,4 +247,14 @@
$restParams = explode('/', $uri);
return $restParams;
}
+
+ private function getUrl()
+ {
+ return substr($_SERVER["REQUEST_URI"],
strlen(Config::get('web_prefix') . '/social/rest'));
+ }
+
+ public function isBatchUrl()
+ {
+ return strrpos($_SERVER["REQUEST_URI"],
RestServlet::$JSON_BATCH_ROUTE) > 0;
+ }
}