From bf94a672dded4286c7bf24b1c9e5b6a70bb90084 Mon Sep 17 00:00:00 2001 From: N Smith Date: Mon, 5 Jul 2010 13:14:40 +0000 Subject: [PATCH] Created api/task-instances GET for Workflow REST API. Gets a list of task instances for specified authority. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@20933 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../workflow/task-instances.get.desc.xml | 18 ++ .../workflow/task-instances.get.json.ftl | 11 + .../alfresco/repository/workflow/task.lib.ftl | 50 +++++ .../web-scripts-application-context.xml | 27 ++- .../workflow/AbstractWorkflowWebscript.java | 83 ++++++++ .../scripts/workflow/TaskInstancesGet.java | 123 ++++++++++++ .../workflow/WorkflowModelBuilder.java | 190 ++++++++++++++++++ .../workflow/WorkflowModelBuilderTest.java | 156 ++++++++++++++ .../scripts/workflow/WorkflowRestApiTest.java | 155 ++++++++++++++ 9 files changed, 810 insertions(+), 3 deletions(-) create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.desc.xml create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.json.ftl create mode 100644 config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl create mode 100644 source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java create mode 100644 source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java create mode 100644 source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java create mode 100644 source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java create mode 100644 source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.desc.xml new file mode 100644 index 0000000000..08769cad29 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.desc.xml @@ -0,0 +1,18 @@ + + List Workflow Tasks + + Lists all Workflow Task Instances associated with an authority and of a given State. + The list of returned tasks also includes pooled tasks which the specified authority is eligible to claim. + + /api/task-instance + /api/task-instance?autority={authority?} + /api/task-instance?state={state?} + /api/task-instance?properties={properties?} + /api/task-instance?autority={authority?}&?state={state?} + /api/task-instance?authority={authority?}&?properties={properties?} + /api/task-instance?state={state?}&?properties={properties?} + /api/task-instance?authority={authority?}&?state={state?}&?properties={properties?} + + user + required + \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.json.ftl new file mode 100644 index 0000000000..d6eb9daa20 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task-instances.get.json.ftl @@ -0,0 +1,11 @@ +<#-- List Workflow Task Instances --> + +<#import "task.lib.ftl" as taskLib /> +{ + "data": [ + <#list taskInstances as task> + <@taskLib.taskJSON task=task /> + <#if task_has_next>, + + ] +} \ No newline at end of file diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl new file mode 100644 index 0000000000..d692c6a229 --- /dev/null +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/workflow/task.lib.ftl @@ -0,0 +1,50 @@ +<#-- Renders a task instance. --> +<#macro taskJSON task > + { + "url": "${task.url}", + "name": "${task.name}", + "title": "${task.title}", + "description": "${task.description}", + "state": "${task.state}", + "typeDefinitionTitle": "${task.typeDefinitionTitle}", + "isPooled": ${task.isPooled?string}, + <#if task.owner??> + "owner": + { + "userName": "${task.owner.userName}", + "firstName": "${task.owner.firstName}", + "lastName": "${task.owner.lastName}" + }, + + "properties": + <@propertiesJSON properties=task.properties /> + } + + +<#-- Renders a map of properties --> +<#macro propertiesJSON properties> + { + <#list properties?keys as key> + <#if properties[key]??> + <#assign val=properties[key]> + "${key}" : + <#if val?is_boolean == true> + ${val?string} + <#elseif val?is_number == true> + ${val?c} + <#elseif val?is_sequence> + [ + <#list val as element> + "${jsonUtils.encodeJSONString(element?string)}"<#if (element_has_next)>, + + ] + <#else> + "${jsonUtils.encodeJSONString(shortQName(val?string))}" + + <#if (key_has_next)>, + + + } + + + \ No newline at end of file diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 0f18bbd28e..1c7c3e88e4 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -731,7 +731,28 @@ - - - + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java new file mode 100644 index 0000000000..1be1b37676 --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowWebscript.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.workflow; + +import java.util.Map; + +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.namespace.NamespaceService; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * @author Nick Smith + * + */ +public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript +{ + + private NamespaceService namespaceService; + private NodeService nodeService; + private PersonService personService; + protected WorkflowService workflowService; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + WorkflowModelBuilder modelBuilder = new WorkflowModelBuilder(namespaceService, nodeService, personService); + return buildModel(modelBuilder, req, status, cache); + } + + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + /** + * This method uses a {@link WorkflowModelBuilder} to build up the model to return. + * @param modelBuilder A {@link WorkflowModelBuilder}. + * @param req the {@link WebScriptRequest} + * @param status the {@link Status} + * @param cache the {@link Cache} + * @return the data model. + */ + protected abstract Map buildModel( + WorkflowModelBuilder modelBuilder, + WebScriptRequest req, + Status status, Cache cache); +} diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java b/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java new file mode 100644 index 0000000000..8eef0f8f6e --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.workflow; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * @author Nick Smith + * + */ +public class TaskInstancesGet extends AbstractWorkflowWebscript +{ + public static final String PARAM_AUTHORITY = "authority"; + public static final String PARAM_STATUS= "status"; + public static final String PARAM_PROPERTIES= "properties"; + + @Override + protected Map buildModel(WorkflowModelBuilder modelBuilder, WebScriptRequest req, Status status, + Cache cache) + { + String authority = getAuthority(req); + WorkflowTaskState state = getState(req); + List properties = getProperties(req); + + //TODO Handle possible thrown exceptions here? + List tasks = workflowService.getAssignedTasks(authority, state); + List pooledTasks= workflowService.getPooledTasks(authority); + ArrayList allTasks = new ArrayList(tasks.size() + pooledTasks.size()); + allTasks.addAll(tasks); + allTasks.addAll(pooledTasks); + + ArrayList> results = new ArrayList>(); + for (WorkflowTask task : allTasks) + { + results.add(modelBuilder.buildSimple(task, properties)); + } + + + Map model = new HashMap(); + model.put("taskInstances", results); + return model; + } + + private List getProperties(WebScriptRequest req) + { + String propertiesStr = req.getParameter(PARAM_PROPERTIES); + if(propertiesStr != null) + { + return Arrays.asList(propertiesStr.split(",")); + } + return null; + } + + /** + * Gets the specified {@link WorkflowTaskState}, defaults to IN_PROGRESS. + * @param req + * @return + */ + private WorkflowTaskState getState(WebScriptRequest req) + { + String stateName= req.getParameter(PARAM_STATUS); + if(stateName != null) + { + try + { + return WorkflowTaskState.valueOf(stateName.toUpperCase()); + } + catch(IllegalArgumentException e) + { + String msg = "Unrecognised State parameter: "+stateName; + throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, msg); + } + } + // Defaults to IN_PROGRESS. + return WorkflowTaskState.IN_PROGRESS; + } + + /** + * Returns the specified authority. If no authority is specified then returns the current Fully Authenticated user. + * @param req + * @return + */ + private String getAuthority(WebScriptRequest req) + { + String authority = req.getParameter(PARAM_AUTHORITY); + if(authority == null) + { + authority = AuthenticationUtil.getFullyAuthenticatedUser(); + } + return authority; + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java new file mode 100644 index 0000000000..2fb1b25fbb --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilder.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.workflow; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Nick Smith + * + */ +public class WorkflowModelBuilder +{ + public static final String PERSON_LAST_NAME = "lastName"; + public static final String PERSON_FIRST_NAME = "firstName"; + public static final String PERSON_USER_NAME = "userName"; + + public static final String TASK_PROPERTIES = "properties"; + public static final String TASK_OWNER = "owner"; + public static final String TASK_TYPE_DEFINITION_TITLE = "typeDefinitionTitle"; + public static final String TASK_STATE = "state"; + public static final String TASK_DESCRIPTION = "description"; + public static final String TASK_TITLE = "title"; + public static final String TASK_NAME = "name"; + public static final String TASK_URL = "url"; + public static final String TASK_IS_POOLED = "isPooled"; + + private static final String PREFIX_SEPARATOR = Character.toString(QName.NAMESPACE_PREFIX); + + private final NamespaceService namespaceService; + private final NodeService nodeService; + private final PersonService personService; + + public WorkflowModelBuilder(NamespaceService namespaceService, NodeService nodeService, PersonService personService) + { + this.namespaceService = namespaceService; + this.nodeService = nodeService; + this.personService = personService; + } + + /** + * Returns a simple representation of a {@link WorkflowTask}. + * @param task The task to be represented. + * @param propertyFilters Specify which properties to include. + * @return + */ + public Map buildSimple(WorkflowTask task, Collection propertyFilters) + { + HashMap model = new HashMap(); + model.put(TASK_URL, getUrl(task)); + model.put(TASK_NAME, task.name); + model.put(TASK_TITLE, task.title); + model.put(TASK_DESCRIPTION, task.description); + model.put(TASK_STATE, task.state.name()); + model.put(TASK_TYPE_DEFINITION_TITLE, task.definition.metadata.getTitle()); + + model.put(TASK_IS_POOLED, isPooled(task.properties)); + Serializable owner = task.properties.get(ContentModel.PROP_OWNER); + if (owner != null && owner instanceof String) { + model.put(TASK_OWNER, getPersonModel((String) owner)); + } + + model.put(TASK_PROPERTIES, buildProperties(task.properties, propertyFilters)); + return model; + } + + private Object isPooled(Map properties) + { + Collection actors = (Collection) properties.get(WorkflowModel.ASSOC_POOLED_ACTORS); + return actors!=null && !actors.isEmpty(); + } + + private Map buildProperties(Map properties, Collection propertyFilters) + { + Collection keys; + if (propertyFilters == null || propertyFilters.size() == 0) + keys = properties.keySet(); + else + keys = buildQNameKeys(propertyFilters); + Map model = new HashMap(); + for (QName key : keys) + { + Object value = convertValue(properties.get(key)); + String preixedKey = key.toPrefixString(namespaceService); + String strKey = preixedKey.replace(PREFIX_SEPARATOR, "_"); + model.put(strKey, value); + } + return model; + } + + private Object convertValue(Object value) + { + if(value == null + || value instanceof Boolean + || value instanceof Number + || value instanceof String) + { + return value; + } + if(value instanceof Collection) + { + Collection collection = (Collection)value; + ArrayList results = new ArrayList(collection.size()); + for (Object obj : collection) + { + results.add(convertValue(obj)); + } + return results; + } + return DefaultTypeConverter.INSTANCE.convert(String.class, value); + } + + private Collection buildQNameKeys(Collection keys) + { + List qKeys = new ArrayList(keys.size()); + for (String key : keys) + { + String prefixedName = key.replaceFirst("_", PREFIX_SEPARATOR); + try + { + QName qKey = QName.createQName(prefixedName, namespaceService); + qKeys.add(qKey); + } catch(NamespaceException e) + { + throw new AlfrescoRuntimeException("Invalid property key: "+key, e); + } + } + return qKeys; + } + + private Map getPersonModel(String name) + { + NodeRef person = personService.getPerson(name); + Map properties = nodeService.getProperties(person); + + // TODO Person URL? + Map model = new HashMap(); + model.put(PERSON_USER_NAME, name); + model.put(PERSON_FIRST_NAME, properties.get(ContentModel.PROP_FIRSTNAME)); + model.put(PERSON_LAST_NAME, properties.get(ContentModel.PROP_LASTNAME)); + return model; + } + +// private String getURl(WorkflowPath path) +// { +// StringBuilder builder = new StringBuilder("api/workflow-instances/"); +// builder.append(path.instance.id); +// builder.append("/paths/"); +// builder.append(path.id); +// return builder.toString(); +// } + + private String getUrl(WorkflowTask task) + { + return "api/task-instance/" + task.id; + } + +} diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java new file mode 100644 index 0000000000..6839dfd4fc --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowModelBuilderTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.workflow; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +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.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.NamespaceServiceMemoryImpl; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ISO8601DateFormat; + +/** + * @author Nick Smith + * + */ +public class WorkflowModelBuilderTest extends TestCase +{ + private static final String URI = "http://test"; + private static final String userName = "joeBloggs"; + private static final String firstName = "Joe"; + private static final String lastName = "Bloggs"; + private static final NodeRef person = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, userName); + + private NamespaceService namespaceService; + private PersonService personService; + private NodeService nodeService; + private WorkflowModelBuilder builder; + + @SuppressWarnings("unchecked") + public void testBuildWorkflowTask() throws Exception + { + WorkflowTask task = new WorkflowTask(); + task.definition = new WorkflowTaskDefinition(); + task.definition.metadata = mock(TypeDefinition.class); + when(task.definition.metadata.getTitle()).thenReturn("Type Title"); + task.description = "Task Desc"; + task.id = "testId$1"; + task.name = "Task Name"; + task.state = WorkflowTaskState.IN_PROGRESS; + task.title = "Task Title"; + task.properties = new HashMap(); + task.properties.put(ContentModel.PROP_OWNER, userName); + + QName testInt = QName.createQName(URI, "int"); + task.properties.put(testInt, 5); + QName testBoolean = QName.createQName(URI, "boolean"); + task.properties.put(testBoolean, false); + QName testString = QName.createQName(URI, "string"); + task.properties.put(testString, "foo bar"); + QName testDate = QName.createQName(URI, "date"); + Date date = new Date(); + task.properties.put(testDate, date); + + Map model = builder.buildSimple(task, null); + Object url = model.get(WorkflowModelBuilder.TASK_URL); + assertEquals("api/task-instance/"+task.id, url); + assertEquals(task.name, model.get(WorkflowModelBuilder.TASK_NAME)); + assertEquals(task.title, model.get(WorkflowModelBuilder.TASK_TITLE)); + assertEquals(task.description, model.get(WorkflowModelBuilder.TASK_DESCRIPTION)); + assertEquals(task.state.name(), model.get(WorkflowModelBuilder.TASK_STATE)); + assertEquals(task.definition.metadata.getTitle(), model.get(WorkflowModelBuilder.TASK_TYPE_DEFINITION_TITLE)); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_POOLED)); + + Map owner = (Map) model.get(WorkflowModelBuilder.TASK_OWNER); + assertEquals( userName, owner.get(WorkflowModelBuilder.PERSON_USER_NAME)); + assertEquals( firstName, owner.get(WorkflowModelBuilder.PERSON_FIRST_NAME)); + assertEquals( lastName, owner.get(WorkflowModelBuilder.PERSON_LAST_NAME)); + + Map props = (Map) model.get(WorkflowModelBuilder.TASK_PROPERTIES); + assertEquals(task.properties.size(), props.size()); + assertEquals(5, props.get("test_int")); + assertEquals(false, props.get("test_boolean")); + assertEquals("foo bar", props.get("test_string")); + String dateStr = (String) props.get("test_date"); + assertEquals(date, ISO8601DateFormat.parse(dateStr)); + + task.properties.put(WorkflowModel.ASSOC_POOLED_ACTORS, new ArrayList(0)); + model = builder.buildSimple(task, null); + assertEquals(false, model.get(WorkflowModelBuilder.TASK_IS_POOLED)); + + ArrayList actors = new ArrayList(1); + actors.add(person); + task.properties.put(WorkflowModel.ASSOC_POOLED_ACTORS, actors); + model = builder.buildSimple(task, null); + assertEquals(true, model.get(WorkflowModelBuilder.TASK_IS_POOLED)); + + model = builder.buildSimple(task, Arrays.asList("test_int", "test_string")); + //Check task owner still created properly. + owner = (Map) model.get(WorkflowModelBuilder.TASK_OWNER); + assertEquals( userName, owner.get(WorkflowModelBuilder.PERSON_USER_NAME)); + + // Check properties populated correctly + props = (Map) model.get(WorkflowModelBuilder.TASK_PROPERTIES); + assertEquals(2, props.size()); + assertEquals(5, props.get("test_int")); + assertEquals("foo bar", props.get("test_string")); + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + namespaceService = new NamespaceServiceMemoryImpl(); + namespaceService.registerNamespace("test", URI); + namespaceService.registerNamespace(NamespaceService.CONTENT_MODEL_PREFIX, NamespaceService.CONTENT_MODEL_1_0_URI); + namespaceService.registerNamespace(NamespaceService.BPM_MODEL_PREFIX, NamespaceService.BPM_MODEL_1_0_URI); + + personService = mock(PersonService.class); + when(personService.getPerson(userName)).thenReturn(person); + + nodeService = mock(NodeService.class); + Map personProps = new HashMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, firstName); + personProps.put(ContentModel.PROP_LASTNAME, lastName); + when(nodeService.getProperties(person)).thenReturn(personProps); + + builder = new WorkflowModelBuilder(namespaceService, nodeService, personService); + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java new file mode 100644 index 0000000000..05ffb1a11f --- /dev/null +++ b/source/java/org/alfresco/repo/web/scripts/workflow/WorkflowRestApiTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.web.scripts.workflow; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.security.person.TestPersonManager; +import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowPath; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.GUID; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.context.ApplicationContext; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest; +import org.springframework.extensions.webscripts.TestWebScriptServer.Response; + +/** + * @author Nick Smith + * + */ +public class WorkflowRestApiTest extends BaseWebScriptTest +{ + private final static String USER1 = "Bob"+GUID.generate(); + private final static String USER2 = "Jane"+GUID.generate(); + private static final String URL_TASKS = "api/task-instance"; + + private TestPersonManager personManager; + private WorkflowService workflowService; + private NodeRef packageRef; + + public void testTaskInstancesGet() throws Exception + { + // Check USER2 starts with no tasks. + personManager.setUser(USER2); + Response response = sendRequest(new GetRequest(URL_TASKS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + JSONObject json = new JSONObject(response.getContentAsString()); + JSONArray results = json.getJSONArray("data"); + assertNotNull(results); + assertTrue(results.length()==0); + + //Start workflow as USER1 and assign task to USER2. + personManager.setUser(USER1); + WorkflowDefinition adhocDef = workflowService.getDefinitionByName("jbpm$wf:adhoc"); + Map params = new HashMap(); + params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2)); + params.put(WorkflowModel.PROP_DUE_DATE, new Date()); + params.put(WorkflowModel.PROP_PRIORITY, 1); + params.put(WorkflowModel.ASSOC_PACKAGE, packageRef); + + WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.id, params); + WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.id).get(0); + workflowService.endTask(startTask.id, null); + + // Check USER2 now has one task. + List tasks = workflowService.getAssignedTasks(USER2, WorkflowTaskState.IN_PROGRESS); + WorkflowTask task = tasks.get(0); + + Serializable actors = task.properties.get(WorkflowModel.ASSOC_POOLED_ACTORS); + System.out.println("Pooled Actors: " + actors); + + personManager.setUser(USER2); + response = sendRequest(new GetRequest(URL_TASKS), 200); + assertEquals(Status.STATUS_OK, response.getStatus()); + json = new JSONObject(response.getContentAsString()); + results = json.getJSONArray("data"); + assertNotNull(results); + assertTrue(results.length()==tasks.size()); + JSONObject result = results.getJSONObject(0); + + String expUrl = "api/task-instance/"+task.id; + assertEquals(expUrl, result.getString("url")); + assertEquals(task.name, result.getString("name")); + assertEquals(task.title, result.getString("title")); + assertEquals(task.description, result.getString("description")); + assertEquals(task.state.name(), result.getString("state")); + assertEquals(task.definition.metadata.getTitle(), result.getString("typeDefinitionTitle")); + assertEquals(false, result.getBoolean("isPooled")); + + JSONObject owner = result.getJSONObject("owner"); + assertEquals(USER2, owner.getString("userName")); + assertEquals(personManager.getFirstName(USER2), owner.getString("firstName")); + assertEquals(personManager.getLastName(USER2), owner.getString("lastName")); + + JSONObject properties = result.getJSONObject("properties"); + Collection values = task.properties.values(); + while(values.contains(null)) + { + values.remove(null); + } + assertEquals(values.size(), properties.length()); + + //TODO Add more tests to check property filtering and pooled actors. + } + + @Override + protected void setUp() throws Exception + { + super.setUp(); + ApplicationContext appContext = getServer().getApplicationContext(); + + workflowService = (WorkflowService)appContext.getBean("WorkflowService"); + MutableAuthenticationService authenticationService = (MutableAuthenticationService)appContext.getBean("AuthenticationService"); + PersonService personService = (PersonService)appContext.getBean("PersonService"); + NodeService nodeService = (NodeService)appContext.getBean("NodeService"); + personManager = new TestPersonManager(authenticationService, personService, nodeService); + + personManager.createPerson(USER1); + personManager.createPerson(USER2); + + packageRef = workflowService.createPackage(null); + } + + /* (non-Javadoc) + * @see junit.framework.TestCase#tearDown() + */ + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + personManager.clearPeople(); + } +}