Merged BRANCHES/DEV/BELARUS/HEAD_2010_07_19 to HEAD:

21326: ALF-3894 : F81 REST API to update properties for a particular task instance
   21376: ALF-3894 : F81 REST API to update properties for a particular task instance

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21404 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2010-07-26 10:54:01 +00:00
parent 1b35eca416
commit 7eef4234aa
11 changed files with 320 additions and 13 deletions

View File

@@ -20,7 +20,9 @@ package org.alfresco.repo.web.scripts.workflow;
import java.util.Map;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
@@ -31,14 +33,16 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
/**
* @author Nick Smith
*
* @since 3.4
*/
public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript
{
private NamespaceService namespaceService;
private NodeService nodeService;
private PersonService personService;
protected NamespaceService namespaceService;
protected NodeService nodeService;
protected PersonService personService;
protected DictionaryService dictionaryService;
protected AuthenticationService authenticationService;
protected WorkflowService workflowService;
@Override
@@ -63,6 +67,16 @@ public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript
this.personService = personService;
}
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
public void setWorkflowService(WorkflowService workflowService)
{
this.workflowService = workflowService;
@@ -80,4 +94,4 @@ public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript
WorkflowModelBuilder modelBuilder,
WebScriptRequest req,
Status status, Cache cache);
}
}

View File

@@ -31,7 +31,7 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
/**
* @author unknown
*
* @since 3.4
*/
public class TaskInstanceGet extends AbstractWorkflowWebscript
{
@@ -41,19 +41,23 @@ public class TaskInstanceGet extends AbstractWorkflowWebscript
{
Map<String, String> params = req.getServiceMatch().getTemplateVars();
// getting task id from request parameters
String taskId = params.get("task_instance_id");
// searching for task in repository
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
// task was not found -> return 404
if (workflowTask == null)
{
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find workflow task with id: " + taskId);
}
Map<String, Object> model = new HashMap<String, Object>();
// build the model for ftl
model.put("workflowTask", modelBuilder.buildDetailed(workflowTask));
return model;
}
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.workflow;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.namespace.QName;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
/**
* @author unknown
* @since 3.4
*/
public class TaskInstancePut extends AbstractWorkflowWebscript
{
@Override
protected Map<String, Object> buildModel(WorkflowModelBuilder modelBuilder, WebScriptRequest req, Status status, Cache cache)
{
Map<String, String> params = req.getServiceMatch().getTemplateVars();
// getting task id from request parameters
String taskId = params.get("task_instance_id");
JSONObject json = null;
try
{
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
String currentUser = authenticationService.getCurrentUserName();
Serializable owner = workflowTask.properties.get(ContentModel.PROP_OWNER);
Serializable initiator = getWorkflowInitiator(workflowTask);
if (!(owner != null && currentUser.equals(owner) || initiator != null && currentUser.equals(initiator)))
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Failed to update workflow task with id: " + taskId);
}
// read request json
json = new JSONObject(new JSONTokener(req.getContent().getContent()));
// update task properties
workflowTask = workflowService.updateTask(taskId, parseTaskProperties(json), null, null);
// task was not founded -> return 404
if (workflowTask == null)
{
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Failed to find workflow task with id: " + taskId);
}
Map<String, Object> model = new HashMap<String, Object>();
// build the model for ftl
model.put("workflowTask", modelBuilder.buildDetailed(workflowTask));
return model;
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not read content from request.", iox);
}
catch (JSONException je)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not parse JSON from request.", je);
}
}
@SuppressWarnings("unchecked")
private Map<QName, Serializable> parseTaskProperties(JSONObject json) throws JSONException
{
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
// gets the array of properties names
String[] names = JSONObject.getNames(json);
if (names != null)
{
// array is not empty
for (String name : names)
{
// build the qname of property
QName key = QName.createQName(name.replaceFirst("_", ":"), namespaceService);
Object jsonValue = json.get(name);
Serializable value = null;
// process null values
if (jsonValue.equals(JSONObject.NULL))
{
props.put(key, null);
}
else
{
// gets the property definition from dictionary
PropertyDefinition prop = dictionaryService.getProperty(key);
if (prop != null)
{
// convert property using its data type specified in model
value = (Serializable) DefaultTypeConverter.INSTANCE.convert(prop.getDataType(), json.get(name));
}
else
{
// property definition was not founded in dictionary
if (jsonValue instanceof JSONArray)
{
value = new ArrayList<String>();
for (int i = 0; i < ((JSONArray)jsonValue).length(); i++)
{
((List<String>)value).add(((JSONArray)jsonValue).getString(i));
}
}
else
{
value = (Serializable) DefaultTypeConverter.INSTANCE.convert(NodeRef.class, jsonValue.toString().replaceAll("\\\\", ""));
}
}
}
props.put(key, value);
}
}
return props;
}
/**
* Retrieves the workflow initiator for the given workflow task.
*
* @param workflowTask The task to get the initiator for
* @return The user name of the initiator or null if there isn't one
*/
private Serializable getWorkflowInitiator(WorkflowTask workflowTask)
{
Serializable initiatorUserName = null;
NodeRef initiator = workflowTask.path.instance.initiator;
if (initiator != null)
{
initiatorUserName = this.nodeService.getProperty(initiator, ContentModel.PROP_USERNAME);
}
return initiatorUserName;
}
}

