From 2881f31d3fc4103e5c082531fc8e739448a822c5 Mon Sep 17 00:00:00 2001 From: Tijs Rademakers Date: Wed, 28 Aug 2013 10:05:33 +0000 Subject: [PATCH] Fixes for history processes and tasks, support for bpm_assignees, and fix for task update response git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@54547 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rest/workflow/api/impl/ProcessesImpl.java | 244 ++++++++++++------ .../workflow/api/impl/RestVariableHelper.java | 4 +- .../rest/workflow/api/impl/TasksImpl.java | 185 ++++++++----- .../workflow/api/impl/WorkflowRestImpl.java | 116 +++++---- .../rest/workflow/api/model/Task.java | 8 +- .../api/tests/EnterpriseWorkflowTestApi.java | 39 +++ .../api/tests/ProcessWorkflowApiTest.java | 15 +- .../api/tests/TaskWorkflowApiTest.java | 94 +++++-- 8 files changed, 481 insertions(+), 224 deletions(-) diff --git a/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java index 6871d31661..f329f39cf1 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java @@ -90,7 +90,6 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.InvalidQNameException; @@ -102,50 +101,53 @@ import org.apache.commons.io.IOUtils; public class ProcessesImpl extends WorkflowRestImpl implements Processes { - private WorkflowPackageImpl workflowPackageComponent; - private ServiceRegistry serviceRegistry; - private AuthorityDAO authorityDAO; - private NodeService nodeService; - private PersonService personService; - private MessageService messageService; - private String engineId; - private Repository repositoryHelper; - private RestVariableHelper restVariableHelper; - - private ActivitiNodeConverter nodeConverter; - private ActivitiUtil activitiUtil; - private DefaultWorkflowPropertyHandler defaultPropertyHandler; - private WorkflowQNameConverter qNameConverter; - private QName defaultStartTaskType = WorkflowModel.TYPE_ACTIVTI_START_TASK; - private WorkflowObjectFactory workflowFactory; - private WorkflowPropertyHandlerRegistry handlerRegistry; - private WorkflowAuthorityManager authorityManager; - private ActivitiPropertyConverter propertyConverter; - private ActivitiTypeConverter typeConverter; + protected static final String BPM_PACKAGE = "bpm_package"; protected static String PROCESS_STATUS_ANY = "any"; protected static String PROCESS_STATUS_ACTIVE = "active"; protected static String PROCESS_STATUS_COMPLETED = "completed"; - private static final Set PROCESS_STATUS_LIST = new HashSet(Arrays.asList( + + protected static final Set PROCESS_STATUS_LIST = new HashSet(Arrays.asList( PROCESS_STATUS_ANY, PROCESS_STATUS_ACTIVE, PROCESS_STATUS_COMPLETED )); - private static final Set PROCESS_COLLECTION_EQUALS_QUERY_PROPERTIES = new HashSet(Arrays.asList( + protected static final Set PROCESS_COLLECTION_EQUALS_QUERY_PROPERTIES = new HashSet(Arrays.asList( "processDefinitionId", "businessKey", "processDefinitionKey", "startUserId", "status" )); - private static final Set PROCESS_COLLECTION_GREATERTHAN_QUERY_PROPERTIES = new HashSet(Arrays.asList( + protected static final Set PROCESS_COLLECTION_GREATERTHAN_QUERY_PROPERTIES = new HashSet(Arrays.asList( "startedAt", "endedAt" )); - private static final Set PROCESS_COLLECTION_LESSTHAN_QUERY_PROPERTIES = new HashSet(Arrays.asList( + protected static final Set PROCESS_COLLECTION_LESSTHAN_QUERY_PROPERTIES = new HashSet(Arrays.asList( "startedAt", "endedAt" )); - private static final Set PROCESS_COLLECTION_SORT_PROPERTIES = new HashSet(Arrays.asList( + protected static final Set PROCESS_COLLECTION_SORT_PROPERTIES = new HashSet(Arrays.asList( "processDefinitionId", "businessKey", "id", "startedAt", "endedAt", "durationInMillis" )); + protected WorkflowPackageImpl workflowPackageComponent; + protected ServiceRegistry serviceRegistry; + protected AuthorityDAO authorityDAO; + protected NodeService nodeService; + protected PersonService personService; + protected MessageService messageService; + protected String engineId; + protected Repository repositoryHelper; + protected RestVariableHelper restVariableHelper; + + protected ActivitiNodeConverter nodeConverter; + protected ActivitiUtil activitiUtil; + protected DefaultWorkflowPropertyHandler defaultPropertyHandler; + protected WorkflowQNameConverter qNameConverter; + protected QName defaultStartTaskType = WorkflowModel.TYPE_ACTIVTI_START_TASK; + protected WorkflowObjectFactory workflowFactory; + protected WorkflowPropertyHandlerRegistry handlerRegistry; + protected WorkflowAuthorityManager authorityManager; + protected ActivitiPropertyConverter propertyConverter; + protected ActivitiTypeConverter typeConverter; + public void setAuthorityDAO(AuthorityDAO authorityDAO) { this.authorityDAO = authorityDAO; @@ -532,7 +534,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("Either processDefinitionId or processDefinitionKey is required"); } - if(!definitionExistingChecked) + if (definitionExistingChecked == false) { // Check if the required definition actually exists ProcessDefinitionQuery query = activitiProcessEngine @@ -584,11 +586,71 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes AssociationDefinition associationDef = taskAssociations.get(propNameMap.get(variableName)); if (variableValue != null && ContentModel.TYPE_PERSON.equals(associationDef.getTargetClass().getName())) { - variableValue = getPersonNodeRef(variableValue.toString()); + if (associationDef.isTargetMany()) + { + if (variableValue instanceof List) + { + List personList = new ArrayList(); + List values = (List) variableValue; + for (Object value : values) + { + NodeRef personRef = getPersonNodeRef(value.toString()); + if (personRef == null) + { + throw new InvalidArgumentException(value.toString() + " is not a valid person user id"); + } + personList.add(personRef); + } + variableValue = personList; + } + else + { + throw new InvalidArgumentException(variableName + " should have an array value"); + } + } + else + { + NodeRef personRef = getPersonNodeRef(variableValue.toString()); + if (personRef == null) + { + throw new InvalidArgumentException(variableValue.toString() + " is not a valid person user id"); + } + variableValue = personRef; + } } else if (variableValue != null && ContentModel.TYPE_AUTHORITY_CONTAINER.equals(associationDef.getTargetClass().getName())) { - variableValue = authorityService.getAuthorityNodeRef(variableValue.toString()); + if (associationDef.isTargetMany()) + { + if (variableValue instanceof List) + { + List authorityList = new ArrayList(); + List values = (List) variableValue; + for (Object value : values) + { + NodeRef authorityRef = authorityService.getAuthorityNodeRef(value.toString()); + if (authorityRef == null) + { + throw new InvalidArgumentException(value.toString() + " is not a valid authority id"); + } + authorityList.add(authorityRef); + } + variableValue = authorityList; + } + else + { + throw new InvalidArgumentException(variableName + " should have an array value"); + } + } + else + { + NodeRef authorityRef = authorityService.getAuthorityNodeRef(variableValue.toString()); + if (authorityRef == null) + { + throw new InvalidArgumentException(variableValue.toString() + " is not a valid authority id"); + } + variableValue = authorityRef; + } } } @@ -621,7 +683,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { for (String item: process.getItems()) { - NodeRef itemNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item); + NodeRef itemNodeRef = getNodeRef(item); QName workflowPackageItemId = QName.createQName("wpi", itemNodeRef.toString()); nodeService.addChild(workflowPackageNodeRef, itemNodeRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); } @@ -680,7 +742,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes public void deleteProcess(String id) { validateIfUserAllowedToWorkWithProcess(id); - activitiProcessEngine.getRuntimeService().deleteProcessInstance(id, null); + activitiProcessEngine.getRuntimeService().deleteProcessInstance(id, "deleted through REST API call"); } @Override @@ -696,9 +758,22 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes ActivitiScriptNode packageScriptNode = null; try { - packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, "bpm_package"); + HistoricVariableInstance variableInstance = activitiProcessEngine.getHistoryService() + .createHistoricVariableInstanceQuery() + .processInstanceId(processId) + .variableName(BPM_PACKAGE) + .singleResult(); + + if (variableInstance != null) + { + packageScriptNode = (ActivitiScriptNode) variableInstance.getValue(); + } + else + { + throw new EntityNotFoundException(processId); + } } - catch(ActivitiObjectNotFoundException e) + catch (ActivitiObjectNotFoundException e) { throw new EntityNotFoundException(processId); } @@ -730,14 +805,29 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("itemId is required to get an attached item"); } + NodeRef nodeRef = getNodeRef(itemId); + validateIfUserAllowedToWorkWithProcess(processId); ActivitiScriptNode packageScriptNode = null; try { - packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, "bpm_package"); + HistoricVariableInstance variableInstance = activitiProcessEngine.getHistoryService() + .createHistoricVariableInstanceQuery() + .processInstanceId(processId) + .variableName(BPM_PACKAGE) + .singleResult(); + + if (variableInstance != null) + { + packageScriptNode = (ActivitiScriptNode) variableInstance.getValue(); + } + else + { + throw new EntityNotFoundException(processId); + } } - catch(ActivitiObjectNotFoundException e) + catch (ActivitiObjectNotFoundException e) { throw new EntityNotFoundException(processId); } @@ -748,7 +838,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes List documentList = nodeService.getChildAssocs(packageScriptNode.getNodeRef()); for (ChildAssociationRef childAssociationRef : documentList) { - if (childAssociationRef.getChildRef().getId().equals(itemId)) + if (childAssociationRef.getChildRef().equals(nodeRef)) { item = createItemForNodeRef(childAssociationRef.getChildRef()); break; @@ -778,10 +868,12 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes validateIfUserAllowedToWorkWithProcess(processId); + NodeRef nodeRef = getNodeRef(item.getId()); + ActivitiScriptNode packageScriptNode = null; try { - packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, "bpm_package"); + packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, BPM_PACKAGE); } catch (ActivitiObjectNotFoundException e) { @@ -796,18 +888,17 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes // check if noderef exists try { - nodeService.getProperties(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item.getId())); + nodeService.getProperties(nodeRef); } catch (Exception e) { - throw new EntityNotFoundException("item with id " + item.getId() + " not found"); + throw new EntityNotFoundException("item with id " + nodeRef.toString() + " not found"); } try { - NodeRef itemNodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, item.getId()); - QName workflowPackageItemId = QName.createQName("wpi", itemNodeRef.toString()); - nodeService.addChild(packageScriptNode.getNodeRef(), itemNodeRef, + QName workflowPackageItemId = QName.createQName("wpi", nodeRef.toString()); + nodeService.addChild(packageScriptNode.getNodeRef(), nodeRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS, workflowPackageItemId); } catch (Exception e) @@ -831,12 +922,14 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("itemId is required to delete an attached item"); } + NodeRef nodeRef = getNodeRef(itemId); + validateIfUserAllowedToWorkWithProcess(processId); ActivitiScriptNode packageScriptNode = null; try { - packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, "bpm_package"); + packageScriptNode = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, BPM_PACKAGE); } catch (ActivitiObjectNotFoundException e) { @@ -852,7 +945,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes List documentList = nodeService.getChildAssocs(packageScriptNode.getNodeRef()); for (ChildAssociationRef childAssociationRef : documentList) { - if (childAssociationRef.getChildRef().getId().equals(itemId)) + if (childAssociationRef.getChildRef().equals(nodeRef)) { itemIdFoundInPackage = true; break; @@ -866,7 +959,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes try { - nodeService.removeChild(packageScriptNode.getNodeRef(), new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, itemId)); + nodeService.removeChild(packageScriptNode.getNodeRef(), nodeRef); } catch (InvalidNodeRefException e) { @@ -879,49 +972,43 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { CollectionWithPagingInfo result = null; - // Check if user is allowed to gte variabled - validateIfUserAllowedToWorkWithProcess(processId); + // Check if user is allowed to get variables + List variableInstances = validateIfUserAllowedToWorkWithProcess(processId); + Map variables = new HashMap(); + for (HistoricVariableInstance variable : variableInstances) + { + variables.put(variable.getVariableName(), variable.getValue()); + } - ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() - .processInstanceId(processId).singleResult(); + ProcessInstance processInstance = activitiProcessEngine.getRuntimeService() + .createProcessInstanceQuery() + .processInstanceId(processId) + .singleResult(); String processDefinitionId = null; - Map variables = null; - if(processInstance != null) + if (processInstance != null) { - // Fetch actual variables from activiti - variables = activitiProcessEngine.getRuntimeService().getVariables(processId); processDefinitionId = processInstance.getProcessDefinitionId(); } else { - // Completed task + // Completed process instance HistoricProcessInstance historicInstance = activitiProcessEngine.getHistoryService().createHistoricProcessInstanceQuery() .processInstanceId(processId).singleResult(); - if(historicInstance == null) + if (historicInstance == null) { throw new EntityNotFoundException(processId); } processDefinitionId = historicInstance.getProcessDefinitionId(); - - // Fetch actual variables from activiti - List varInstances = activitiProcessEngine.getHistoryService() - .createHistoricVariableInstanceQuery().processInstanceId(processId).list(); - - variables = new HashMap(); - for(HistoricVariableInstance var : varInstances) - { - variables.put(var.getVariableName(), var.getValue()); - } } // Get start-task definition for explicit typing of variables submitted at the start String formKey = null; StartFormData startFormData = activitiProcessEngine.getFormService().getStartFormData(processDefinitionId); - if(startFormData != null) + if (startFormData != null) { formKey = startFormData.getFormKey(); } @@ -939,8 +1026,10 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { validateIfUserAllowedToWorkWithProcess(processId); - ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() - .processInstanceId(processId).singleResult(); + ProcessInstance processInstance = activitiProcessEngine.getRuntimeService() + .createProcessInstanceQuery() + .processInstanceId(processId) + .singleResult(); if (processInstance == null) { @@ -955,8 +1044,10 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { validateIfUserAllowedToWorkWithProcess(processId); - ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() - .processInstanceId(processId).singleResult(); + ProcessInstance processInstance = activitiProcessEngine.getRuntimeService() + .createProcessInstanceQuery() + .processInstanceId(processId) + .singleResult(); if (processInstance == null) { @@ -1036,7 +1127,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes try { - if(!activitiProcessEngine.getRuntimeService().hasVariable(processId, variableName)) + if (activitiProcessEngine.getRuntimeService().hasVariable(processId, variableName) == false) { throw new EntityNotFoundException(variableName); } @@ -1053,10 +1144,12 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { validateIfUserAllowedToWorkWithProcess(processId); - ProcessInstance processInstance = activitiProcessEngine.getRuntimeService().createProcessInstanceQuery() - .processInstanceId(processId).singleResult(); + ProcessInstance processInstance = activitiProcessEngine.getRuntimeService() + .createProcessInstanceQuery() + .processInstanceId(processId) + .singleResult(); - if(processInstance == null) + if (processInstance == null) { throw new EntityNotFoundException(processId); } @@ -1162,7 +1255,8 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes item.setCreatedBy(createdBy); item.setModifiedAt(modifiedAt); item.setModifiedBy(modifiedBy); - if (contentData != null) { + if (contentData != null) + { item.setMimeType(contentData.getMimetype()); item.setSize(contentData.getSize()); } diff --git a/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java b/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java index 016fdf07ea..238b1667b9 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java @@ -85,12 +85,12 @@ public class RestVariableHelper List result = new ArrayList(); TypeDefinitionContext context = new TypeDefinitionContext(typeDefinition); - if(localVariables != null) + if (localVariables != null) { addTaskVariables(result, localVariables, context, VariableScope.LOCAL); } - if(globalVariables != null) + if (globalVariables != null) { addTaskVariables(result, globalVariables, context, VariableScope.GLOBAL); } diff --git a/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java index 7d281a9d35..8a4b3a8297 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java @@ -21,17 +21,19 @@ package org.alfresco.rest.workflow.api.impl; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.activiti.engine.ActivitiTaskAlreadyClaimedException; -import org.activiti.engine.form.FormData; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.history.HistoricTaskInstanceQuery; +import org.activiti.engine.history.HistoricVariableInstance; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.impl.task.TaskDefinition; +import org.activiti.engine.task.DelegationState; import org.activiti.engine.task.IdentityLink; import org.activiti.engine.task.IdentityLinkType; import org.activiti.engine.task.TaskQuery; @@ -643,7 +645,8 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks TaskStateTransition taskAction = null; List selectedProperties = parameters.getSelectedProperties(); - if(selectedProperties.contains("state")) { + if (selectedProperties.contains("state")) + { taskAction = TaskStateTransition.getTaskActionFromString(task.getState()); } @@ -651,7 +654,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks TaskQuery query = activitiProcessEngine.getTaskService().createTaskQuery().taskId(taskId); org.activiti.engine.task.Task taskInstance = query.singleResult(); - if(taskInstance == null) + if (taskInstance == null) { // Check if task exists in history, to be able to return appropriate error when trying to update an // existing completed task vs. an unexisting task vs. unauthorized @@ -659,7 +662,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks .taskId(taskId) .count() > 0; - if(taskHasExisted) + if (taskHasExisted) { throw new UnsupportedResourceOperationException("Task with id: " + taskId + " cannot be updated, it's completed"); } @@ -679,7 +682,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks Set candidateGroups = new HashSet(); - if(!authorized) + if (!authorized) { // Check if user is initiator of the process this task is involved with List linksForTask = activitiProcessEngine.getTaskService().getIdentityLinksForTask(taskId); @@ -707,7 +710,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } // When claiming, a limited update (set assignee through claim) is allowed - if(!authorized && taskAction == TaskStateTransition.CLAIMED) + if (!authorized && taskAction == TaskStateTransition.CLAIMED) { Set userGroups = authorityService.getAuthoritiesForUser(user); for(String group : candidateGroups) @@ -720,7 +723,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } } - if(!authorized) + if (!authorized) { // None of the above conditions are met, not authorized to update task throw new PermissionDeniedException(); @@ -728,10 +731,10 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } // Update fields if no action is required - if(taskAction == null) + if (taskAction == null) { // Only update task in Activiti API if actual properties are changed - if(updateTaskProperties(selectedProperties, task, taskInstance)) + if (updateTaskProperties(selectedProperties, task, taskInstance)) { activitiProcessEngine.getTaskService().saveTask(taskInstance); } @@ -739,8 +742,8 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks else { // Perform actions associated to state transition - if(taskAction != null) { - switch(taskAction) { + if (taskAction != null) { + switch (taskAction) { case CLAIMED: try { @@ -780,34 +783,46 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } } - if(taskAction == TaskStateTransition.COMPLETED) + Task responseTask = new Task(activitiProcessEngine.getHistoryService() + .createHistoricTaskInstanceQuery() + .taskId(taskId).singleResult()); + + // if the task is not ended the task state might be pending or resolved + if (responseTask.getEndedAt() == null) { - // Task completed, fetch from history instead of returning runtime instance - return new Task(activitiProcessEngine.getHistoryService() - .createHistoricTaskInstanceQuery() - .taskId(taskId).singleResult()); - } - else - { - // Task object updated, can return the object without fetching it agian - return new Task(taskInstance); + try + { + org.activiti.engine.task.Task runningTask = activitiProcessEngine.getTaskService().createTaskQuery().taskId(taskId).singleResult(); + if (runningTask != null) + { + if (runningTask.getDelegationState() == DelegationState.PENDING) + { + responseTask.setState(TaskStateTransition.DELEGATED.name().toLowerCase()); + } + else if (runningTask.getDelegationState() == DelegationState.RESOLVED) + { + responseTask.setState(TaskStateTransition.RESOLVED.name().toLowerCase()); + } + } + } + catch (Exception e) + { + // ignore the exception + } } + + return responseTask; } @Override public CollectionWithPagingInfo getTaskFormModel(String taskId, Paging paging) { // Check if task can be accessed by the current user - getValidTask(taskId, false); - - FormData formData = activitiProcessEngine.getFormService().getTaskFormData(taskId); - if(formData == null) - { - throw new EntityNotFoundException(taskId); - } + HistoricTaskInstance task = getValidHistoricTask(taskId, true); + String formKey = task.getFormKey(); // Lookup type definition for the task - TypeDefinition taskType = getWorkflowFactory().getTaskFullTypeDefinition(formData.getFormKey(), true); + TypeDefinition taskType = getWorkflowFactory().getTaskFullTypeDefinition(formKey, true); return getFormModelElements(taskType, paging); } @@ -815,21 +830,44 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks public CollectionWithPagingInfo getTaskVariables(String taskId, Paging paging, VariableScope scope) { // Ensure the user is allowed to get variables for the task involved. - org.activiti.engine.task.Task taskInstance = getValidTask(taskId, true); - String formKey = activitiProcessEngine.getFormService().getTaskFormKey(taskInstance.getProcessDefinitionId(), taskInstance.getTaskDefinitionKey()); + HistoricTaskInstance taskInstance = getValidHistoricTask(taskId, true); + String formKey = taskInstance.getFormKey(); // Based on the scope, right variables are queried - Map taskvariables = null; - Map processVariables = null; + Map taskvariables = new HashMap(); + Map processVariables = new HashMap(); - if(scope == VariableScope.ANY || scope == VariableScope.LOCAL) + if (scope == VariableScope.ANY || scope == VariableScope.LOCAL) { - taskvariables = activitiProcessEngine.getTaskService().getVariablesLocal(taskId); + List variables = activitiProcessEngine.getHistoryService() + .createHistoricVariableInstanceQuery() + .taskId(taskId) + .list(); + + if (variables != null) + { + for (HistoricVariableInstance variable : variables) + { + taskvariables.put(variable.getVariableName(), variable.getValue()); + } + } } - if((scope == VariableScope.ANY || scope == VariableScope.GLOBAL) && taskInstance.getExecutionId() != null) + if ((scope == VariableScope.ANY || scope == VariableScope.GLOBAL) && taskInstance.getProcessInstanceId() != null) { - processVariables = activitiProcessEngine.getRuntimeService().getVariables(taskInstance.getExecutionId()); + List variables = activitiProcessEngine.getHistoryService() + .createHistoricVariableInstanceQuery() + .processInstanceId(taskInstance.getProcessInstanceId()) + .excludeTaskVariables() + .list(); + + if (variables != null) + { + for (HistoricVariableInstance variable : variables) + { + processVariables.put(variable.getVariableName(), variable.getValue()); + } + } } // Convert raw variables to TaskVariables @@ -860,25 +898,25 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks protected TaskVariable updateVariableInTask(String taskId, org.activiti.engine.task.Task taskInstance, TaskVariable taskVariable) { - if(taskVariable.getName() == null) + if (taskVariable.getName() == null) { throw new InvalidArgumentException("Variable name is required."); } - if(taskVariable.getVariableScope() == null || taskVariable.getVariableScope() == VariableScope.ANY) + if (taskVariable.getVariableScope() == null || taskVariable.getVariableScope() == VariableScope.ANY) { throw new InvalidArgumentException("Variable scope is required and can only be 'local' or 'global'."); } DataTypeDefinition dataTypeDefinition = null; - if(taskVariable.getType() != null) + if (taskVariable.getType() != null) { try { QName dataType = QName.createQName(taskVariable.getType(), namespaceService); dataTypeDefinition = dictionaryService.getDataType(dataType); } - catch(InvalidQNameException iqne) + catch (InvalidQNameException iqne) { throw new InvalidArgumentException("Unsupported type of variable: '" + taskVariable.getType() +"'."); } @@ -893,7 +931,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks QName propQName = WorkflowQNameConverter.convertNameToQName(taskVariable.getName(), namespaceService); PropertyDefinition propDef = typeDefinition.getProperties().get(propQName); - if(propDef != null) + if (propDef != null) { dataTypeDefinition = propDef.getDataType(); } @@ -906,15 +944,15 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } } } - catch(InvalidQNameException ignore) + catch (InvalidQNameException ignore) { // In case the property is not part of the model, it's possible that the property-name is not a valid. // This can be ignored safeley as it falls back to the raw type } - if(dataTypeDefinition == null) + if (dataTypeDefinition == null) { - // Finall fallback to raw value when no type has been passed and not present in model + // Final fallback to raw value when no type has been passed and not present in model dataTypeDefinition = dictionaryService.getDataType(restVariableHelper.extractTypeFromValue(taskVariable.getValue())); } } @@ -968,7 +1006,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks getValidTask(taskId, true); // Check if variable is present on the scope - if(!activitiProcessEngine.getTaskService().hasVariableLocal(taskId, variableName)) + if (activitiProcessEngine.getTaskService().hasVariableLocal(taskId, variableName) == false) { throw new EntityNotFoundException(variableName); } @@ -978,6 +1016,9 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks @Override public CollectionWithPagingInfo getTaskCandidates(String taskId, Paging paging) { + // Fetch task to check if user is authorized to perform the delete + getValidTask(taskId, true); + List links = activitiProcessEngine.getTaskService().getIdentityLinksForTask(taskId); List page = new ArrayList(); if (links != null) @@ -999,7 +1040,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { org.activiti.engine.task.Task task = getValidTask(taskId, false); - if(task.getProcessInstanceId() == null) + if (task.getProcessInstanceId() == null) { throw new UnsupportedResourceOperationException("Task is not part of process, no items available."); } @@ -1021,9 +1062,9 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks @Override public Item getItem(String taskId, String itemId) { - org.activiti.engine.task.Task task = getValidTask(taskId, false); + HistoricTaskInstance task = getValidHistoricTask(taskId, true); - if(task.getProcessInstanceId() == null) + if (task.getProcessInstanceId() == null) { throw new UnsupportedResourceOperationException("Task is not part of process, no items available."); } @@ -1033,7 +1074,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks @Override public CollectionWithPagingInfo getItems(String taskId, Paging paging) { - org.activiti.engine.task.Task task = getValidTask(taskId, false); + HistoricTaskInstance task = getValidHistoricTask(taskId, true); if(task.getProcessInstanceId() == null) { @@ -1140,10 +1181,11 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks .createHistoricTaskInstanceQuery() .taskId(taskId); - if(authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) + if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) { // Admin is allowed to read all tasks in the current tenant - if(tenantService.isEnabled()) { + if (tenantService.isEnabled()) + { query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); } } @@ -1153,18 +1195,18 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks query.taskInvolvedUser(AuthenticationUtil.getRunAsUser()); } - HistoricTaskInstance taskInstance = query.singleResult(); + HistoricTaskInstance taskInstance = query.singleResult(); - if(taskInstance == null) + if (taskInstance == null) { - // Either the task doesn't exist or the user is not involved direcltly. We can differentiate by + // Either the task doesn't exist or the user is not involved directly. We can differentiate by // checking if the task exists without applying the additional filtering taskInstance = activitiProcessEngine.getHistoryService() .createHistoricTaskInstanceQuery() .taskId(taskId) .singleResult(); - if(taskInstance == null) + if (taskInstance == null) { // Full error message will be "Task with id: 'id' was not found" throw new EntityNotFoundException(taskId); @@ -1173,21 +1215,21 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { boolean isTaskClaimable = false; - if(validIfClaimable) + if (validIfClaimable) { - if(taskInstance.getEndTime() == null) + if (taskInstance.getEndTime() == null) { // Task is not yet finished, so potentially claimable. If user is part of a "candidateGroup", the task is accessible to the // user regardless of not being involved/owner/assignee isTaskClaimable = activitiProcessEngine.getTaskService() - .createTaskQuery() - .taskCandidateGroupIn(new ArrayList(authorityService.getAuthoritiesForUser(AuthenticationUtil.getRunAsUser()))) - .taskId(taskId) - .count() == 1; + .createTaskQuery() + .taskCandidateGroupIn(new ArrayList(authorityService.getAuthoritiesForUser(AuthenticationUtil.getRunAsUser()))) + .taskId(taskId) + .count() == 1; } } - if(!isTaskClaimable) + if (isTaskClaimable == false) { throw new PermissionDeniedException(); } @@ -1206,7 +1248,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks */ protected org.activiti.engine.task.Task getValidTask(String taskId, boolean validIfClaimable) { - if(taskId == null) + if (taskId == null) { throw new InvalidArgumentException("Task id is required."); } @@ -1215,10 +1257,11 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks .createTaskQuery() .taskId(taskId); - if(authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) + if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) { // Admin is allowed to read all tasks in the current tenant - if(tenantService.isEnabled()) { + if (tenantService.isEnabled()) + { query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); } } @@ -1230,16 +1273,16 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks org.activiti.engine.task.Task taskInstance = query.singleResult(); - if(taskInstance == null) + if (taskInstance == null) { - // Either the task doesn't exist or the user is not involved direcltly. We can differentiate by + // Either the task doesn't exist or the user is not involved directly. We can differentiate by // checking if the task exists without applying the additional filtering taskInstance = activitiProcessEngine.getTaskService() .createTaskQuery() .taskId(taskId) .singleResult(); - if(taskInstance == null) + if (taskInstance == null) { // Full error message will be "Task with id: 'id' was not found" throw new EntityNotFoundException(taskId); @@ -1248,7 +1291,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { boolean isTaskClaimable = false; - if(validIfClaimable) + if (validIfClaimable) { // Task is not yet finished, so potentially claimable. If user is part of a "candidateGroup", the task is accessible to the // user regardless of not being involved/owner/assignee @@ -1259,7 +1302,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks .count() == 1; } - if(!isTaskClaimable) + if (isTaskClaimable == false) { throw new PermissionDeniedException(); } diff --git a/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java index aa64e460d1..9a0c6c47f3 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/WorkflowRestImpl.java @@ -4,16 +4,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import org.activiti.engine.ActivitiObjectNotFoundException; import org.activiti.engine.ProcessEngine; import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.history.HistoricTaskInstanceQuery; +import org.activiti.engine.history.HistoricVariableInstance; import org.activiti.engine.impl.ProcessEngineImpl; import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.activiti.engine.impl.context.Context; @@ -43,6 +44,8 @@ import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -103,6 +106,23 @@ public class WorkflowRestImpl this.deployWorkflowsInTenant = deployWorkflowsInTenant; } + /** + * Create NodeRef from item id String + */ + public NodeRef getNodeRef(String itemId) + { + NodeRef nodeRef = null; + if (NodeRef.isNodeRef(itemId) == false) + { + nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, itemId); + } + else + { + nodeRef = new NodeRef(itemId); + } + return nodeRef; + } + /** * Get the process definition from the cache if available * @@ -263,65 +283,63 @@ public class WorkflowRestImpl * * @param processId identifier of the process instance */ - protected void validateIfUserAllowedToWorkWithProcess(String processId) + protected List validateIfUserAllowedToWorkWithProcess(String processId) { - if (tenantService.isEnabled()) - { - try - { - String tenantDomain = (String) activitiProcessEngine.getRuntimeService().getVariable(processId, ActivitiConstants.VAR_TENANT_DOMAIN); - if (TenantUtil.getCurrentDomain().equals(tenantDomain) == false) - { - throw new PermissionDeniedException("Process is running in another tenant"); - } - } - catch (ActivitiObjectNotFoundException e) - { - throw new EntityNotFoundException(processId); - } - } + List variableInstances = activitiProcessEngine.getHistoryService() + .createHistoricVariableInstanceQuery() + .processInstanceId(processId) + .list(); - try + Map variableMap = new HashMap(); + if (variableInstances != null && variableInstances.size() > 0) { - ActivitiScriptNode initiator = (ActivitiScriptNode) activitiProcessEngine.getRuntimeService().getVariable(processId, WorkflowConstants.PROP_INITIATOR); - if (AuthenticationUtil.getRunAsUser().equals(initiator.getNodeRef().getId())) + for (HistoricVariableInstance variableInstance : variableInstances) { - // user is allowed - return; - } - } - catch (ActivitiObjectNotFoundException e) - { - throw new EntityNotFoundException(processId); - } - - HistoricTaskInstanceQuery query = activitiProcessEngine.getHistoryService() - .createHistoricTaskInstanceQuery() - .processInstanceId(processId); - - if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) - { - // Admin is allowed to read all processes in the current tenant - if (tenantService.isEnabled()) - { - query.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); - } - else - { - return; + variableMap.put(variableInstance.getVariableName(), variableInstance.getValue()); } } else { - // If non-admin user, involvement in the task is required (either owner, assignee or externally involved). - query.taskInvolvedUser(AuthenticationUtil.getRunAsUser()); + throw new EntityNotFoundException(processId); } - List taskList = query.list(); - - if(org.apache.commons.collections.CollectionUtils.isEmpty(taskList)) + if (tenantService.isEnabled()) { - throw new PermissionDeniedException("user is not allowed to access information about process " + processId); + String tenantDomain = (String) variableMap.get(ActivitiConstants.VAR_TENANT_DOMAIN); + if (TenantUtil.getCurrentDomain().equals(tenantDomain) == false) + { + throw new PermissionDeniedException("Process is running in another tenant"); + } } + + ActivitiScriptNode initiator = (ActivitiScriptNode) variableMap.get(WorkflowConstants.PROP_INITIATOR); + if (initiator != null && AuthenticationUtil.getRunAsUser().equals(initiator.getNodeRef().getId())) + { + // user is allowed + return variableInstances; + } + + if (authorityService.isAdminAuthority(AuthenticationUtil.getRunAsUser())) + { + // Admin is allowed to read all processes in the current tenant + return variableInstances; + } + else + { + // If non-admin user, involvement in the task is required (either owner, assignee or externally involved). + HistoricTaskInstanceQuery query = activitiProcessEngine.getHistoryService() + .createHistoricTaskInstanceQuery() + .processInstanceId(processId) + .taskInvolvedUser(AuthenticationUtil.getRunAsUser()); + + List taskList = query.list(); + + if (org.apache.commons.collections.CollectionUtils.isEmpty(taskList)) + { + throw new PermissionDeniedException("user is not allowed to access information about process " + processId); + } + } + + return variableInstances; } } diff --git a/source/java/org/alfresco/rest/workflow/api/model/Task.java b/source/java/org/alfresco/rest/workflow/api/model/Task.java index 571e7b72b0..4ba083ab10 100644 --- a/source/java/org/alfresco/rest/workflow/api/model/Task.java +++ b/source/java/org/alfresco/rest/workflow/api/model/Task.java @@ -90,9 +90,13 @@ public class Task this.assignee = taskInstance.getAssignee(); if (taskInstance.getDelegationState() == DelegationState.PENDING) { - + this.state = TaskStateTransition.DELEGATED.name().toLowerCase(); } - if (taskInstance.getAssignee() != null) + else if (taskInstance.getDelegationState() == DelegationState.RESOLVED) + { + this.state = TaskStateTransition.RESOLVED.name().toLowerCase(); + } + else if (taskInstance.getAssignee() != null) { this.state = TaskStateTransition.CLAIMED.name().toLowerCase(); } diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java b/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java index d2686c033d..b70f7c6013 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/EnterpriseWorkflowTestApi.java @@ -306,4 +306,43 @@ public class EnterpriseWorkflowTestApi extends EnterpriseTestApi return processesClient.createProcess(createProcessObject.toJSONString()); } + + /** + * Start a review pooled process through the public REST-API. + */ + @SuppressWarnings("unchecked") + protected ProcessInfo startParallelReviewProcess(final RequestContext requestContext) throws PublicApiException { + org.activiti.engine.repository.ProcessDefinition processDefinition = activitiProcessEngine + .getRepositoryService() + .createProcessDefinitionQuery() + .processDefinitionKey("@" + requestContext.getNetworkId() + "@activitiParallelReview") + .singleResult(); + + ProcessesClient processesClient = publicApiClient.processesClient(); + + final JSONObject createProcessObject = new JSONObject(); + createProcessObject.put("processDefinitionId", processDefinition.getId()); + + final JSONObject variablesObject = new JSONObject(); + variablesObject.put("bpm_priority", 1); + variablesObject.put("wf_notifyMe", Boolean.FALSE); + + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + JSONArray assigneeArray = new JSONArray(); + assigneeArray.add(requestContext.getRunAsUser()); + TestPerson otherPerson = getOtherPersonInNetwork(requestContext.getRunAsUser(), requestContext.getNetworkId()); + assigneeArray.add(otherPerson.getId()); + variablesObject.put("bpm_assignees", assigneeArray); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + createProcessObject.put("variables", variablesObject); + + return processesClient.createProcess(createProcessObject.toJSONString()); + } } diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java index 6450186ad8..f88aa28ad7 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessWorkflowApiTest.java @@ -223,6 +223,17 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi { final RequestContext requestContext = initApiClientWithTestUser(); final ProcessInfo processInfo = startReviewPooledProcess(requestContext); + assertNotNull(processInfo); + assertNotNull(processInfo.getId()); + } + + @Test + public void testCreateProcessInstanceForParallelReview() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + final ProcessInfo processInfo = startParallelReviewProcess(requestContext); + assertNotNull(processInfo); + assertNotNull(processInfo.getId()); } @Test @@ -458,7 +469,7 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi .createHistoricProcessInstanceQuery().processInstanceId(process.getId()).singleResult(); assertNotNull(deletedInstance); assertNotNull(deletedInstance.getEndTime()); - assertNull(deletedInstance.getDeleteReason()); + assertEquals("deleted through REST API call", deletedInstance.getDeleteReason()); } finally { @@ -480,7 +491,7 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi .createHistoricProcessInstanceQuery().processInstanceId(process.getId()).singleResult(); assertNotNull(deletedInstance); assertNotNull(deletedInstance.getEndTime()); - assertNull(deletedInstance.getDeleteReason()); + assertEquals("deleted through REST API call", deletedInstance.getDeleteReason()); } finally { diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java index e8e859e365..d145057518 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/TaskWorkflowApiTest.java @@ -227,7 +227,13 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi List selectedFields = new ArrayList(); selectedFields.addAll(Arrays.asList(new String[] { "name", "description", "dueAt", "priority", "assignee", "owner"})); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("Updated name", result.get("name")); + assertEquals("Updated description", result.get("description")); + assertEquals(1234, Integer.valueOf(result.get("priority").toString()).intValue()); + assertEquals("john", result.get("assignee")); + assertEquals("james", result.get("owner")); + assertEquals(dueDate, parseDate(result, "dueAt")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(task); @@ -277,7 +283,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().setAssignee(task.getId(), requestContext.getRunAsUser()); taskBody.put("name", "Updated name by assignee"); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("Updated name by assignee", result.get("name")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(task); assertEquals("Updated name by assignee", task.getName()); @@ -287,7 +294,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().setOwner(task.getId(), requestContext.getRunAsUser()); taskBody.put("name", "Updated name by owner"); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("Updated name by owner", result.get("name")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(task); assertEquals("Updated name by owner", task.getName()); @@ -295,7 +303,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Update as process initiator taskBody.put("name", "Updated name by initiator"); requestContext.setRunAsUser(initiator); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("Updated name by initiator", result.get("name")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(task); assertEquals("Updated name by initiator", task.getName()); @@ -305,7 +314,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi publicApiClient.setRequestContext(new RequestContext(TenantUtil.DEFAULT_TENANT, tenantAdmin)); taskBody.put("name", "Updated name by admin"); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("Updated name by admin", result.get("name")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertNotNull(task); assertEquals("Updated name by admin", task.getName()); @@ -441,11 +451,13 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi taskBody.put("state", "claimed"); JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); assertNotNull(result); + assertEquals(requestContext.getRunAsUser(), result.get("assignee")); assertEquals(requestContext.getRunAsUser(), activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Re-claiming the same task with the current assignee shouldn't be a problem result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); assertNotNull(result); + assertEquals(requestContext.getRunAsUser(), result.get("assignee")); assertEquals(requestContext.getRunAsUser(), activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Claiming as a candidateUser should also work @@ -454,6 +466,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().addCandidateUser(task.getId(), requestContext.getRunAsUser()); result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); assertNotNull(result); + assertEquals(requestContext.getRunAsUser(), result.get("assignee")); assertEquals(requestContext.getRunAsUser(), activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Claiming as a task owner should also work @@ -462,6 +475,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().deleteUserIdentityLink(task.getId(), requestContext.getRunAsUser(), IdentityLinkType.CANDIDATE); result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); assertNotNull(result); + assertEquals(requestContext.getRunAsUser(), result.get("assignee")); assertEquals(requestContext.getRunAsUser(), activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Claiming as admin should work @@ -472,6 +486,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().deleteUserIdentityLink(task.getId(), requestContext.getRunAsUser(), IdentityLinkType.CANDIDATE); result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); assertNotNull(result); + assertEquals(tenantAdmin, result.get("assignee")); assertEquals(tenantAdmin, activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); } @@ -514,21 +529,24 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Unclaiming as process initiator requestContext.setRunAsUser(initiator); activitiProcessEngine.getTaskService().setAssignee(task.getId(), null); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNull(result.get("assignee")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Unclaiming as assignee activitiProcessEngine.getTaskService().setAssignee(task.getId(), user); requestContext.setRunAsUser(user); assertNotNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNull(result.get("assignee")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Unclaim as owner activitiProcessEngine.getTaskService().setOwner(task.getId(), user); activitiProcessEngine.getTaskService().setAssignee(task.getId(), initiator); assertNotNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNull(result.get("assignee")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); // Unclaim as admin @@ -538,7 +556,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().setAssignee(task.getId(), initiator); activitiProcessEngine.getTaskService().deleteUserIdentityLink(task.getId(), requestContext.getRunAsUser(), IdentityLinkType.CANDIDATE); assertNotNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNull(result.get("assignee")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(task.getId()).singleResult().getAssignee()); } finally @@ -585,20 +604,26 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi // Completing as assignee initiator activitiProcessEngine.getTaskService().setAssignee(asAssigneeTask.getId(), user); - tasksClient.updateTask(asAssigneeTask.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(asAssigneeTask.getId(), taskBody, selectedFields); + assertEquals("completed", result.get("state")); + assertNotNull(result.get("endedAt")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(asAssigneeTask.getId()).singleResult()); // Completing as process initiator requestContext.setRunAsUser(initiator); activitiProcessEngine.getTaskService().setAssignee(asInitiatorTask.getId(), null); - tasksClient.updateTask(asInitiatorTask.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(asInitiatorTask.getId(), taskBody, selectedFields); + assertEquals("completed", result.get("state")); + assertNotNull(result.get("endedAt")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(asInitiatorTask.getId()).singleResult()); // Completing as owner requestContext.setRunAsUser(user); asOwnerTask.setOwner(user); activitiProcessEngine.getTaskService().saveTask(asOwnerTask); - tasksClient.updateTask(asOwnerTask.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(asOwnerTask.getId(), taskBody, selectedFields); + assertEquals("completed", result.get("state")); + assertNotNull(result.get("endedAt")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(asOwnerTask.getId()).singleResult()); // Complete as admin @@ -606,7 +631,9 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi publicApiClient.setRequestContext(new RequestContext(TenantUtil.DEFAULT_TENANT, tenantAdmin)); asAdminTask.setOwner(null); activitiProcessEngine.getTaskService().saveTask(asAdminTask); - tasksClient.updateTask(asAdminTask.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(asAdminTask.getId(), taskBody, selectedFields); + assertEquals("completed", result.get("state")); + assertNotNull(result.get("endedAt")); assertNull(activitiProcessEngine.getTaskService().createTaskQuery().taskId(asAdminTask.getId()).singleResult()); } finally @@ -669,7 +696,10 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi selectedFields = new ArrayList(); selectedFields.addAll(Arrays.asList(new String[] { "state", "assignee" })); assertNull(task.getDelegationState()); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("delegated", result.get("state")); + assertEquals(initiator, result.get("assignee")); + assertEquals(requestContext.getRunAsUser(), result.get("owner")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.PENDING, task.getDelegationState()); assertEquals(initiator, task.getAssignee()); @@ -680,7 +710,10 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi task.setOwner(requestContext.getRunAsUser()); task.setAssignee(null); activitiProcessEngine.getTaskService().saveTask(task); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("delegated", result.get("state")); + assertEquals(initiator, result.get("assignee")); + assertEquals(requestContext.getRunAsUser(), result.get("owner")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.PENDING, task.getDelegationState()); assertEquals(initiator, task.getAssignee()); @@ -693,7 +726,10 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().saveTask(task); requestContext.setRunAsUser(initiator); taskBody.put("assignee", user); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("delegated", result.get("state")); + assertEquals(user, result.get("assignee")); + assertEquals(initiator, result.get("owner")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.PENDING, task.getDelegationState()); assertEquals(user, task.getAssignee()); @@ -708,7 +744,10 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); requestContext.setRunAsUser(tenantAdmin); taskBody.put("assignee", user); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("delegated", result.get("state")); + assertEquals(user, result.get("assignee")); + assertEquals(tenantAdmin, result.get("owner")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.PENDING, task.getDelegationState()); assertEquals(user, task.getAssignee()); @@ -758,7 +797,9 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi taskBody.put("assignee", initiator); selectedFields = new ArrayList(); selectedFields.addAll(Arrays.asList(new String[] { "state", "assignee" })); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("resolved", result.get("state")); + assertEquals(initiator, result.get("assignee")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.RESOLVED, task.getDelegationState()); assertEquals(initiator, task.getAssignee()); @@ -768,7 +809,9 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi task.setOwner(requestContext.getRunAsUser()); task.setAssignee(null); activitiProcessEngine.getTaskService().saveTask(task); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("resolved", result.get("state")); + assertEquals(user, result.get("assignee")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.RESOLVED, task.getDelegationState()); assertEquals(user, task.getAssignee()); @@ -780,7 +823,8 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi activitiProcessEngine.getTaskService().saveTask(task); requestContext.setRunAsUser(initiator); taskBody.put("assignee", user); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("resolved", result.get("state")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.RESOLVED, task.getDelegationState()); @@ -793,7 +837,9 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); requestContext.setRunAsUser(tenantAdmin); taskBody.put("assignee", user); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertEquals("resolved", result.get("state")); + assertEquals(initiator, result.get("assignee")); task = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); assertEquals(DelegationState.RESOLVED, task.getDelegationState()); assertEquals(initiator, task.getAssignee()); @@ -825,14 +871,16 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi JSONObject taskBody = new JSONObject(); String dueAt = formatDate(new Date()); taskBody.put("dueAt", dueAt); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + JSONObject result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNotNull(result.get("dueAt")); taskObject = tasksClient.findTaskById(task.getId()); assertNotNull(taskObject.get("dueAt")); taskBody = new JSONObject(); taskBody.put("dueAt", taskObject.get("dueAt")); - tasksClient.updateTask(task.getId(), taskBody, selectedFields); + result = tasksClient.updateTask(task.getId(), taskBody, selectedFields); + assertNotNull(result.get("dueAt")); taskObject = tasksClient.findTaskById(task.getId()); assertNotNull(taskObject.get("dueAt"));