From a402cc75ed1a4855cc11750c982c3d20cc2dcde5 Mon Sep 17 00:00:00 2001 From: Tijs Rademakers Date: Wed, 2 Oct 2013 17:42:33 +0000 Subject: [PATCH] ALF-20190, ALF-20178 fixes for workflow rest api git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@56272 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/public-rest-context.xml | 1 + .../workflow/api/impl/DeploymentsImpl.java | 3 +- .../api/impl/ProcessDefinitionsImpl.java | 3 +- .../rest/workflow/api/impl/ProcessesImpl.java | 188 +++++++++------- .../workflow/api/impl/RestVariableHelper.java | 43 +--- .../rest/workflow/api/impl/TasksImpl.java | 202 ++++++++++++++---- .../api/impl/TypeDefinitionContext.java | 48 +++++ .../api/tests/DeploymentWorkflowApiTest.java | 53 +++++ .../ProcessDefinitionWorkflowApiTest.java | 52 +++++ .../api/tests/ProcessWorkflowApiTest.java | 200 +++++++++++++++++ .../api/tests/TaskWorkflowApiTest.java | 175 ++++++++++++++- .../workflow/api/tests/WorkflowApiClient.java | 14 ++ 12 files changed, 826 insertions(+), 156 deletions(-) create mode 100644 source/java/org/alfresco/rest/workflow/api/impl/TypeDefinitionContext.java diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index 699b7ba469..327e9fda76 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -882,6 +882,7 @@ + diff --git a/source/java/org/alfresco/rest/workflow/api/impl/DeploymentsImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/DeploymentsImpl.java index 3aa8ed2002..6ea90b0931 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/DeploymentsImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/DeploymentsImpl.java @@ -57,6 +57,7 @@ public class DeploymentsImpl extends WorkflowRestImpl implements Deployments query.orderByDeploymenTime().desc(); List deployments = query.listPage(paging.getSkipCount(), paging.getMaxItems()); + int totalCount = (int) query.count(); List page = new ArrayList(deployments.size()); for (org.activiti.engine.repository.Deployment deployment: deployments) @@ -64,7 +65,7 @@ public class DeploymentsImpl extends WorkflowRestImpl implements Deployments page.add(new Deployment(deployment)); } - return CollectionWithPagingInfo.asPaged(paging, page, false, page.size()); + return CollectionWithPagingInfo.asPaged(paging, page, page.size() != totalCount, totalCount); } @Override diff --git a/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java b/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java index 081f66b00a..9ba279306c 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/ProcessDefinitionsImpl.java @@ -224,6 +224,7 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD List processDefinitions = query.listPage(parameters.getPaging().getSkipCount(), parameters.getPaging().getMaxItems()); + int totalCount = (int) query.count(); List page = new ArrayList(processDefinitions.size()); for (org.activiti.engine.repository.ProcessDefinition processDefinition: processDefinitions) @@ -231,7 +232,7 @@ public class ProcessDefinitionsImpl extends WorkflowRestImpl implements ProcessD page.add(createProcessDefinitionRest((ProcessDefinitionEntity) processDefinition)); } - return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, false, page.size()); + return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, page.size() != totalCount, totalCount); } @Override 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 23c98b444f..a1897a46dc 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/ProcessesImpl.java @@ -607,74 +607,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes if (taskAssociations.containsKey(propNameMap.get(variableName))) { AssociationDefinition associationDef = taskAssociations.get(propNameMap.get(variableName)); - if (variableValue != null && ContentModel.TYPE_PERSON.equals(associationDef.getTargetClass().getName())) - { - 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())) - { - 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; - } - } + variableValue = convertAssociationDefinitionValue(associationDef, variableName, variableValue); } else if (taskProperties.containsKey(propNameMap.get(variableName))) { @@ -921,7 +854,7 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new EntityNotFoundException(processId); } - return updateVariableInProcess(processId, variable); + return updateVariableInProcess(processId, processInstance.getProcessDefinitionId(), variable); } @Override @@ -944,21 +877,45 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes { for (Variable variable : variables) { - updatedVariables.add(updateVariableInProcess(processId, variable)); + updatedVariables.add(updateVariableInProcess(processId, processInstance.getProcessDefinitionId(), variable)); } } return updatedVariables; } - protected Variable updateVariableInProcess(String processId,Variable variable) + protected Variable updateVariableInProcess(String processId, String processDefinitionId, Variable variable) { if (variable.getName() == null) { throw new InvalidArgumentException("Variable name is required."); } + // 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) + { + formKey = startFormData.getFormKey(); + } + DataTypeDefinition dataTypeDefinition = null; - if(variable.getType() != null) + + TypeDefinition startTaskTypeDefinition = getWorkflowFactory().getTaskFullTypeDefinition(formKey, true); + TypeDefinitionContext context = new TypeDefinitionContext(startTaskTypeDefinition, getQNameConverter()); + if (context.getPropertyDefinition(variable.getName()) != null) + { + dataTypeDefinition = context.getPropertyDefinition(variable.getName()).getDataType(); + if (variable.getType() != null && dataTypeDefinition.getName().toPrefixString(namespaceService).equals(variable.getType()) == false) { + throw new InvalidArgumentException("type of variable " + variable.getName() + " should be " + + dataTypeDefinition.getName().toPrefixString(namespaceService)); + } + } + else if (context.getAssociationDefinition(variable.getName()) != null) + { + dataTypeDefinition = dictionaryService.getDataType(DataTypeDefinition.NODE_REF); + } + + if (dataTypeDefinition == null && variable.getType() != null) { try { @@ -970,13 +927,13 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes throw new InvalidArgumentException("Unsupported type of variable: '" + variable.getType() +"'."); } } - else + else if (dataTypeDefinition == null) { // Fallback to raw value when no type has been passed and not present in model dataTypeDefinition = dictionaryService.getDataType(restVariableHelper.extractTypeFromValue(variable.getValue())); } - if(dataTypeDefinition == null) + if (dataTypeDefinition == null) { throw new InvalidArgumentException("Unsupported type of variable: '" + variable.getType() +"'."); } @@ -989,7 +946,15 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes } else { - actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, variable.getValue()); + if (context.getAssociationDefinition(variable.getName()) != null) + { + actualValue = convertAssociationDefinitionValue(context.getAssociationDefinition(variable.getName()), + variable.getName(), variable.getValue()); + } + else + { + actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, variable.getValue()); + } } variable.setValue(actualValue); @@ -1066,6 +1031,79 @@ public class ProcessesImpl extends WorkflowRestImpl implements Processes } } + protected Object convertAssociationDefinitionValue(AssociationDefinition associationDef, String variableName, Object variableValue) + { + if (variableValue != null && ContentModel.TYPE_PERSON.equals(associationDef.getTargetClass().getName())) + { + 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())) + { + 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; + } + } + return variableValue; + } + protected String getProcessDefinitionKey(String paramProcessDefinitionKey) { String processDefinitionKey = null; 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 39c0602c74..c25917b35d 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/RestVariableHelper.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -94,13 +93,13 @@ public class RestVariableHelper List result = new ArrayList(); if (localVariables != null && localVariables.size() > 0) { - TypeDefinitionContext context = new TypeDefinitionContext(taskTypeDefinition); + TypeDefinitionContext context = new TypeDefinitionContext(taskTypeDefinition, getQNameConverter()); addTaskVariables(result, localVariables, context, VariableScope.LOCAL); } if (globalVariables != null && globalVariables.size() > 0) { - TypeDefinitionContext context = new TypeDefinitionContext(startFormTypeDefinition); + TypeDefinitionContext context = new TypeDefinitionContext(startFormTypeDefinition, getQNameConverter()); addTaskVariables(result, globalVariables, context, VariableScope.GLOBAL); } @@ -115,7 +114,7 @@ public class RestVariableHelper public List getVariables(Map variables, TypeDefinition typeDefinition) { List result = new ArrayList(); - TypeDefinitionContext context = new TypeDefinitionContext(typeDefinition); + TypeDefinitionContext context = new TypeDefinitionContext(typeDefinition, getQNameConverter()); Variable variable = null; for(Entry entry : variables.entrySet()) @@ -372,40 +371,4 @@ public class RestVariableHelper QName type = extractTypeFromValue(value); return type.toPrefixString(namespaceService); } - - /** - * Helper contxt class used when checking variable types based on {@link TypeDefinition}. - * - * @author Frederik Heremans - */ - private class TypeDefinitionContext { - private Map propertyDefinitions; - private Map associationDefinitions; - - public TypeDefinitionContext(TypeDefinition typeDefinition) - { - propertyDefinitions = new HashMap(); - associationDefinitions = new HashMap(); - - for (Entry entry : typeDefinition.getProperties().entrySet()) - { - propertyDefinitions.put(getQNameConverter().mapQNameToName(entry.getKey()), entry.getValue()); - } - - for (Entry entry : typeDefinition.getAssociations().entrySet()) - { - associationDefinitions.put(getQNameConverter().mapQNameToName(entry.getKey()), entry.getValue()); - } - } - - public PropertyDefinition getPropertyDefinition(String rawVariableName) - { - return propertyDefinitions.get(rawVariableName); - } - - public AssociationDefinition getAssociationDefinition(String rawVariableName) - { - return associationDefinitions.get(rawVariableName); - } - } } 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 ec6d88138e..f32354550e 100644 --- a/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java +++ b/source/java/org/alfresco/rest/workflow/api/impl/TasksImpl.java @@ -36,6 +36,7 @@ import org.activiti.engine.task.DelegationState; import org.activiti.engine.task.IdentityLink; import org.activiti.engine.task.IdentityLinkType; import org.activiti.engine.task.TaskQuery; +import org.alfresco.model.ContentModel; import org.alfresco.repo.i18n.MessageService; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantUtil; @@ -65,10 +66,11 @@ import org.alfresco.rest.workflow.api.model.TaskVariable; import org.alfresco.rest.workflow.api.model.VariableScope; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -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.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.InvalidQNameException; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO8601DateFormat; @@ -123,6 +125,7 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks private WorkflowObjectFactory workflowFactory; private WorkflowQNameConverter qNameConverter; private MessageService messageService; + private PersonService personService; public void setRestVariableHelper(RestVariableHelper restVariableHelper) { @@ -134,6 +137,11 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks this.messageService = messageService; } + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + @Override public CollectionWithPagingInfo getTasks(Parameters parameters) { @@ -1054,9 +1062,63 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { throw new InvalidArgumentException("Variable scope is required and can only be 'local' or 'global'."); } - + DataTypeDefinition dataTypeDefinition = null; - if (taskVariable.getType() != null) + TypeDefinitionContext context = null; + if (taskVariable.getVariableScope() == VariableScope.GLOBAL) + { + // Get start-task definition for explicit typing of variables submitted at the start + String formKey = null; + StartFormData startFormData = activitiProcessEngine.getFormService().getStartFormData(taskInstance.getProcessDefinitionId()); + if (startFormData != null) + { + formKey = startFormData.getFormKey(); + } + + TypeDefinition startTaskTypeDefinition = getWorkflowFactory().getTaskFullTypeDefinition(formKey, true); + context = new TypeDefinitionContext(startTaskTypeDefinition, getQNameConverter()); + if (context.getPropertyDefinition(taskVariable.getName()) != null) + { + dataTypeDefinition = context.getPropertyDefinition(taskVariable.getName()).getDataType(); + if (taskVariable.getType() != null && dataTypeDefinition.getName().toPrefixString(namespaceService).equals(taskVariable.getType()) == false) { + throw new InvalidArgumentException("type of variable " + taskVariable.getName() + " should be " + + dataTypeDefinition.getName().toPrefixString(namespaceService)); + } + } + else if (context.getAssociationDefinition(taskVariable.getName()) != null) + { + dataTypeDefinition = dictionaryService.getDataType(DataTypeDefinition.NODE_REF); + } + } + else + { + // Revert to either the content-model type or the raw type provided by the request + try + { + String formKey = activitiProcessEngine.getFormService().getTaskFormKey(taskInstance.getProcessDefinitionId(), taskInstance.getTaskDefinitionKey()); + TypeDefinition typeDefinition = getWorkflowFactory().getTaskFullTypeDefinition(formKey, false); + context = new TypeDefinitionContext(typeDefinition, getQNameConverter()); + if (context.getPropertyDefinition(taskVariable.getName()) != null) + { + dataTypeDefinition = context.getPropertyDefinition(taskVariable.getName()).getDataType(); + if (taskVariable.getType() != null && dataTypeDefinition.getName().toPrefixString(namespaceService).equals(taskVariable.getType()) == false) { + throw new InvalidArgumentException("type of variable " + taskVariable.getName() + " should be " + + dataTypeDefinition.getName().toPrefixString(namespaceService)); + } + } + else if (context.getAssociationDefinition(taskVariable.getName()) != null) + { + dataTypeDefinition = dictionaryService.getDataType(DataTypeDefinition.NODE_REF); + } + } + 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 && taskVariable.getType() != null) { try { @@ -1067,41 +1129,11 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks { throw new InvalidArgumentException("Unsupported type of variable: '" + taskVariable.getType() +"'."); } - } - else + } + else if (dataTypeDefinition == null) { - // Revert to either the content-model type or the raw type provided by the request - try - { - String formKey = activitiProcessEngine.getFormService().getTaskFormKey(taskInstance.getProcessDefinitionId(), taskInstance.getTaskDefinitionKey()); - TypeDefinition typeDefinition = getWorkflowFactory().getTaskFullTypeDefinition(formKey, false); - QName propQName = WorkflowQNameConverter.convertNameToQName(taskVariable.getName(), namespaceService); - - PropertyDefinition propDef = typeDefinition.getProperties().get(propQName); - if (propDef != null) - { - dataTypeDefinition = propDef.getDataType(); - } - else - { - AssociationDefinition assocDef = typeDefinition.getAssociations().get(propQName); - if(assocDef != null) - { - dataTypeDefinition = dictionaryService.getDataType(DataTypeDefinition.NODE_REF); - } - } - } - 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) - { - // Final fallback to raw value when no type has been passed and not present in model - dataTypeDefinition = dictionaryService.getDataType(restVariableHelper.extractTypeFromValue(taskVariable.getValue())); - } + // Final fallback to raw value when no type has been passed and not present in model + dataTypeDefinition = dictionaryService.getDataType(restVariableHelper.extractTypeFromValue(taskVariable.getValue())); } if (dataTypeDefinition == null) @@ -1117,7 +1149,15 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } else { - actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, taskVariable.getValue()); + if (context != null && context.getAssociationDefinition(taskVariable.getName()) != null) + { + actualValue = convertAssociationDefinitionValue(context.getAssociationDefinition(taskVariable.getName()), + taskVariable.getName(), taskVariable.getValue()); + } + else + { + actualValue = DefaultTypeConverter.INSTANCE.convert(dataTypeDefinition, taskVariable.getValue()); + } } taskVariable.setValue(actualValue); @@ -1556,6 +1596,92 @@ public class TasksImpl extends WorkflowRestImpl implements Tasks } } + protected Object convertAssociationDefinitionValue(AssociationDefinition associationDef, String variableName, Object variableValue) + { + if (variableValue != null && ContentModel.TYPE_PERSON.equals(associationDef.getTargetClass().getName())) + { + 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())) + { + 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; + } + } + return variableValue; + } + + protected NodeRef getPersonNodeRef(String name) + { + NodeRef authority = null; + if (name != null) + { + if (personService.personExists(name)) + { + authority = personService.getPerson(name); + } + } + return authority; + } + protected WorkflowQNameConverter getQNameConverter() { if (qNameConverter == null) diff --git a/source/java/org/alfresco/rest/workflow/api/impl/TypeDefinitionContext.java b/source/java/org/alfresco/rest/workflow/api/impl/TypeDefinitionContext.java new file mode 100644 index 0000000000..3b4a9fbfb6 --- /dev/null +++ b/source/java/org/alfresco/rest/workflow/api/impl/TypeDefinitionContext.java @@ -0,0 +1,48 @@ +package org.alfresco.rest.workflow.api.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.alfresco.repo.workflow.WorkflowQNameConverter; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.QName; + +/** + * Helper contxt class used when checking variable types based on {@link TypeDefinition}. + * + * @author Frederik Heremans + */ +public class TypeDefinitionContext { + + private Map propertyDefinitions; + private Map associationDefinitions; + + public TypeDefinitionContext(TypeDefinition typeDefinition, WorkflowQNameConverter qNameConverter) + { + propertyDefinitions = new HashMap(); + associationDefinitions = new HashMap(); + + for (Entry entry : typeDefinition.getProperties().entrySet()) + { + propertyDefinitions.put(qNameConverter.mapQNameToName(entry.getKey()), entry.getValue()); + } + + for (Entry entry : typeDefinition.getAssociations().entrySet()) + { + associationDefinitions.put(qNameConverter.mapQNameToName(entry.getKey()), entry.getValue()); + } + } + + public PropertyDefinition getPropertyDefinition(String rawVariableName) + { + return propertyDefinitions.get(rawVariableName); + } + + public AssociationDefinition getAssociationDefinition(String rawVariableName) + { + return associationDefinitions.get(rawVariableName); + } +} diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java index 3bfaa44125..a6a18b1154 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/DeploymentWorkflowApiTest.java @@ -38,6 +38,7 @@ import org.alfresco.rest.api.tests.client.PublicApiException; import org.alfresco.rest.api.tests.client.RequestContext; import org.alfresco.rest.workflow.api.model.Deployment; import org.alfresco.rest.workflow.api.tests.WorkflowApiClient.DeploymentsClient; +import org.json.simple.JSONObject; import org.junit.Test; import org.springframework.http.HttpStatus; @@ -120,6 +121,58 @@ public class DeploymentWorkflowApiTest extends EnterpriseWorkflowTestApi assertEquals(activitiDeployment.getCategory(), adhocDeployment.getCategory()); assertEquals(activitiDeployment.getName(), adhocDeployment.getName()); assertEquals(activitiDeployment.getDeploymentTime(), adhocDeployment.getDeployedAt()); + + Map params = new HashMap(); + params.put("maxItems", "2"); + JSONObject deploymentsListObject = deploymentsClient.getDeploymentsWithRawResponse(params); + assertNotNull(deploymentsListObject); + JSONObject paginationJSON = (JSONObject) deploymentsListObject.get("pagination"); + assertEquals(2l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + deploymentsListObject = deploymentsClient.getDeploymentsWithRawResponse(params); + assertNotNull(deploymentsListObject); + paginationJSON = (JSONObject) deploymentsListObject.get("pagination"); + assertEquals(5l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(false, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "2"); + params.put("maxItems", "2"); + deploymentsListObject = deploymentsClient.getDeploymentsWithRawResponse(params); + assertNotNull(deploymentsListObject); + paginationJSON = (JSONObject) deploymentsListObject.get("pagination"); + assertEquals(2l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(2l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "2"); + params.put("maxItems", "5"); + deploymentsListObject = deploymentsClient.getDeploymentsWithRawResponse(params); + assertNotNull(deploymentsListObject); + paginationJSON = (JSONObject) deploymentsListObject.get("pagination"); + assertEquals(3l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(2l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "0"); + params.put("maxItems", "7"); + deploymentsListObject = deploymentsClient.getDeploymentsWithRawResponse(params); + assertNotNull(deploymentsListObject); + paginationJSON = (JSONObject) deploymentsListObject.get("pagination"); + assertEquals(5l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(false, paginationJSON.get("hasMoreItems")); } @Test diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java index a62a30a42f..862dd4425e 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/ProcessDefinitionWorkflowApiTest.java @@ -88,6 +88,58 @@ public class ProcessDefinitionWorkflowApiTest extends EnterpriseWorkflowTestApi assertEquals("wf:submitAdhocTask", adhocDefinitionRest.getStartFormResourceKey()); assertEquals("New Task", adhocDefinitionRest.getTitle()); assertEquals("Assign a new task to yourself or a colleague", adhocDefinitionRest.getDescription()); + + Map params = new HashMap(); + params.put("maxItems", "2"); + JSONObject definitionListObject = processDefinitionsClient.getProcessDefinitionsWithRawResponse(params); + assertNotNull(definitionListObject); + JSONObject paginationJSON = (JSONObject) definitionListObject.get("pagination"); + assertEquals(2l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + definitionListObject = processDefinitionsClient.getProcessDefinitionsWithRawResponse(params); + assertNotNull(definitionListObject); + paginationJSON = (JSONObject) definitionListObject.get("pagination"); + assertEquals(5l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(false, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "2"); + params.put("maxItems", "2"); + definitionListObject = processDefinitionsClient.getProcessDefinitionsWithRawResponse(params); + assertNotNull(definitionListObject); + paginationJSON = (JSONObject) definitionListObject.get("pagination"); + assertEquals(2l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(2l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "2"); + params.put("maxItems", "5"); + definitionListObject = processDefinitionsClient.getProcessDefinitionsWithRawResponse(params); + assertNotNull(definitionListObject); + paginationJSON = (JSONObject) definitionListObject.get("pagination"); + assertEquals(3l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(2l, paginationJSON.get("skipCount")); + assertEquals(true, paginationJSON.get("hasMoreItems")); + + params = new HashMap(); + params.put("skipCount", "0"); + params.put("maxItems", "7"); + definitionListObject = processDefinitionsClient.getProcessDefinitionsWithRawResponse(params); + assertNotNull(definitionListObject); + paginationJSON = (JSONObject) definitionListObject.get("pagination"); + assertEquals(5l, paginationJSON.get("count")); + assertEquals(5l, paginationJSON.get("totalItems")); + assertEquals(0l, paginationJSON.get("skipCount")); + assertEquals(false, paginationJSON.get("hasMoreItems")); } @Test 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 a27ffdaceb..eaa01085b8 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 @@ -1749,6 +1749,25 @@ public class ProcessWorkflowApiTest extends EnterpriseWorkflowTestApi assertEquals("d:long", result.get("type")); assertEquals(4567L, activitiProcessEngine.getRuntimeService().getVariable(processId, "newVariable")); + JSONObject processvariables = publicApiClient.processesClient().getProcessvariables(processId); + assertNotNull(processvariables); + JSONObject newVariableEntry = null; + JSONArray entries = (JSONArray) processvariables.get("entries"); + assertNotNull(entries); + for(int i=0; i variablesByName = new HashMap(); + JSONObject entry = null; + JSONArray entries = (JSONArray) processvariables.get("entries"); + assertNotNull(entries); + for(int i=0; i() + { + @Override + public Void doWork() throws Exception + { + JSONArray assigneeArray = new JSONArray(); + assigneeArray.add(requestContext.getRunAsUser()); + updateAssigneesJson.put("value", assigneeArray); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + resultEntry = publicApiClient.processesClient().updateVariable(processId, "bpm_assignees", updateAssigneesJson); + assertNotNull(resultEntry); + final JSONObject updateAssigneeResult = (JSONObject) resultEntry.get("entry"); + + assertEquals("bpm_assignees", updateAssigneeResult.get("name")); + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + JSONArray assigneeArray = (JSONArray) updateAssigneeResult.get("value"); + assertNotNull(assigneeArray); + assertEquals(1, assigneeArray.size()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + assertEquals("d:noderef", updateAssigneeResult.get("type")); + + // update the bpm_assignees with a single entry, should result in an error + final JSONObject updateAssigneeJson = new JSONObject(); + updateAssigneeJson.put("name", "bpm_assignees"); + updateAssigneeJson.put("type", "d:noderef"); + + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + updateAssigneeJson.put("value", requestContext.getRunAsUser()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + try + { + publicApiClient.processesClient().updateVariable(processId, "bpm_assignees", updateAssigneeJson); + fail("Exception expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + + // change the variable value with a non-existing person + variableJson = new JSONObject(); + variableJson.put("name", "bpm_assignees"); + JSONArray assigneeArray = new JSONArray(); + assigneeArray.add("nonExistingPerson"); + variableJson.put("value", assigneeArray); + variableJson.put("type", "d:noderef"); + + try + { + publicApiClient.processesClient().updateVariable(processId, "bpm_assignees", variableJson); + fail("Exception expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + + } + finally + { + cleanupProcessInstance(processRest.getId()); + } + } + @Test public void testDeleteProcessVariable() throws Exception { 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 9c219930b6..6980eb5d8b 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 @@ -1106,7 +1106,7 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi JSONObject variableBody = new JSONObject(); variableBody.put("name", "bpm_workflowDueDate"); variableBody.put("value", formatDate(new Date())); - variableBody.put("type", "d:datetime"); + variableBody.put("type", "d:date"); variableBody.put("scope", "global"); tasksClient.updateTaskVariable(task.getId(), "bpm_workflowDueDate", variableBody); @@ -2397,6 +2397,179 @@ public class TaskWorkflowApiTest extends EnterpriseWorkflowTestApi } } + @SuppressWarnings("unchecked") + @Test + public void testUpdateTaskVariableWithWrongType() throws Exception + { + final RequestContext requestContext = initApiClientWithTestUser(); + + ProcessInfo processRest = startParallelReviewProcess(requestContext); + + try + { + List tasks = activitiProcessEngine.getTaskService().createTaskQuery().processInstanceId(processRest.getId()).list(); + assertNotNull(tasks); + + String taskId = tasks.get(0).getId(); + + // Update an existing variable with wrong type + JSONObject variableJson = new JSONObject(); + variableJson.put("name", "wf_requiredApprovePercent"); + variableJson.put("value", 55.99); + variableJson.put("type", "d:double"); + variableJson.put("scope", "global"); + + try + { + publicApiClient.tasksClient().updateTaskVariable(taskId, "wf_requiredApprovePercent", variableJson); + fail("Exception expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + + variableJson = new JSONObject(); + variableJson.put("name", "wf_requiredApprovePercent"); + variableJson.put("value", 55.99); + variableJson.put("type", "d:int"); + variableJson.put("scope", "global"); + + JSONObject resultEntry = publicApiClient.tasksClient().updateTaskVariable(taskId, "wf_requiredApprovePercent", variableJson); + assertNotNull(resultEntry); + JSONObject result = (JSONObject) resultEntry.get("entry"); + + assertEquals("wf_requiredApprovePercent", result.get("name")); + assertEquals(55l, result.get("value")); + assertEquals("d:int", result.get("type")); + assertEquals(55, activitiProcessEngine.getRuntimeService().getVariable(processRest.getId(), "wf_requiredApprovePercent")); + + JSONObject taskVariables = publicApiClient.tasksClient().findTaskVariables(taskId); + assertNotNull(taskVariables); + JSONObject list = (JSONObject) taskVariables.get("list"); + assertNotNull(list); + + // Add process variables to map for easy lookup + Map variablesByName = new HashMap(); + JSONObject entry = null; + JSONArray entries = (JSONArray) list.get("entries"); + assertNotNull(entries); + for(int i=0; i() + { + @Override + public Void doWork() throws Exception + { + JSONArray assigneeArray = new JSONArray(); + assigneeArray.add(requestContext.getRunAsUser()); + updateAssigneesJson.put("value", assigneeArray); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + resultEntry = publicApiClient.tasksClient().updateTaskVariable(taskId, "bpm_assignees", updateAssigneesJson); + assertNotNull(resultEntry); + final JSONObject updateAssigneeResult = (JSONObject) resultEntry.get("entry"); + + assertEquals("bpm_assignees", updateAssigneeResult.get("name")); + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + JSONArray assigneeArray = (JSONArray) updateAssigneeResult.get("value"); + assertNotNull(assigneeArray); + assertEquals(1, assigneeArray.size()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + assertEquals("d:noderef", updateAssigneeResult.get("type")); + + // update the bpm_assignees with a single entry, should result in an error + final JSONObject updateAssigneeJson = new JSONObject(); + updateAssigneeJson.put("name", "bpm_assignees"); + updateAssigneeJson.put("type", "d:noderef"); + updateAssigneeJson.put("scope", "global"); + + TenantUtil.runAsUserTenant(new TenantRunAsWork() + { + @Override + public Void doWork() throws Exception + { + updateAssigneeJson.put("value", requestContext.getRunAsUser()); + return null; + } + }, requestContext.getRunAsUser(), requestContext.getNetworkId()); + + try + { + publicApiClient.tasksClient().updateTaskVariable(taskId, "bpm_assignees", updateAssigneeJson); + fail("Exception expected"); + } + catch (PublicApiException e) + { + assertEquals(HttpStatus.BAD_REQUEST.value(), e.getHttpResponse().getStatusCode()); + } + + } + finally + { + cleanupProcessInstance(processRest.getId()); + } + } + @Test @SuppressWarnings("unchecked") public void testUpdateTaskVariablesAuthentication() throws Exception diff --git a/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java index 28bc2ca55a..6d6503082d 100644 --- a/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java +++ b/source/test-java/org/alfresco/rest/workflow/api/tests/WorkflowApiClient.java @@ -69,6 +69,13 @@ public class WorkflowApiClient extends PublicApiClient HttpResponse response = getAll("deployments", null, null, null, params, "Failed to get deploymentsClient"); return DeploymentParser.INSTANCE.parseList(response.getJsonResponse()); } + + public JSONObject getDeploymentsWithRawResponse(Map params) throws PublicApiException + { + HttpResponse response = getAll("deployments", null, null, null, params, "Failed to get deploymentsClient"); + JSONObject list = (JSONObject) response.getJsonResponse().get("list"); + return list; + } public ListResponse getDeployments() throws PublicApiException { @@ -106,6 +113,13 @@ public class WorkflowApiClient extends PublicApiClient HttpResponse response = getAll("process-definitions", null, null, null, params, "Failed to get process definitions"); return ProcessDefinitionParser.INSTANCE.parseList(response.getJsonResponse()); } + + public JSONObject getProcessDefinitionsWithRawResponse(Map params) throws PublicApiException + { + HttpResponse response = getAll("process-definitions", null, null, null, params, "Failed to get process definitions"); + JSONObject list = (JSONObject) response.getJsonResponse().get("list"); + return list; + } public ProcessDefinition findProcessDefinitionById(String processDefinitionId) throws PublicApiException {