View File

@@ -39,6 +39,7 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.workflow.WorkflowDefinition;
import org.alfresco.service.cmr.workflow.WorkflowNode;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTransition;
@@ -144,7 +145,7 @@ public class WorkflowModelBuilder
{
Map<String, Object> model = buildSimple(workflowTask, null);
model.put(TASK_PATH, getUrl(workflowTask) + "/paths/" + workflowTask.path.id);
model.put(TASK_PATH, getUrl(workflowTask, workflowTask.path));
// definition part
model.put(TASK_DEFINITION, buildTaskDefinition(workflowTask.definition, workflowTask));
@@ -348,4 +349,9 @@ public class WorkflowModelBuilder
return "api/classes/" + typeDefinition.getName().toPrefixString().replace(PREFIX_SEPARATOR, "_");
}
private String getUrl(WorkflowTask task, WorkflowPath path)
{
return getUrl(task) + "/paths/" + path.id;
}
}

View File

@@ -40,13 +40,17 @@ import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.GUID;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.surf.util.ISO8601DateFormat;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
/**
@@ -57,11 +61,13 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
{
private final static String USER1 = "Bob" + GUID.generate();
private final static String USER2 = "Jane" + GUID.generate();
private final static String USER3 = "Nick" + GUID.generate();
private static final String URL_TASKS = "api/task-instances";
private static final String URL_WORKFLOW_DEFINITIONS = "api/workflow-definitions";
private TestPersonManager personManager;
private WorkflowService workflowService;
private NamespaceService namespaceService;
private NodeRef packageRef;
public void testTaskInstancesGet() throws Exception
@@ -213,6 +219,49 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
}
public void testTaskInstancePut() throws Exception
{
//Start workflow as USER1 and assign task to USER2.
personManager.setUser(USER1);
WorkflowDefinition adhocDef = workflowService.getDefinitionByName("jbpm$wf:adhoc");
Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
params.put(WorkflowModel.PROP_DUE_DATE, new Date());
params.put(WorkflowModel.PROP_PRIORITY, 1);
params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.id, params);
WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.id).get(0);
Response getResponse = sendRequest(new GetRequest(URL_TASKS + "/" + startTask.id), 200);
JSONObject jsonProperties = new JSONObject(getResponse.getContentAsString()).getJSONObject("data").getJSONObject("properties");
// make some changes
jsonProperties.remove(qnameToString(WorkflowModel.ASSOC_PACKAGE));
jsonProperties.put(qnameToString(WorkflowModel.PROP_COMMENT), "Edited comment");
jsonProperties.put(qnameToString(WorkflowModel.PROP_DUE_DATE), ISO8601DateFormat.format(new Date()));
jsonProperties.put(qnameToString(WorkflowModel.PROP_DESCRIPTION), "Edited description");
jsonProperties.put(qnameToString(WorkflowModel.PROP_PRIORITY), 1);
personManager.setUser(USER3);
Response unauthResponse = sendRequest(new PutRequest(URL_TASKS + "/" + startTask.id, jsonProperties.toString(), "application/json"), 401);
assertEquals(Status.STATUS_UNAUTHORIZED, unauthResponse.getStatus());
personManager.setUser(USER1);
Response putResponse = sendRequest(new PutRequest(URL_TASKS + "/" + startTask.id, jsonProperties.toString(), "application/json"), 200);
assertEquals(Status.STATUS_OK, putResponse.getStatus());
String jsonStr = putResponse.getContentAsString();
JSONObject json = new JSONObject(jsonStr);
JSONObject result = json.getJSONObject("data");
assertNotNull(result);
JSONObject editedJsonProperties = result.getJSONObject("properties");
compareProperties(jsonProperties, editedJsonProperties);
}
public void testWorkflowDefinitionsGet() throws Exception
{
Response response = sendRequest(new GetRequest(URL_WORKFLOW_DEFINITIONS), 200);
@@ -250,6 +299,7 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
super.setUp();
ApplicationContext appContext = getServer().getApplicationContext();
namespaceService = (NamespaceService)appContext.getBean("NamespaceService");
workflowService = (WorkflowService)appContext.getBean("WorkflowService");
MutableAuthenticationService authenticationService = (MutableAuthenticationService)appContext.getBean("AuthenticationService");
PersonService personService = (PersonService)appContext.getBean("PersonService");
@@ -258,6 +308,7 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
personManager.createPerson(USER1);
personManager.createPerson(USER2);
personManager.createPerson(USER3);
packageRef = workflowService.createPackage(null);
}
@@ -271,4 +322,32 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
super.tearDown();
personManager.clearPeople();
}
private String qnameToString(QName qName)
{
String separator = Character.toString(QName.NAMESPACE_PREFIX);
return qName.toPrefixString(namespaceService).replaceFirst(separator, "_");
}
private void compareProperties(JSONObject before, JSONObject after) throws JSONException
{
for (String name : JSONObject.getNames(after))
{
if (before.has(name))
{
if (before.get(name) instanceof JSONArray)
{
for (int i = 0; i < before.getJSONArray(name).length(); i++)
{
assertEquals(before.getJSONArray(name).get(i), after.getJSONArray(name).get(i));
}
}
else
{
assertEquals(before.get(name), after.get(name));
}
}
}
}
}