diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java
new file mode 100644
index 0000000000..3f5e1f47ff
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java
@@ -0,0 +1,1728 @@
+/*
+ * 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.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.security.person.TestGroupManager;
+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.dictionary.TypeDefinition;
+import org.alfresco.service.cmr.model.FileFolderService;
+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.search.SearchService;
+import org.alfresco.service.cmr.security.AuthorityService;
+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.WorkflowInstance;
+import org.alfresco.service.cmr.workflow.WorkflowNode;
+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.WorkflowTaskDefinition;
+import org.alfresco.service.cmr.workflow.WorkflowTaskState;
+import org.alfresco.service.cmr.workflow.WorkflowTransition;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.springframework.context.ApplicationContext;
+import org.springframework.extensions.surf.util.ISO8601DateFormat;
+import org.springframework.extensions.webscripts.Status;
+import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
+
+import com.ibm.icu.util.Calendar;
+
+/**
+ * @author Nick Smith
+ * @author Frederik Heremans
+ *
+ */
+public abstract class AbstractWorkflowRestApiTest extends BaseWebScriptTest
+{
+ protected final static String USER1 = "Bob" + GUID.generate();
+ protected final static String USER2 = "Jane" + GUID.generate();
+ protected final static String USER3 = "Nick" + GUID.generate();
+ protected final static String GROUP ="Group" + GUID.generate();
+ protected static final String URL_TASKS = "api/task-instances";
+ protected static final String URL_USER_TASKS = "api/task-instances?authority={0}";
+ protected static final String URL_USER_TASKS_PROPERTIES = "api/task-instances?authority={0}&properties={1}";
+ protected static final String URL_TASKS_DUE_BEFORE = "api/task-instances?dueBefore={0}";
+ protected static final String URL_TASKS_DUE_AFTER = "api/task-instances?dueAfter={0}";
+ protected static final String URL_WORKFLOW_TASKS = "api/workflow-instances/{0}/task-instances";
+ protected static final String URL_WORKFLOW_DEFINITIONS = "api/workflow-definitions";
+ protected static final String URL_WORKFLOW_DEFINITION = "api/workflow-definitions/{0}";
+ protected static final String URL_WORKFLOW_INSTANCES = "api/workflow-instances";
+ protected static final String URL_WORKFLOW_INSTANCES_FOR_DEFINITION = "api/workflow-definitions/{0}/workflow-instances";
+ protected static final String URL_WORKFLOW_INSTANCES_FOR_NODE = "api/node/{0}/{1}/{2}/workflow-instances";
+
+ protected static final String COMPANY_HOME = "/app:company_home";
+ protected static final String TEST_CONTENT = "TestContent";
+ protected static final String ADHOC_START_TASK_TYPE = "wf:submitAdhocTask";
+ protected static final String ADHOC_TASK_TYPE = "wf:adhocTask";
+ protected static final String ADHOC_TASK_COMPLETED_TYPE = "wf:completedAdhocTask";
+
+
+ private TestPersonManager personManager;
+ private TestGroupManager groupManager;
+
+ protected WorkflowService workflowService;
+ private NodeService nodeService;
+ private NamespaceService namespaceService;
+ private NodeRef packageRef;
+ private NodeRef contentNodeRef;
+ private AuthenticationComponent authenticationComponent;
+
+ public void testTaskInstancesGet() throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ // Check USER2 starts with no tasks.
+ personManager.setUser(USER2);
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ 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(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Calendar dueDateCal = Calendar.getInstance();
+ Date dueDate = dueDateCal.getTime();
+
+ params.put(WorkflowModel.PROP_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+ workflowService.endTask(startTask.getId(), null);
+
+ // Check USER2 now has one task.
+ List tasks = workflowService.getAssignedTasks(USER2, WorkflowTaskState.IN_PROGRESS);
+ WorkflowTask task = tasks.get(0);
+
+ Map updateParams = new HashMap(1);
+ updateParams.put(WorkflowModel.PROP_DUE_DATE, new Date());
+ workflowService.updateTask(task.getId(), updateParams, null, null);
+
+ personManager.setUser(USER2);
+ response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertTrue(results.length() == tasks.size());
+ JSONObject result = results.getJSONObject(0);
+
+ int totalItems = results.length();
+
+ String expUrl = "api/task-instances/" + task.getId();
+ assertEquals(expUrl, result.getString("url"));
+ assertEquals(task.getName(), result.getString("name"));
+ assertEquals(task.getTitle(), result.getString("title"));
+ assertEquals(task.getDescription(), result.getString("description"));
+ assertEquals(task.getState().name(), result.getString("state"));
+ assertEquals( "api/workflow-paths/" + adhocPath.getId(), result.getString("path"));
+ assertFalse(result.getBoolean("isPooled"));
+ assertTrue(result.getBoolean("isEditable"));
+ assertTrue(result.getBoolean("isReassignable"));
+ assertFalse(result.getBoolean("isClaimable"));
+ assertFalse(result.getBoolean("isReleasable"));
+
+ 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");
+ assertNotNull(properties);
+
+ JSONObject instance = result.getJSONObject("workflowInstance");
+ assertNotNull(instance);
+
+ // Check state filtering
+ checkTasksState(URL_TASKS + "?state=completed", WorkflowTaskState.COMPLETED);
+ checkTasksState(URL_TASKS + "?state=in_progress", WorkflowTaskState.IN_PROGRESS);
+
+ // TODO: Add more tests to check pooled actors.
+
+ // Check for priority filtering
+ checkPriorityFiltering(URL_TASKS + "?priority=2");
+
+ // Due after yesterday, started task should be in it
+ dueDateCal.add(Calendar.DAY_OF_MONTH, -1);
+ checkTasksPresent(MessageFormat.format(URL_TASKS_DUE_AFTER, ISO8601DateFormat.format(dueDateCal.getTime())),
+ true,
+ task.getId());
+
+ // Due before yesterday, started task shouldn't be in it
+ checkTasksPresent(MessageFormat.format(URL_TASKS_DUE_BEFORE, ISO8601DateFormat.format(dueDateCal.getTime())),
+ false,
+ task.getId());
+
+ // Due before tomorrow, started task should be in it
+ dueDateCal.add(Calendar.DAY_OF_MONTH, 2);
+ checkTasksPresent(MessageFormat.format(URL_TASKS_DUE_BEFORE, ISO8601DateFormat.format(dueDateCal.getTime())),
+ true,
+ task.getId());
+
+ // Due after tomorrow, started task shouldn't be in it
+ checkTasksPresent(MessageFormat.format(URL_TASKS_DUE_AFTER, ISO8601DateFormat.format(dueDateCal.getTime())),
+ false,
+ task.getId());
+
+ //checkFiltering(URL_TASKS + "?dueAfter=" + ISO8601DateFormat.format(dueDate));
+
+ //checkFiltering(URL_TASKS + "?dueBefore=" + ISO8601DateFormat.format(new Date()));
+
+ // Check property filtering on the task assigned to USER2
+ String customProperties = "bpm_description,bpm_priority";
+ checkTaskPropertyFiltering(customProperties, Arrays.asList("bpm_description", "bpm_priority"));
+
+ // Properties that aren't explicitally present on task should be returned as wel
+ customProperties = "bpm_unexistingProperty,bpm_description,bpm_priority";
+ checkTaskPropertyFiltering(customProperties, Arrays.asList("bpm_description", "bpm_priority", "bpm_unexistingProperty"));
+
+ // Check paging
+ int maxItems = 3;
+ for (int skipCount = 0; skipCount < totalItems; skipCount += maxItems)
+ {
+ // one of this should test situation when skipCount + maxItems > totalItems
+ checkPaging(MessageFormat.format(URL_USER_TASKS, USER2) + "&maxItems=" + maxItems + "&skipCount=" + skipCount, totalItems, maxItems, skipCount);
+ }
+
+ // testing when skipCount > totalItems
+ checkPaging(MessageFormat.format(URL_USER_TASKS, USER2) + "&maxItems=" + maxItems + "&skipCount=" + (totalItems + 1), totalItems, maxItems, totalItems + 1);
+
+ // check the exclude filtering
+ String exclude = "wf:submitAdhocTask";
+ response = sendRequest(new GetRequest(URL_TASKS + "?exclude=" + exclude), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+
+ boolean adhocTasksPresent = false;
+ for (int i = 0; i < results.length(); i++)
+ {
+ JSONObject taskJSON = results.getJSONObject(i);
+
+ String type = taskJSON.getString("name");
+ if (exclude.equals(type))
+ {
+ adhocTasksPresent = true;
+ break;
+ }
+ }
+
+ assertFalse("Found wf:submitAdhocTask when they were supposed to be excluded", adhocTasksPresent);
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.cancelWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testTaskInstancesForWorkflowGet() throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ // Check starts with no workflow.
+ personManager.setUser(USER2);
+ sendRequest(new GetRequest(MessageFormat.format(URL_WORKFLOW_TASKS, "Foo")), Status.STATUS_INTERNAL_SERVER_ERROR);
+
+ // Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Calendar dueDateCal = Calendar.getInstance();
+ Date dueDate = dueDateCal.getTime();
+
+ params.put(WorkflowModel.PROP_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+
+ // End start task.
+ WorkflowTask startTask = workflowService.getStartTask(startedWorkflowId);
+ String startTaskId = startTask.getId();
+ workflowService.endTask(startTaskId, null);
+
+ // Check USER2 now has one task.
+ List tasks = workflowService.getAssignedTasks(USER2, WorkflowTaskState.IN_PROGRESS);
+ assertEquals(1, tasks.size());
+ WorkflowTask task = tasks.get(0);
+
+ // Retrieve tasks using the workflow instance
+ String baseUrl = MessageFormat.format(URL_WORKFLOW_TASKS, startedWorkflowId);
+
+ // Check returns the completed start task and the current task.
+ String adhocTaskId = task.getId();
+ checkTasksMatch(baseUrl, startTaskId, adhocTaskId);
+
+ String completedUrl = baseUrl + "?state="+WorkflowTaskState.COMPLETED;
+ checkTasksMatch(completedUrl, startTaskId);
+
+ String inProgressUrl = baseUrl + "?state="+WorkflowTaskState.IN_PROGRESS;
+ checkTasksMatch(inProgressUrl, adhocTaskId);
+
+ String user1Url = baseUrl + "?authority="+USER1;
+ checkTasksMatch(user1Url, startTaskId);
+
+ String user2Url = baseUrl + "?authority="+USER2;
+ checkTasksMatch(user2Url, adhocTaskId);
+
+ String user1CompletedURL = user1Url + "&state=" + WorkflowTaskState.COMPLETED;
+ checkTasksMatch(user1CompletedURL, startTaskId);
+
+ String user1InProgressURL = user1Url + "&state=" + WorkflowTaskState.IN_PROGRESS;
+ checkTasksMatch(user1InProgressURL);
+
+ String user2CompletedURL = user2Url + "&state=" + WorkflowTaskState.COMPLETED;
+ checkTasksMatch(user2CompletedURL);
+
+ String user2InProgressURL = user2Url + "&state=" + WorkflowTaskState.IN_PROGRESS;
+ checkTasksMatch(user2InProgressURL, adhocTaskId);
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.cancelWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testTaskInstanceGet() throws Exception
+ {
+ String startedWorkflowId = null;
+
+ try {
+ //Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ 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.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+
+ // Get the start-task
+ Response response = sendRequest(new GetRequest(URL_TASKS + "/" + startTask.getId()), Status.STATUS_OK);
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONObject result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ assertEquals(startTask.getId(), result.getString("id"));
+ assertEquals(URL_TASKS + "/" + startTask.getId(), result.getString("url"));
+ assertEquals(startTask.getName(), result.getString("name"));
+ assertEquals(startTask.getTitle(), result.getString("title"));
+ assertEquals(startTask.getDescription(), result.getString("description"));
+
+ // Task should be in progress
+ assertEquals(startTask.getState().name(), result.getString("state"));
+ assertEquals(WorkflowTaskState.IN_PROGRESS.toString(), result.getString("state"));
+ assertEquals("api/workflow-paths/" + adhocPath.getId(), result.getString("path"));
+
+ checkWorkflowTaskEditable(result);
+ checkWorkflowTaskOwner(result, USER1);
+ checkWorkflowTaskPropertiesPresent(result);
+
+ checkWorkflowInstance(startTask.getPath().getInstance(), result.getJSONObject("workflowInstance"));
+ checkWorkflowTaskDefinition(startTask.getDefinition(), result.getJSONObject("definition"));
+
+ // Finish the start-task, and fetch it again
+ workflowService.endTask(startTask.getId(), null);
+ startTask = workflowService.getTaskById(startTask.getId());
+
+ response = sendRequest(new GetRequest(URL_TASKS + "/" + startTask.getId()), Status.STATUS_OK);
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ assertEquals(startTask.getId(), result.getString("id"));
+ assertEquals(URL_TASKS + "/" + startTask.getId(), result.getString("url"));
+ assertEquals(startTask.getName(), result.getString("name"));
+ assertEquals(startTask.getTitle(), result.getString("title"));
+ assertEquals(startTask.getDescription(), result.getString("description"));
+
+ // Start task should be completed
+ assertEquals(startTask.getState().name(), result.getString("state"));
+ assertEquals(WorkflowTaskState.COMPLETED.toString(), result.getString("state"));
+ assertEquals("api/workflow-paths/" + adhocPath.getId(), result.getString("path"));
+
+ checkWorkflowTaskReadOnly(result);
+ checkWorkflowTaskOwner(result, USER1);
+ checkWorkflowTaskPropertiesPresent(result);
+
+ checkWorkflowInstance(startTask.getPath().getInstance(), result.getJSONObject("workflowInstance"));
+ checkWorkflowTaskDefinition(startTask.getDefinition(), result.getJSONObject("definition"));
+
+ // Get the next active task
+ WorkflowTask firstTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+
+ response = sendRequest(new GetRequest(URL_TASKS + "/" + firstTask.getId()), 200);
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ assertEquals(firstTask.getId(), result.getString("id"));
+ assertEquals(URL_TASKS + "/" + firstTask.getId(), result.getString("url"));
+ assertEquals(firstTask.getName(), result.getString("name"));
+ assertEquals(firstTask.getTitle(), result.getString("title"));
+ assertEquals(firstTask.getDescription(), result.getString("description"));
+
+ // Task should be in progress
+ assertEquals(firstTask.getState().name(), result.getString("state"));
+ assertEquals(WorkflowTaskState.IN_PROGRESS.toString(), result.getString("state"));
+ assertEquals("api/workflow-paths/" + adhocPath.getId(), result.getString("path"));
+
+ checkWorkflowTaskEditable(result);
+ checkWorkflowTaskOwner(result, USER2);
+ checkWorkflowTaskPropertiesPresent(result);
+
+ checkWorkflowInstance(firstTask.getPath().getInstance(), result.getJSONObject("workflowInstance"));
+ checkWorkflowTaskDefinition(firstTask.getDefinition(), result.getJSONObject("definition"));
+
+ // Finish the task, and fetch it again
+ workflowService.endTask(firstTask.getId(), null);
+ firstTask = workflowService.getTaskById(firstTask.getId());
+
+ response = sendRequest(new GetRequest(URL_TASKS + "/" + firstTask.getId()), 200);
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ assertEquals(firstTask.getId(), result.getString("id"));
+ assertEquals(URL_TASKS + "/" + firstTask.getId(), result.getString("url"));
+ assertEquals(firstTask.getName(), result.getString("name"));
+ assertEquals(firstTask.getTitle(), result.getString("title"));
+ assertEquals(firstTask.getDescription(), result.getString("description"));
+
+ // The task should be completed
+ assertEquals(firstTask.getState().name(), result.getString("state"));
+ assertEquals(WorkflowTaskState.COMPLETED.toString(), result.getString("state"));
+ assertEquals("api/workflow-paths/" + adhocPath.getId(), result.getString("path"));
+
+ checkWorkflowTaskReadOnly(result);
+ checkWorkflowTaskOwner(result, USER2);
+ checkWorkflowTaskPropertiesPresent(result);
+
+ checkWorkflowInstance(firstTask.getPath().getInstance(), result.getJSONObject("workflowInstance"));
+ checkWorkflowTaskDefinition(firstTask.getDefinition(), result.getJSONObject("definition"));
+
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ private void checkWorkflowTaskPropertiesPresent(JSONObject taskJson) throws Exception
+ {
+ JSONObject properties = taskJson.getJSONObject("properties");
+ assertNotNull(properties);
+ assertTrue(properties.has("bpm_priority"));
+ assertTrue(properties.has("bpm_description"));
+ assertTrue(properties.has("bpm_reassignable"));
+ }
+ private void checkWorkflowTaskReadOnly(JSONObject taskJson) throws Exception
+ {
+ // Task shouldn't be editable and reassignable, since it's completed
+ assertFalse(taskJson.getBoolean("isPooled"));
+ assertFalse(taskJson.getBoolean("isEditable"));
+ assertFalse(taskJson.getBoolean("isReassignable"));
+ assertFalse(taskJson.getBoolean("isClaimable"));
+ assertFalse(taskJson.getBoolean("isReleasable"));
+ }
+
+ private void checkWorkflowTaskOwner(JSONObject taskJson, String user) throws Exception
+ {
+ JSONObject owner = taskJson.getJSONObject("owner");
+ assertEquals(user, owner.getString("userName"));
+ assertEquals(personManager.getFirstName(user), owner.getString("firstName"));
+ assertEquals(personManager.getLastName(user), owner.getString("lastName"));
+ }
+
+ private void checkWorkflowTaskEditable(JSONObject taskJson) throws Exception
+ {
+ assertFalse(taskJson.getBoolean("isPooled"));
+ assertTrue(taskJson.getBoolean("isEditable"));
+ assertTrue(taskJson.getBoolean("isReassignable"));
+ assertFalse(taskJson.getBoolean("isClaimable"));
+ assertFalse(taskJson.getBoolean("isReleasable"));
+ }
+
+ private void checkWorkflowInstance(WorkflowInstance wfInstance, JSONObject instance) throws Exception
+ {
+ assertNotNull(instance);
+ assertEquals(wfInstance.getId(), instance.getString("id"));
+ assertTrue(instance.has("url"));
+ assertEquals(wfInstance.getDefinition().getName(), instance.getString("name"));
+ assertEquals(wfInstance.getDefinition().getTitle(), instance.getString("title"));
+ assertEquals(wfInstance.getDefinition().getDescription(), instance.getString("description"));
+ assertEquals(wfInstance.isActive(), instance.getBoolean("isActive"));
+ assertTrue(instance.has("startDate"));
+
+ JSONObject initiator = instance.getJSONObject("initiator");
+
+ assertEquals(USER1, initiator.getString("userName"));
+ assertEquals(personManager.getFirstName(USER1), initiator.getString("firstName"));
+ assertEquals(personManager.getLastName(USER1), initiator.getString("lastName"));
+ }
+
+ private void checkWorkflowTaskDefinition(WorkflowTaskDefinition wfDefinition, JSONObject definition) throws Exception
+ {
+ assertNotNull(definition);
+
+ assertEquals(wfDefinition.getId(), definition.getString("id"));
+ assertTrue(definition.has("url"));
+
+ JSONObject type = definition.getJSONObject("type");
+ TypeDefinition startType = (wfDefinition).getMetadata();
+
+ assertNotNull(type);
+
+ assertEquals(startType.getName().toPrefixString(), type.getString("name"));
+ assertEquals(startType.getTitle(), type.getString("title"));
+ assertEquals(startType.getDescription(), type.getString("description"));
+ assertTrue(type.has("url"));
+
+ JSONObject node = definition.getJSONObject("node");
+ WorkflowNode startNode = wfDefinition.getNode();
+
+ assertNotNull(node);
+
+ assertEquals(startNode.getName(), node.getString("name"));
+ assertEquals(startNode.getTitle(), node.getString("title"));
+ assertEquals(startNode.getDescription(), node.getString("description"));
+ assertEquals(startNode.isTaskNode(), node.getBoolean("isTaskNode"));
+
+ JSONArray transitions = node.getJSONArray("transitions");
+ WorkflowTransition[] startTransitions = startNode.getTransitions();
+
+ assertNotNull(transitions);
+
+ assertEquals(startTransitions.length, transitions.length());
+
+ for (int i = 0; i < transitions.length(); i++)
+ {
+ JSONObject transition = transitions.getJSONObject(i);
+ WorkflowTransition startTransition = startTransitions[i];
+
+ assertNotNull(transition);
+
+ assertEquals(startTransition.getId(), transition.getString("id"));
+ assertEquals(startTransition.getTitle(), transition.getString("title"));
+ assertEquals(startTransition.getDescription(), transition.getString("description"));
+ assertEquals(startTransition.isDefault(), transition.getBoolean("isDefault"));
+ assertTrue(transition.has("isHidden"));
+ }
+
+ }
+
+ public void testTaskInstancePut() throws Exception
+ {
+ String startedWorkflowId = null;
+
+ try
+ {
+ // Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ 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.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getStartTask(adhocPath.getInstance().getId());
+
+ // Finish the start-task
+ workflowService.endTask(startTask.getId(), null);
+
+ WorkflowTask firstTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+
+ Response getResponse = sendRequest(new GetRequest(URL_TASKS + "/" + firstTask.getId()), 200);
+
+ JSONObject jsonProperties = new JSONObject(getResponse.getContentAsString()).getJSONObject("data").getJSONObject("properties");
+
+ // make some changes in existing properties
+ jsonProperties.remove(qnameToString(WorkflowModel.ASSOC_PACKAGE));
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_COMMENT), "Edited comment");
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_DUE_DATE), ISO8601DateFormat.format(new Date()));
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_DESCRIPTION), "Edited description");
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_PRIORITY), 1);
+
+ // Add some custom properties, which are not defined in typeDef
+ jsonProperties.put("customIntegerProperty", 1234);
+ jsonProperties.put("customBooleanProperty", Boolean.TRUE);
+ jsonProperties.put("customStringProperty", "Property value");
+
+ // test USER3 can not update the task
+ personManager.setUser(USER3);
+ Response unauthResponse = sendRequest(new PutRequest(URL_TASKS + "/" + firstTask.getId(), jsonProperties.toString(), "application/json"), 401);
+ assertEquals(Status.STATUS_UNAUTHORIZED, unauthResponse.getStatus());
+
+
+ // test USER2 (the task owner) can update the task
+ personManager.setUser(USER2);
+ Response putResponse = sendRequest(new PutRequest(URL_TASKS + "/" + firstTask.getId(), jsonProperties.toString(), "application/json"), 200);
+
+ assertEquals(Status.STATUS_OK, putResponse.getStatus());
+ String jsonStr = putResponse.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONObject result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ JSONObject editedJsonProperties = result.getJSONObject("properties");
+ compareProperties(jsonProperties, editedJsonProperties);
+
+ // test USER1 (the task workflow initiator) can update the task
+ personManager.setUser(USER1);
+ putResponse = sendRequest(new PutRequest(URL_TASKS + "/" + firstTask.getId(), jsonProperties.toString(), "application/json"), 200);
+
+ assertEquals(Status.STATUS_OK, putResponse.getStatus());
+ jsonStr = putResponse.getContentAsString();
+ json = new JSONObject(jsonStr);
+ result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ editedJsonProperties = result.getJSONObject("properties");
+ compareProperties(jsonProperties, editedJsonProperties);
+
+ // Reassign the task to USER3 using taskInstance PUT
+ jsonProperties = new JSONObject();
+ jsonProperties.put(qnameToString(ContentModel.PROP_OWNER), USER3);
+ putResponse = sendRequest(new PutRequest(URL_TASKS + "/" + firstTask.getId(), jsonProperties.toString(), "application/json"), 200);
+ assertEquals(Status.STATUS_OK, putResponse.getStatus());
+
+ // test USER3 (now the task owner) can update the task
+ personManager.setUser(USER3);
+
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_COMMENT), "Edited comment by USER3");
+ putResponse = sendRequest(new PutRequest(URL_TASKS + "/" + firstTask.getId(), jsonProperties.toString(), "application/json"), 200);
+
+ assertEquals(Status.STATUS_OK, putResponse.getStatus());
+
+ jsonStr = putResponse.getContentAsString();
+ json = new JSONObject(jsonStr);
+ result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ editedJsonProperties = result.getJSONObject("properties");
+ compareProperties(jsonProperties, editedJsonProperties);
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testTaskInstancePutCompletedTask() throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ // Start workflow as USER1 and assign to self
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER1));
+ 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.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+
+ // Finish the start-task
+ workflowService.endTask(startTask.getId(), null);
+
+ Response getResponse = sendRequest(new GetRequest(URL_TASKS + "/" + startTask.getId()), 200);
+
+ JSONObject jsonProperties = new JSONObject(getResponse.getContentAsString()).getJSONObject("data").getJSONObject("properties");
+
+ // Make a change
+ jsonProperties.put(qnameToString(WorkflowModel.PROP_DESCRIPTION), "Edited description");
+
+ // Update task. An error is expected, since the task is completed (and not editable)
+ sendRequest(new PutRequest(URL_TASKS + "/" + startTask.getId(), jsonProperties.toString(), "application/json"),
+ Status.STATUS_UNAUTHORIZED);
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testWorkflowDefinitionsGet() throws Exception
+ {
+ Response response = sendRequest(new GetRequest(URL_WORKFLOW_DEFINITIONS), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ JSONObject json = new JSONObject(response.getContentAsString());
+ JSONArray results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertTrue(results.length() > 0);
+
+ boolean adhocWorkflowPresent = false;
+
+ for (int i = 0; i < results.length(); i++)
+ {
+ JSONObject workflowDefinitionJSON = results.getJSONObject(i);
+
+ assertTrue(workflowDefinitionJSON.has("id"));
+ assertTrue(workflowDefinitionJSON.getString("id").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("url"));
+ String url = workflowDefinitionJSON.getString("url");
+ assertTrue(url.length() > 0);
+ assertTrue(url.startsWith("api/workflow-definitions/"));
+
+ assertTrue(workflowDefinitionJSON.has("name"));
+ assertTrue(workflowDefinitionJSON.getString("name").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("title"));
+ assertTrue(workflowDefinitionJSON.getString("title").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("description"));
+ assertTrue(workflowDefinitionJSON.getString("description").length() > 0);
+
+ if(getAdhocWorkflowDefinitionName().equals(workflowDefinitionJSON.getString("name")))
+ {
+ adhocWorkflowPresent = true;
+ }
+ }
+
+ assertTrue("Adhoc workflow definition was returned", adhocWorkflowPresent);
+
+ // filter the workflow definitions and check they are not returned
+ String exclude = getAdhocWorkflowDefinitionName();
+ response = sendRequest(new GetRequest(URL_WORKFLOW_DEFINITIONS + "?exclude=" + exclude), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ json = new JSONObject(response.getContentAsString());
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+
+ adhocWorkflowPresent = false;
+ for (int i = 0; i < results.length(); i++)
+ {
+ JSONObject workflowDefinitionJSON = results.getJSONObject(i);
+
+ String name = workflowDefinitionJSON.getString("name");
+ if (exclude.equals(name))
+ {
+ adhocWorkflowPresent = true;
+ break;
+ }
+ }
+
+ assertFalse("Found adhoc workflow when it was supposed to be excluded", adhocWorkflowPresent);
+
+ // filter with a wildcard and ensure they all get filtered out
+ exclude = getAdhocWorkflowDefinitionName() + ", jbpm$wcmwf:*";
+ response = sendRequest(new GetRequest(URL_WORKFLOW_DEFINITIONS + "?exclude=" + exclude), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ json = new JSONObject(response.getContentAsString());
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+
+ adhocWorkflowPresent = false;
+ boolean wcmWorkflowsPresent = false;
+ for (int i = 0; i < results.length(); i++)
+ {
+ JSONObject workflowDefinitionJSON = results.getJSONObject(i);
+
+ String name = workflowDefinitionJSON.getString("name");
+ if (name.equals(getAdhocWorkflowDefinitionName()))
+ {
+ adhocWorkflowPresent = true;
+ }
+ if (name.startsWith("jbpm$wcmwf:"))
+ {
+ wcmWorkflowsPresent = true;
+ }
+ }
+
+ assertFalse("Found adhoc workflow when it was supposed to be excluded", adhocWorkflowPresent);
+ assertFalse("Found a WCM workflow when they were supposed to be excluded", wcmWorkflowsPresent);
+ }
+
+ public void testWorkflowDefinitionGet() throws Exception
+ {
+
+ // Get the latest definition for the adhoc-workflow
+ WorkflowDefinition wDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+
+ String responseUrl = MessageFormat.format(
+ URL_WORKFLOW_DEFINITION, wDef.getId());
+
+ Response response = sendRequest(new GetRequest(responseUrl), Status.STATUS_OK);
+ JSONObject json = new JSONObject(response.getContentAsString());
+ JSONObject workflowDefinitionJSON = json.getJSONObject("data");
+ assertNotNull(workflowDefinitionJSON);
+
+ // Check fields
+ assertTrue(workflowDefinitionJSON.has("id"));
+ assertTrue(workflowDefinitionJSON.getString("id").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("url"));
+ String url = workflowDefinitionJSON.getString("url");
+ assertTrue(url.length() > 0);
+ assertTrue(url.startsWith("api/workflow-definitions/"));
+
+ assertTrue(workflowDefinitionJSON.has("name"));
+ assertTrue(workflowDefinitionJSON.getString("name").length() > 0);
+ assertEquals(getAdhocWorkflowDefinitionName(), workflowDefinitionJSON.getString("name"));
+
+ assertTrue(workflowDefinitionJSON.has("title"));
+ assertTrue(workflowDefinitionJSON.getString("title").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("description"));
+ assertTrue(workflowDefinitionJSON.getString("description").length() > 0);
+
+ assertTrue(workflowDefinitionJSON.has("startTaskDefinitionUrl"));
+ String startTaskDefUrl = workflowDefinitionJSON.getString("startTaskDefinitionUrl");
+ assertEquals(startTaskDefUrl, "api/classes/"
+ + getSafeDefinitionName(ADHOC_START_TASK_TYPE));
+
+ assertTrue(workflowDefinitionJSON.has("startTaskDefinitionType"));
+ assertEquals(ADHOC_START_TASK_TYPE, workflowDefinitionJSON.getString("startTaskDefinitionType"));
+
+ // Check task-definitions
+ JSONArray taskDefinitions = workflowDefinitionJSON.getJSONArray("taskDefinitions");
+ assertNotNull(taskDefinitions);
+
+ // Two task definitions should be returned. Start-task is not included
+ assertEquals(2, taskDefinitions.length());
+
+ // Should be adhoc-task
+ JSONObject firstTaskDefinition = (JSONObject) taskDefinitions.get(0);
+ checkTaskDefinitionTypeAndUrl(ADHOC_TASK_TYPE, firstTaskDefinition);
+
+ // Should be adhoc completed task
+ JSONObject secondTaskDefinition = (JSONObject) taskDefinitions.get(1);
+ checkTaskDefinitionTypeAndUrl(ADHOC_TASK_COMPLETED_TYPE, secondTaskDefinition);
+ }
+
+ private void checkTaskDefinitionTypeAndUrl(String expectedTaskType, JSONObject taskDefinition) throws Exception
+ {
+ // Check type
+ assertTrue(taskDefinition.has("type"));
+ assertEquals(expectedTaskType, taskDefinition.getString("type"));
+
+ // Check URL
+ assertTrue(taskDefinition.has("url"));
+ assertEquals("api/classes/"
+ + getSafeDefinitionName(expectedTaskType), taskDefinition.getString("url"));
+ }
+
+ private String getSafeDefinitionName(String definitionName) {
+ return definitionName.replace(":", "_");
+ }
+
+
+ public void testWorkflowInstanceGet() throws Exception
+ {
+ String startedWorkflowId = null;
+
+ try
+ {
+ //Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Date dueDate = new Date();
+ params.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_WORKFLOW_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+ params.put(WorkflowModel.PROP_CONTEXT, packageRef);
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+ startTask = workflowService.endTask(startTask.getId(), null);
+
+ WorkflowInstance adhocInstance = startTask.getPath().getInstance();
+
+ Response response = sendRequest(new GetRequest(URL_WORKFLOW_INSTANCES + "/" + adhocInstance.getId() + "?includeTasks=true"), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONObject result = json.getJSONObject("data");
+ assertNotNull(result);
+
+ assertEquals(adhocInstance.getId(), result.getString("id"));
+ assertTrue(result.opt("message").equals(JSONObject.NULL));
+ assertEquals(adhocInstance.getDefinition().getName(), result.getString("name"));
+ assertEquals(adhocInstance.getDefinition().getTitle(), result.getString("title"));
+ assertEquals(adhocInstance.getDefinition().getDescription(), result.getString("description"));
+ assertEquals(adhocInstance.isActive(), result.getBoolean("isActive"));
+ assertEquals(ISO8601DateFormat.format(adhocInstance.getStartDate()), result.getString("startDate"));
+ assertNotNull(result.getString("dueDate"));
+ assertNotNull(result.getString("endDate"));
+ assertEquals(1, result.getInt("priority"));
+ JSONObject initiator = result.getJSONObject("initiator");
+
+ assertEquals(USER1, initiator.getString("userName"));
+ assertEquals(personManager.getFirstName(USER1), initiator.getString("firstName"));
+ assertEquals(personManager.getLastName(USER1), initiator.getString("lastName"));
+
+ assertEquals(adhocInstance.getContext().toString(), result.getString("context"));
+ assertEquals(adhocInstance.getWorkflowPackage().toString(), result.getString("package"));
+ assertNotNull(result.getString("startTaskInstanceId"));
+
+ JSONObject jsonDefinition = result.getJSONObject("definition");
+ WorkflowDefinition adhocDefinition = adhocInstance.getDefinition();
+
+ assertNotNull(jsonDefinition);
+
+ assertEquals(adhocDefinition.getId(), jsonDefinition.getString("id"));
+ assertEquals(adhocDefinition.getName(), jsonDefinition.getString("name"));
+ assertEquals(adhocDefinition.getTitle(), jsonDefinition.getString("title"));
+ assertEquals(adhocDefinition.getDescription(), jsonDefinition.getString("description"));
+ assertEquals(adhocDefinition.getVersion(), jsonDefinition.getString("version"));
+ assertEquals(adhocDefinition.getStartTaskDefinition().getMetadata().getName().toPrefixString(namespaceService), jsonDefinition.getString("startTaskDefinitionType"));
+ assertTrue(jsonDefinition.has("taskDefinitions"));
+
+ JSONArray tasks = result.getJSONArray("tasks");
+ assertTrue(tasks.length() > 1);
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testWorkflowInstancesGet() throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ //Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Date dueDate = new Date();
+ params.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_WORKFLOW_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+ params.put(WorkflowModel.PROP_CONTEXT, packageRef);
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+ startedWorkflowId = adhocPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+ WorkflowInstance adhocInstance = startTask.getPath().getInstance();
+ workflowService.endTask(startTask.getId(), null);
+
+ // Get Workflow Instance Collection
+ Response response = sendRequest(new GetRequest(URL_WORKFLOW_INSTANCES), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+
+ int totalItems = result.length();
+ for (int i = 0; i < result.length(); i++)
+ {
+ checkSimpleWorkflowInstanceResponse(result.getJSONObject(i));
+ }
+
+ Response forDefinitionResponse = sendRequest(new GetRequest(MessageFormat.format(URL_WORKFLOW_INSTANCES_FOR_DEFINITION, adhocDef.getId())), 200);
+ assertEquals(Status.STATUS_OK, forDefinitionResponse.getStatus());
+ String forDefinitionJsonStr = forDefinitionResponse.getContentAsString();
+ JSONObject forDefinitionJson = new JSONObject(forDefinitionJsonStr);
+ JSONArray forDefinitionResult = forDefinitionJson.getJSONArray("data");
+ assertNotNull(forDefinitionResult);
+
+ for (int i = 0; i < forDefinitionResult.length(); i++)
+ {
+ checkSimpleWorkflowInstanceResponse(forDefinitionResult.getJSONObject(i));
+ }
+
+ // create a date an hour ago to test filtering
+ Calendar hourAgoCal = Calendar.getInstance();
+ hourAgoCal.setTime(dueDate);
+ hourAgoCal.add(Calendar.HOUR_OF_DAY, -1);
+ Date anHourAgo = hourAgoCal.getTime();
+
+ Calendar hourLater = Calendar.getInstance();
+ hourLater.setTime(dueDate);
+ hourLater.add(Calendar.HOUR_OF_DAY, 1);
+ Date anHourLater = hourLater.getTime();
+
+ // filter by initiator
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?initiator=" + USER1);
+
+ // filter by startedAfter
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?startedAfter=" + ISO8601DateFormat.format(anHourAgo));
+
+ // filter by startedBefore
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?startedBefore=" + ISO8601DateFormat.format(anHourLater));
+
+ // filter by dueAfter
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?dueAfter=" + ISO8601DateFormat.format(anHourAgo));
+
+ // filter by dueBefore
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?dueBefore=" + ISO8601DateFormat.format(anHourLater));
+
+ if (adhocInstance.getEndDate() != null)
+ {
+ // filter by completedAfter
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?completedAfter=" + ISO8601DateFormat.format(adhocInstance.getEndDate()));
+
+ // filter by completedBefore
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?completedBefore=" + ISO8601DateFormat.format(adhocInstance.getEndDate()));
+ }
+
+ // filter by priority
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?priority=1");
+
+ // filter by state
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?state=active");
+
+ // filter by definition name
+ checkFiltering(URL_WORKFLOW_INSTANCES + "?definitionName=" + getAdhocWorkflowDefinitionName());
+
+ // paging
+ int maxItems = 3;
+ for (int skipCount = 0; skipCount < totalItems; skipCount += maxItems)
+ {
+ checkPaging(URL_WORKFLOW_INSTANCES + "?maxItems=" + maxItems + "&skipCount=" + skipCount, totalItems, maxItems, skipCount);
+ }
+
+ // check the exclude filtering
+ String exclude = getAdhocWorkflowDefinitionName();
+ response = sendRequest(new GetRequest(URL_WORKFLOW_INSTANCES + "?exclude=" + exclude), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ JSONArray results = json.getJSONArray("data");
+ assertNotNull(results);
+
+ boolean adhocWorkflowPresent = false;
+ for (int i = 0; i < results.length(); i++)
+ {
+ JSONObject workflowInstanceJSON = results.getJSONObject(i);
+
+ String type = workflowInstanceJSON.getString("name");
+ if (exclude.equals(type))
+ {
+ adhocWorkflowPresent = true;
+ break;
+ }
+ }
+ assertFalse("Found adhoc workflows when they were supposed to be excluded", adhocWorkflowPresent);
+
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ public void testWorkflowInstancesForNodeGet() throws Exception
+ {
+ //Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ 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);
+
+ nodeService.addChild(packageRef, contentNodeRef,
+ WorkflowModel.ASSOC_PACKAGE_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
+ QName.createValidLocalName((String)nodeService.getProperty(
+ contentNodeRef, ContentModel.PROP_NAME))));
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+
+ String url = MessageFormat.format(URL_WORKFLOW_INSTANCES_FOR_NODE, contentNodeRef.getStoreRef().getProtocol(), contentNodeRef.getStoreRef().getIdentifier(), contentNodeRef.getId());
+ Response response = sendRequest(new GetRequest(url), 200);
+
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+
+ assertTrue(result.length() > 0);
+
+ workflowService.cancelWorkflow(adhocPath.getInstance().getId());
+
+ Response afterCancelResponse = sendRequest(new GetRequest(url), 200);
+
+ assertEquals(Status.STATUS_OK, afterCancelResponse.getStatus());
+ String afterCancelJsonStr = afterCancelResponse.getContentAsString();
+ JSONObject afterCancelJson = new JSONObject(afterCancelJsonStr);
+ JSONArray afterCancelResult = afterCancelJson.getJSONArray("data");
+ assertNotNull(afterCancelResult);
+
+ assertTrue(afterCancelResult.length() == 0);
+ }
+
+ public void testWorkflowInstanceDelete() throws Exception
+ {
+ //Start workflow as USER1 and assign task to USER2.
+ personManager.setUser(USER1);
+ WorkflowDefinition adhocDef = workflowService.getDefinitionByName(getAdhocWorkflowDefinitionName());
+ Map params = new HashMap();
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Date dueDate = new Date();
+ params.put(WorkflowModel.PROP_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+ params.put(WorkflowModel.PROP_CONTEXT, packageRef);
+
+ WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
+ startTask = workflowService.endTask(startTask.getId(), null);
+
+ WorkflowInstance adhocInstance = startTask.getPath().getInstance();
+
+ // attempt to delete workflow as a user that is not the initiator
+ personManager.setUser(USER3);
+ String instanceId = adhocInstance.getId();
+ sendRequest(new DeleteRequest(URL_WORKFLOW_INSTANCES + "/" + instanceId), Status.STATUS_FORBIDDEN);
+
+ // make sure workflow instance is still present
+ assertNotNull(workflowService.getWorkflowById(instanceId));
+
+ // now delete as initiator of workflow
+ personManager.setUser(USER1);
+ sendRequest(new DeleteRequest(URL_WORKFLOW_INSTANCES + "/" + instanceId), Status.STATUS_OK);
+
+ WorkflowInstance instance = workflowService.getWorkflowById(instanceId);
+ if(instance!=null)
+ {
+ assertFalse("The deleted workflow is still active!", instance.isActive());
+ }
+
+ List instances = workflowService.getActiveWorkflows(adhocInstance.getDefinition().getId());
+ for (WorkflowInstance activeInstance : instances)
+ {
+ assertFalse(instanceId.equals(activeInstance.getId()));
+ }
+
+ // Try deleting an non-existent workflow instance, should result in 404
+ sendRequest(new DeleteRequest(URL_WORKFLOW_INSTANCES + "/" + instanceId), Status.STATUS_NOT_FOUND);
+ }
+
+ public void testReviewProcessFlow() throws Exception
+ {
+ // Approve path
+ runReviewFlow(true);
+
+ // Create package again, since WF is deleteds
+ packageRef = workflowService.createPackage(null);
+
+ // Reject path
+ runReviewFlow(false);
+ }
+
+ public void testReviewPooledProcessFlow() throws Exception
+ {
+ // Approve path
+ runReviewPooledFlow(true);
+
+ // Create package again, since WF is deleteds
+ packageRef = workflowService.createPackage(null);
+
+ // Reject path
+ runReviewPooledFlow(false);
+ }
+
+ protected void runReviewFlow(boolean approve) throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ // Start workflow as USER1
+ personManager.setUser(USER1);
+ WorkflowDefinition reviewDef = workflowService.getDefinitionByName(getReviewWorkflowDefinitionName());
+ Map params = new HashMap();
+ // Reviewer is USER2
+ params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
+ Date dueDate = new Date();
+ params.put(WorkflowModel.PROP_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+ params.put(WorkflowModel.PROP_CONTEXT, packageRef);
+
+ WorkflowPath reviewPath = workflowService.startWorkflow(reviewDef.getId(), params);
+ startedWorkflowId = reviewPath.getInstance().getId();
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(reviewPath.getId()).get(0);
+
+ // End start task
+ startTask = workflowService.endTask(startTask.getId(), null);
+
+ // Check of task is available in list of reviewer, USER2
+ personManager.setUser(USER2);
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONArray results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(1, results.length());
+
+ String taskId = results.getJSONObject(0).getString("id");
+
+ // Delegate approval/rejection to implementing engine-test
+ if(approve)
+ {
+ approveTask(taskId);
+ }
+ else
+ {
+ rejectTask(taskId);
+ }
+
+ // 'Approved'/'Rejected' task should be available for initiator
+ personManager.setUser(USER1);
+ response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER1)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(1, results.length());
+
+ // Correct task type check
+ String taskType = results.getJSONObject(0).getString("name");
+ if(approve)
+ {
+ assertEquals("wf:approvedTask", taskType);
+ }
+ else
+ {
+ assertEquals("wf:rejectedTask", taskType);
+ }
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+ }
+
+ protected void runReviewPooledFlow(boolean approve) throws Exception
+ {
+ String startedWorkflowId = null;
+ try
+ {
+ // Start workflow as USER1
+ personManager.setUser(USER1);
+ WorkflowDefinition reviewDef = workflowService.getDefinitionByName(getReviewPooledWorkflowDefinitionName());
+ Map params = new HashMap();
+
+ // Reviewer is group GROUP
+ params.put(WorkflowModel.ASSOC_GROUP_ASSIGNEE, groupManager.get(GROUP));
+ Date dueDate = new Date();
+ params.put(WorkflowModel.PROP_DUE_DATE, dueDate);
+ params.put(WorkflowModel.PROP_PRIORITY, 1);
+ params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
+ params.put(WorkflowModel.PROP_CONTEXT, packageRef);
+
+ WorkflowPath reviewPath = workflowService.startWorkflow(reviewDef.getId(), params);
+ startedWorkflowId = reviewPath.getInstance().getId();
+
+ WorkflowTask startTask = workflowService.getTasksForWorkflowPath(reviewPath.getId()).get(0);
+
+ // End start task
+ startTask = workflowService.endTask(startTask.getId(), null);
+
+ // Check if task is NOT available in list USER3, not a member of the group
+ personManager.setUser(USER3);
+ Response response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER3)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+ JSONArray results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(0, results.length());
+
+ // Check if task is available in list of reviewer, member of GROUP: USER2
+ personManager.setUser(USER2);
+ response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(1, results.length());
+
+ // Check if task is claimable and pooled
+ JSONObject taskJson = results.getJSONObject(0);
+ String taskId = taskJson.getString("id");
+ assertTrue(taskJson.getBoolean("isClaimable"));
+ assertTrue(taskJson.getBoolean("isPooled"));
+
+ // Claim task, using PUT, updating the owner
+ JSONObject properties = new JSONObject();
+ properties.put(qnameToString(ContentModel.PROP_OWNER), USER2);
+ sendRequest(new PutRequest(URL_TASKS + "/" + taskId, properties.toString(), "application/json"), 200);
+
+ // Check if task insn't claimable anymore
+ response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(1, results.length());
+
+ taskJson = results.getJSONObject(0);
+ assertFalse(taskJson.getBoolean("isClaimable"));
+ assertTrue(taskJson.getBoolean("isPooled"));
+
+
+ // Delegate approval/rejection to implementing engine-test
+ if(approve)
+ {
+ approveTask(taskId);
+ }
+ else
+ {
+ rejectTask(taskId);
+ }
+
+ // 'Approved'/'Rejected' task should be available for initiator
+ personManager.setUser(USER1);
+ response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER1)), 200);
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ jsonStr = response.getContentAsString();
+ json = new JSONObject(jsonStr);
+ results = json.getJSONArray("data");
+ assertNotNull(results);
+ assertEquals(1, results.length());
+
+ // Correct task type check
+ String taskType = results.getJSONObject(0).getString("name");
+ if(approve)
+ {
+ assertEquals("wf:approvedTask", taskType);
+ }
+ else
+ {
+ assertEquals("wf:rejectedTask", taskType);
+ }
+ }
+ finally
+ {
+ if(startedWorkflowId != null)
+ {
+ try {
+ // Cleanup
+ workflowService.deleteWorkflow(startedWorkflowId);
+ }
+ catch(Throwable t)
+ {
+ // Ignore exception while cleaning up
+ }
+ }
+ }
+
+ }
+
+ protected abstract void approveTask(String taskId) throws Exception;
+
+ protected abstract void rejectTask(String taskId) throws Exception;
+
+ protected abstract String getAdhocWorkflowDefinitionName();
+
+ protected abstract String getReviewWorkflowDefinitionName();
+
+ protected abstract String getReviewPooledWorkflowDefinitionName();
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ ApplicationContext appContext = getServer().getApplicationContext();
+
+ namespaceService = (NamespaceService) appContext.getBean("NamespaceService");
+ workflowService = (WorkflowService) appContext.getBean("WorkflowService");
+ MutableAuthenticationService authenticationService = (MutableAuthenticationService) appContext.getBean("AuthenticationService");
+ PersonService personService = (PersonService) appContext.getBean("PersonService");
+ SearchService searchService = (SearchService) appContext.getBean("SearchService");
+ FileFolderService fileFolderService = (FileFolderService) appContext.getBean("FileFolderService");
+ nodeService = (NodeService) appContext.getBean("NodeService");
+
+ AuthorityService authorityService = (AuthorityService) appContext.getBean("AuthorityService");
+ personManager = new TestPersonManager(authenticationService, personService, nodeService);
+ groupManager = new TestGroupManager(authorityService, searchService);
+
+ authenticationComponent = (AuthenticationComponent) appContext.getBean("authenticationComponent");
+
+ personManager.createPerson(USER1);
+ personManager.createPerson(USER2);
+ personManager.createPerson(USER3);
+
+ authenticationComponent.setSystemUserAsCurrentUser();
+
+ groupManager.addUserToGroup(GROUP, USER2);
+
+ packageRef = workflowService.createPackage(null);
+
+ NodeRef companyHome = searchService.selectNodes(nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE), COMPANY_HOME, null, namespaceService, false).get(0);
+
+ contentNodeRef = fileFolderService.create(companyHome, TEST_CONTENT + System.currentTimeMillis(), ContentModel.TYPE_CONTENT).getNodeRef();
+
+ authenticationComponent.clearCurrentSecurityContext();
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+ authenticationComponent.setSystemUserAsCurrentUser();
+ groupManager.clearGroups();
+ personManager.clearPeople();
+ authenticationComponent.clearCurrentSecurityContext();
+ }
+
+ private String qnameToString(QName qName)
+ {
+ String separator = Character.toString(QName.NAMESPACE_PREFIX);
+
+ return qName.toPrefixString(namespaceService).replaceFirst(separator, "_");
+ }
+
+ private void compareProperties(JSONObject before, JSONObject after) throws JSONException
+ {
+ for (String name : JSONObject.getNames(after))
+ {
+ if (before.has(name))
+ {
+ if (before.get(name) instanceof JSONArray)
+ {
+ for (int i = 0; i < before.getJSONArray(name).length(); i++)
+ {
+ assertEquals(before.getJSONArray(name).get(i), after.getJSONArray(name).get(i));
+ }
+ }
+ else
+ {
+ assertEquals(before.get(name), after.get(name));
+ }
+ }
+ }
+ }
+
+ private void checkSimpleWorkflowInstanceResponse(JSONObject json) throws JSONException
+ {
+ assertTrue(json.has("id"));
+ assertTrue(json.getString("id").length() > 0);
+
+ assertTrue(json.has("url"));
+ assertTrue(json.getString("url").startsWith(URL_WORKFLOW_INSTANCES));
+
+ assertTrue(json.has("name"));
+ assertTrue(json.getString("name").length() > 0);
+
+ assertTrue(json.has("title"));
+ assertTrue(json.getString("title").length() > 0);
+
+ assertTrue(json.has("description"));
+ assertTrue(json.getString("description").length() > 0);
+
+ assertTrue(json.has("isActive"));
+
+ assertTrue(json.has("startDate"));
+ assertTrue(json.getString("startDate").length() > 0);
+
+ assertTrue(json.has("endDate"));
+
+ assertTrue(json.has("initiator"));
+ Object initiator = json.get("initiator");
+ if (!initiator.equals(JSONObject.NULL))
+ {
+ assertTrue(((JSONObject) initiator).has("userName"));
+ assertTrue(((JSONObject) initiator).has("firstName"));
+ assertTrue(((JSONObject) initiator).has("lastName"));
+ }
+
+ assertTrue(json.has("definitionUrl"));
+ assertTrue(json.getString("definitionUrl").startsWith(URL_WORKFLOW_DEFINITIONS));
+ }
+
+ private void checkPriorityFiltering(String url) throws Exception
+ {
+ JSONObject json = getDataFromRequest(url);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+ assertTrue(result.length() > 0);
+
+ for(int i=0; i taskIds = Arrays.asList(ids);
+ JSONObject json = getDataFromRequest(url);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+
+ ArrayList resultIds = new ArrayList(result.length());
+ for(int i=0; i taskIds = Arrays.asList(ids);
+ JSONObject json = getDataFromRequest(url);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+
+ ArrayList resultIds = new ArrayList(result.length());
+ for(int i=0; i expectedProperties) throws Exception
+ {
+ JSONObject data = getDataFromRequest(MessageFormat.format(URL_USER_TASKS_PROPERTIES, USER2, propertiesParamValue));
+ JSONArray taskArray = data.getJSONArray("data");
+ assertEquals(1, taskArray.length());
+
+ JSONObject taskProperties = taskArray.getJSONObject(0).getJSONObject("properties");
+ assertNotNull(taskProperties);
+
+ int expectedNumberOfProperties = 0;
+ if(expectedProperties != null)
+ {
+ expectedNumberOfProperties = expectedProperties.size();
+ }
+ // Check right number of properties
+ assertEquals(expectedNumberOfProperties, taskProperties.length());
+
+ // Check if all properties are present
+ if(expectedProperties != null)
+ {
+ for(String prop : expectedProperties)
+ {
+ assertTrue(taskProperties.has(prop));
+ }
+ }
+ }
+
+ private void checkFiltering(String url) throws Exception
+ {
+ JSONObject json = getDataFromRequest(url);
+ JSONArray result = json.getJSONArray("data");
+ assertNotNull(result);
+
+ assertTrue(result.length() > 0);
+ }
+
+ private void checkPaging(String url, int totalItems, int maxItems, int skipCount) throws Exception
+ {
+ Response response = sendRequest(new GetRequest(url), 200);
+
+ assertEquals(Status.STATUS_OK, response.getStatus());
+ String jsonStr = response.getContentAsString();
+ JSONObject json = new JSONObject(jsonStr);
+
+ JSONArray data = json.getJSONArray("data");
+ JSONObject paging = json.getJSONObject("paging");
+
+ assertNotNull(data);
+ assertNotNull(paging);
+
+ assertTrue(data.length() >= 0);
+ assertTrue(data.length() <= maxItems);
+
+ assertEquals(totalItems, paging.getInt("totalItems"));
+ assertEquals(maxItems, paging.getInt("maxItems"));
+ assertEquals(skipCount, paging.getInt("skipCount"));
+ }
+}