Rating Service. Work ongoing.

Added a POST webscript to apply a rating to a node.
  Associated REST test: apply rating as user one, get, apply rating as user two, get.
  Deleted the PUT webscript (half-formed anyway)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21086 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Neil McErlean
2010-07-12 11:33:57 +00:00
parent 91efe3970c
commit 83bb722d2e
6 changed files with 255 additions and 55 deletions

View File

@@ -0,0 +1,22 @@
<webscript>
<shortname>POST a rating to a NodeRef</shortname>
<description><![CDATA[
Posts a user rating to the specified NodeRef.<br/>
The rating consists of a score and a rating scheme name. The rating scheme will
define a minimum and a maximum allowed score and it is the responsibility of the
caller of this webscript to ensure that the scheme name is valid and that the posted
score is within the allowed range.<br/>
The rating will be applied using the fully authenticated user who makes the POST call.<br/>
The body of the post should be in the form, e.g.<br/>
{<br/>
&nbsp;&nbsp;&nbsp;"rating": 5,<br/>
&nbsp;&nbsp;&nbsp;"ratingScheme": "fiveStarRatingScheme"<br/>
}<br/>
]]>
</description>
<url>/api/node/{store_type}/{store_id}/{id}/ratings</url>
<format default="json" />
<authentication>user</authentication>
<transaction>required</transaction>
<lifecycle>internal</lifecycle>
</webscript>

View File

@@ -0,0 +1,8 @@
<#escape x as jsonUtils.encodeJSONString(x)>
{
"data":
{
"ratedNodeUrl": "${ratedNode!""}"
}
}
</#escape>

View File

@@ -753,6 +753,11 @@
parent="abstractRatingWebScript"> parent="abstractRatingWebScript">
</bean> </bean>
<bean id="webscript.org.alfresco.repository.rating.rating.post"
class="org.alfresco.repo.web.scripts.rating.RatingPost"
parent="abstractRatingWebScript">
</bean>
<bean id="webscript.org.alfresco.repository.rating.ratingdefinitions.get" <bean id="webscript.org.alfresco.repository.rating.ratingdefinitions.get"
class="org.alfresco.repo.web.scripts.rating.RatingDefinitionsGet" class="org.alfresco.repo.web.scripts.rating.RatingDefinitionsGet"
parent="abstractRatingWebScript"> parent="abstractRatingWebScript">

View File

@@ -18,21 +18,88 @@
*/ */
package org.alfresco.repo.web.scripts.rating; package org.alfresco.repo.web.scripts.rating;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.alfresco.service.cmr.rating.RatingScheme;
import org.alfresco.service.cmr.repository.NodeRef;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
/** /**
* @author unknown * This class is the controller for the rating.post webscript.
* *
* @author Neil McErlean
* @since 3.4
*/ */
public class RatingPost extends AbstractRatingWebScript public class RatingPost extends AbstractRatingWebScript
{ {
// Web script parameters.
private static final String RATING_SCHEME = "ratingScheme";
private static final String RATING = "rating";
private static final String RATED_NODE = "ratedNode";
// Url format
private final static String NODE_RATINGS_URL_FORMAT = "/api/node/{0}/ratings";
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache) protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{ {
return null; Map<String, Object> model = new HashMap<String, Object>();
NodeRef nodeRefToBeRated = parseRequestForNodeRef(req);
JSONObject json = null;
try
{
// read request json
json = new JSONObject(new JSONTokener(req.getContent().getContent()));
// Check mandatory parameters.
if (json.has(RATING) == false)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "rating parameter missing when applying rating");
}
if (json.has(RATING_SCHEME) == false)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "schemeName parameter missing when applying rating");
}
// Check that the scheme name actually exists
String schemeName = json.getString(RATING_SCHEME);
RatingScheme scheme = ratingService.getRatingScheme(schemeName);
if (scheme == null)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Unknown scheme name: " + schemeName);
}
// Range checking of the rating score will be done within the RatingService.
// So we can just apply the rating.
int rating = json.getInt(RATING);
ratingService.applyRating(nodeRefToBeRated, rating, schemeName);
// We'll return the URL to the ratings of the just-rated node.
String ratedNodeUrlFragment = nodeRefToBeRated.toString().replace("://", "/");
String ratedNodeUrl = MessageFormat.format(NODE_RATINGS_URL_FORMAT, ratedNodeUrlFragment);
model.put(RATED_NODE, ratedNodeUrl);
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not read content from req.", iox);
}
catch (JSONException je)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not parse JSON from req.", je);
}
return model;
} }
} }

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.web.scripts.rating;
import java.util.Map;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;
/**
* @author unknown
*
*/
public class RatingPut extends RatingPost
{
@Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
return null;
}
}

View File

@@ -28,40 +28,57 @@ import org.alfresco.repo.web.scripts.BaseWebScriptTest;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.util.PropertyMap;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONStringer;
import org.json.JSONTokener; import org.json.JSONTokener;
import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response; import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
public class RatingRestApiTest extends BaseWebScriptTest public class RatingRestApiTest extends BaseWebScriptTest
{ {
private final static String GET_RATINGS_URL_FORMAT = "/api/node/{0}/ratings"; private static final String USER_ONE = "UserOne";
private static final String USER_TWO = "UserTwo";
private final static String NODE_RATINGS_URL_FORMAT = "/api/node/{0}/ratings";
private final static String GET_RATING_DEFS_URL = "/api/rating/schemedefinitions"; private final static String GET_RATING_DEFS_URL = "/api/rating/schemedefinitions";
private static final String APPLICATION_JSON = "application/json"; private static final String APPLICATION_JSON = "application/json";
private NodeRef testNode; private NodeRef testNode;
protected NodeService nodeService; private MutableAuthenticationService authenticationService;
protected Repository repositoryHelper; private NodeService nodeService;
protected RetryingTransactionHelper transactionHelper; private PersonService personService;
private Repository repositoryHelper;
private RetryingTransactionHelper transactionHelper;
@Override @Override
protected void setUp() throws Exception protected void setUp() throws Exception
{ {
super.setUp(); super.setUp();
authenticationService = (MutableAuthenticationService) getServer().getApplicationContext().getBean("AuthenticationService");
nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService"); nodeService = (NodeService) getServer().getApplicationContext().getBean("NodeService");
personService = (PersonService) getServer().getApplicationContext().getBean("PersonService");
repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper"); repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper");
transactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper"); transactionHelper = (RetryingTransactionHelper)getServer().getApplicationContext().getBean("retryingTransactionHelper");
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
// Create a test node which we will rate. It doesn't matter that it has no content. // Create some users to rate each other's content
// and a test node which we will rate.
// It doesn't matter that it has no content.
testNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>() testNode = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<NodeRef>()
{ {
public NodeRef execute() throws Throwable public NodeRef execute() throws Throwable
{ {
createUser(USER_ONE);
createUser(USER_TWO);
ChildAssociationRef result = nodeService.createNode(repositoryHelper.getCompanyHome(), ChildAssociationRef result = nodeService.createNode(repositoryHelper.getCompanyHome(),
ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS,
ContentModel.TYPE_CONTENT, null); ContentModel.TYPE_CONTENT, null);
@@ -74,6 +91,9 @@ public class RatingRestApiTest extends BaseWebScriptTest
public void tearDown() throws Exception public void tearDown() throws Exception
{ {
super.tearDown(); super.tearDown();
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>() transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Void>()
{ {
public Void execute() throws Throwable public Void execute() throws Throwable
@@ -81,6 +101,8 @@ public class RatingRestApiTest extends BaseWebScriptTest
if (testNode != null && nodeService.exists(testNode)) if (testNode != null && nodeService.exists(testNode))
{ {
nodeService.deleteNode(testNode); nodeService.deleteNode(testNode);
deleteUser(USER_ONE);
deleteUser(USER_TWO);
} }
return null; return null;
} }
@@ -88,9 +110,7 @@ public class RatingRestApiTest extends BaseWebScriptTest
} }
//TODO test POST out-of-range. //TODO test POST out-of-range.
//TODO test get my-ratings on node with mine & others' ratings.
//TODO test GET average //TODO test GET average
//TODO test POST and PUT (same)
public void testGetRatingSchemeDefinitions() throws Exception public void testGetRatingSchemeDefinitions() throws Exception
{ {
@@ -100,9 +120,6 @@ public class RatingRestApiTest extends BaseWebScriptTest
JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString()));
System.err.println(jsonRsp);
JSONObject dataObj = (JSONObject)jsonRsp.get("data"); JSONObject dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj); assertNotNull("JSON 'data' object was null", dataObj);
@@ -124,16 +141,13 @@ public class RatingRestApiTest extends BaseWebScriptTest
public void testGetRatingsFromUnratedNodeRef() throws Exception public void testGetRatingsFromUnratedNodeRef() throws Exception
{ {
// GET ratings // GET ratings
String nodeUrl = testNode.toString().replace("://", "/"); String ratingUrl = getRatingUrl(testNode);
String ratingUrl = MessageFormat.format(GET_RATINGS_URL_FORMAT, nodeUrl);
final int expectedStatus = 200; final int expectedStatus = 200;
Response rsp = sendRequest(new GetRequest(ratingUrl), expectedStatus); Response rsp = sendRequest(new GetRequest(ratingUrl), expectedStatus);
JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString())); JSONObject jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString()));
System.err.println(jsonRsp);
JSONObject dataObj = (JSONObject)jsonRsp.get("data"); JSONObject dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj); assertNotNull("JSON 'data' object was null", dataObj);
@@ -141,4 +155,126 @@ public class RatingRestApiTest extends BaseWebScriptTest
final JSONArray ratingsArray = dataObj.getJSONArray("ratings"); final JSONArray ratingsArray = dataObj.getJSONArray("ratings");
assertEquals(0, ratingsArray.length()); assertEquals(0, ratingsArray.length());
} }
public void testApplyRatingAndRetrieve() throws Exception
{
// POST a new rating to the testNode - as User One.
AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE);
final String ratingUrl = getRatingUrl(testNode);
final int ratingValue = 5;
String jsonString = new JSONStringer().object()
.key("rating").value(ratingValue)
.key("ratingScheme").value("fiveStarRatingScheme")
.endObject()
.toString();
Response rsp = sendRequest(new PostRequest(ratingUrl,
jsonString, APPLICATION_JSON), 200);
String rspContent = rsp.getContentAsString();
// Get the returned URL and validate
JSONObject jsonRsp = new JSONObject(new JSONTokener(rspContent));
JSONObject dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj);
String returnedUrl = dataObj.getString("ratedNodeUrl");
assertEquals(ratingUrl, returnedUrl);
// Now GET the ratings via that returned URL
rsp = sendRequest(new GetRequest(ratingUrl), 200);
jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString()));
dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj);
// There should only be the one rating in there.
final JSONArray ratingsArray = dataObj.getJSONArray("ratings");
assertEquals(1, ratingsArray.length());
JSONObject firstRating = (JSONObject)ratingsArray.get(0);
assertEquals(ratingValue, firstRating.getInt("rating"));
assertEquals("fiveStarRatingScheme", firstRating.getString("ratingScheme"));
// Now POST a second new rating to the testNode - as User Two.
AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO);
final int userTwoRatingValue = 3;
jsonString = new JSONStringer().object()
.key("rating").value(userTwoRatingValue)
.key("ratingScheme").value("fiveStarRatingScheme")
.endObject()
.toString();
rsp = sendRequest(new PostRequest(ratingUrl,
jsonString, APPLICATION_JSON), 200);
rspContent = rsp.getContentAsString();
// Get the returned URL and validate
jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString()));
dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj);
returnedUrl = dataObj.getString("ratedNodeUrl");
// Again GET the ratings via that returned URL
rsp = sendRequest(new GetRequest(returnedUrl), 200);
jsonRsp = new JSONObject(new JSONTokener(rsp.getContentAsString()));
dataObj = (JSONObject)jsonRsp.get("data");
assertNotNull("JSON 'data' object was null", dataObj);
// There should still only be the one rating in the results - because we're running
// as UserTwo and should not see UserOne's rating.
final JSONArray userTwoRatingsArray = dataObj.getJSONArray("ratings");
assertEquals(1, userTwoRatingsArray.length());
JSONObject secondRating = (JSONObject)userTwoRatingsArray.get(0);
assertEquals(userTwoRatingValue, secondRating.getInt("rating"));
assertEquals("fiveStarRatingScheme", secondRating.getString("ratingScheme"));
//TODO Could probably put the GET average call here then.
}
/**
* This method gives the 'ratings' URL for the specified NodeRef.
*/
private String getRatingUrl(NodeRef nodeRef)
{
String nodeUrl = nodeRef.toString().replace("://", "/");
String ratingUrl = MessageFormat.format(NODE_RATINGS_URL_FORMAT, nodeUrl);
return ratingUrl;
}
private void createUser(String userName)
{
if (! authenticationService.authenticationExists(userName))
{
authenticationService.createAuthentication(userName, "PWD".toCharArray());
}
if (! personService.personExists(userName))
{
PropertyMap ppOne = new PropertyMap(4);
ppOne.put(ContentModel.PROP_USERNAME, userName);
ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName");
ppOne.put(ContentModel.PROP_LASTNAME, "lastName");
ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");
personService.createPerson(ppOne);
}
}
private void deleteUser(String userName)
{
if (personService.personExists(userName))
{
personService.deletePerson(userName);
}
}
} }