SHA-1598: Added support to Get Task Instances API to filter result, based on the given property.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@130404 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jamal Kaabi-Mofrad
2016-09-07 13:06:32 +00:00
parent 59eed12c13
commit f46962577f
3 changed files with 190 additions and 67 deletions

View File

@@ -4,7 +4,7 @@
Lists all Workflow Task Instances associated with an authority and of a given State. 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. The list of returned tasks also includes pooled tasks which the specified authority is eligible to claim.
</description> </description>
<url>/api/task-instances?authority={authority?}&amp;state={state?}&amp;priority={priority?}&amp;pooledTasks={pooledTasks?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;properties={properties?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}&amp;exclude={exclude?}</url> <url>/api/task-instances?authority={authority?}&amp;state={state?}&amp;priority={priority?}&amp;pooledTasks={pooledTasks?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;properties={properties?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}&amp;exclude={exclude?}&amp;property={propQName/propValue?}</url>
<url>/api/workflow-instances/{workflow_instance_id}/task-instances?authority={authority?}&amp;state={state?}&amp;priority={priority?}&amp;dueBefore={isoDate?}&amp;dueAfter={isoDate?}&amp;properties={prop1, prop2, prop3...?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}&amp;exclude={exclude?}</url> <url>/api/workflow-instances/{workflow_instance_id}/task-instances?authority={authority?}&amp;state={state?}&amp;priority={priority?}&amp;dueBefore={isoDate?}&amp;dueAfter={isoDate?}&amp;properties={prop1, prop2, prop3...?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}&amp;exclude={exclude?}</url>
<format default="json"/> <format default="json"/>
<authentication>user</authentication> <authentication>user</authentication>
@@ -55,6 +55,10 @@
<shortname>workflow_instance_id</shortname> <shortname>workflow_instance_id</shortname>
<description>Restricts the returned tasks to those that belong to the given workflow process instance id.</description> <description>Restricts the returned tasks to those that belong to the given workflow process instance id.</description>
</arg> </arg>
<arg>
<shortname>property</shortname>
<description>Restricts the returned tasks to only those that match the given property. The property name should be a valid QName format followed by '/' then the property value. E.g. bpm:description/myDescription</description>
</arg>
</args> </args>
<responses> <responses>
<response> <response>

View File

@@ -1,30 +1,31 @@
/* /*
* #%L * #%L
* Alfresco Remote API * Alfresco Remote API
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2016 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.web.scripts.workflow; package org.alfresco.repo.web.scripts.workflow;
import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@@ -41,6 +42,8 @@ import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery; import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery.OrderBy; import org.alfresco.service.cmr.workflow.WorkflowTaskQuery.OrderBy;
import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.ModelUtil; import org.alfresco.util.ModelUtil;
import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
@@ -63,6 +66,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
public static final String PARAM_DUE_AFTER = "dueAfter"; public static final String PARAM_DUE_AFTER = "dueAfter";
public static final String PARAM_PROPERTIES = "properties"; public static final String PARAM_PROPERTIES = "properties";
public static final String PARAM_POOLED_TASKS = "pooledTasks"; public static final String PARAM_POOLED_TASKS = "pooledTasks";
public static final String PARAM_PROPERTY = "property";
public static final String VAR_WORKFLOW_INSTANCE_ID = "workflow_instance_id"; public static final String VAR_WORKFLOW_INSTANCE_ID = "workflow_instance_id";
private WorkflowTaskDueAscComparator taskComparator = new WorkflowTaskDueAscComparator(); private WorkflowTaskDueAscComparator taskComparator = new WorkflowTaskDueAscComparator();
@@ -96,6 +100,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
// get filter param values // get filter param values
filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY)); filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY));
filters.put(PARAM_PROPERTY, req.getParameter(PARAM_PROPERTY));
processDateFilter(req, PARAM_DUE_BEFORE, filters); processDateFilter(req, PARAM_DUE_BEFORE, filters);
processDateFilter(req, PARAM_DUE_AFTER, filters); processDateFilter(req, PARAM_DUE_AFTER, filters);
@@ -104,7 +109,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
{ {
filters.put(PARAM_EXCLUDE, new ExcludeFilter(excludeParam)); filters.put(PARAM_EXCLUDE, new ExcludeFilter(excludeParam));
} }
List<WorkflowTask> allTasks; List<WorkflowTask> allTasks;
if (workflowInstanceId != null) if (workflowInstanceId != null)
@@ -179,20 +184,18 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
ArrayList<Map<String, Object>> results = new ArrayList<Map<String, Object>>(); ArrayList<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
// Filter results // Filter results
WorkflowTask task = null; for (WorkflowTask task : allTasks)
for(int i=0; i<allTasks.size(); i++)
{ {
task = allTasks.get(i); if (matches(task, filters))
if (matches(task, filters))
{ {
// Total-count needs to be based on matching tasks only, so we can't just use allTasks.size() for this // Total-count needs to be based on matching tasks only, so we can't just use allTasks.size() for this
totalCount++; totalCount++;
if(totalCount > skipCount && (maxItems < 0 || maxItems > results.size())) if(totalCount > skipCount && (maxItems < 0 || maxItems > results.size()))
{ {
// Only build the actual detail if it's in the range of items we need. This will // Only build the actual detail if it's in the range of items we need. This will
// drastically improve performance over paging after building the model // drastically improve performance over paging after building the model
results.add(modelBuilder.buildSimple(task, properties)); results.add(modelBuilder.buildSimple(task, properties));
} }
} }
} }
@@ -341,12 +344,35 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
break; break;
} }
} }
else if(key.equals(PARAM_PROPERTY))
{
String[] propertyValuePair = filterValue.toString().split("/");
if (propertyValuePair.length != 2)
{
break;
}
QName propertyQName;
try
{
propertyQName = QName.createQName(propertyValuePair[0], namespaceService);
}
catch (NamespaceException ne)
{
break;
}
Serializable value = task.getProperties().get(propertyQName);
if (value != null && !value.equals(propertyValuePair[1]))
{
result = false;
break;
}
}
} }
} }
return result; return result;
} }
/** /**
* Comparator to sort workflow tasks by due date in ascending order. * Comparator to sort workflow tasks by due date in ascending order.
*/ */

View File

@@ -1,28 +1,28 @@
/* /*
* #%L * #%L
* Alfresco Remote API * Alfresco Remote API
* %% * %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited * Copyright (C) 2005 - 2016 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is * the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms: * provided under the following open source license terms:
* *
* Alfresco is free software: you can redistribute it and/or modify * 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 * 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 * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* Alfresco is distributed in the hope that it will be useful, * Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details. * GNU Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public License * You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.web.scripts.workflow; package org.alfresco.repo.web.scripts.workflow;
import java.io.Serializable; import java.io.Serializable;
@@ -305,7 +305,90 @@ public abstract class AbstractWorkflowRestApiTest extends BaseWebScriptTest
JSONArray resultArray = json.getJSONArray("data"); JSONArray resultArray = json.getJSONArray("data");
assertEquals(0, resultArray.length()); assertEquals(0, resultArray.length());
} }
public void testTaskInstancesGetWithFiltering() throws Exception
{
// Check USER2 starts with no tasks.
personManager.setUser(USER2);
Response response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
getJsonArray(response, 0);
// Start workflow as USER1 and assign the task to GROUP.
personManager.setUser(USER1);
WorkflowDefinition wfDefinition = workflowService.getDefinitionByName(getReviewPooledWorkflowDefinitionName());
Map<QName, Serializable> params = new HashMap<>(3);
params.put(WorkflowModel.ASSOC_GROUP_ASSIGNEE, groupManager.get(GROUP));
params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
params.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "descTest1");
WorkflowPath wfPath = workflowService.startWorkflow(wfDefinition.getId(), params);
String workflowId = wfPath.getInstance().getId();
workflows.add(workflowId);
WorkflowTask startTask = workflowService.getStartTask(workflowId);
workflowService.endTask(startTask.getId(), null);
// Start another workflow as USER1 and assign the task to GROUP.
wfDefinition = workflowService.getDefinitionByName(getReviewPooledWorkflowDefinitionName());
params.put(WorkflowModel.ASSOC_GROUP_ASSIGNEE, groupManager.get(GROUP));
params.put(WorkflowModel.ASSOC_PACKAGE, workflowService.createPackage(null));
params.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "descTest2");
wfPath = workflowService.startWorkflow(wfDefinition.getId(), params);
workflowId = wfPath.getInstance().getId();
workflows.add(workflowId);
startTask = workflowService.getStartTask(workflowId);
workflowService.endTask(startTask.getId(), null);
// Check USER2's tasks without filtering. It should return two tasks as USER2 is a member of the GROUP
personManager.setUser(USER2);
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2)), 200);
getJsonArray(response, 2);
//Check USER2's tasks With filtering where property bpm:description should match "descTest1"
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=bpm:description/descTest1"), 200);
JSONArray results = getJsonArray(response, 1);
JSONObject result = results.getJSONObject(0);
assertNotNull(result);
JSONObject properties = result.getJSONObject("properties");
assertNotNull(properties);
assertEquals("descTest1", properties.getString("bpm_description"));
//Check USER2's tasks With filtering where property bpm:description should match "descTest2"
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=bpm:description/descTest2"), 200);
results = getJsonArray(response, 1);
result = results.getJSONObject(0);
assertNotNull(result);
properties = result.getJSONObject("properties");
assertNotNull(properties);
assertEquals("descTest2", properties.getString("bpm_description"));
/*
* -ve tests
*/
// Mismatched property value - There is no task with the description "somePropValue"
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=bpm:description/somePropValue"), 200);
getJsonArray(response, 0);
//Unregistered namespace prefix (ignores "property" parameter)
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=unknownPrefix:description/test"), 200);
getJsonArray(response, 2);
// Nonexistent property (ignores "property" parameter)
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=bpm:nonexistentProp/test"), 200);
getJsonArray(response, 2);
// Not well-formed parameter
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER2) + "&property=bpm:description/"), 200);
getJsonArray(response, 2);
// Check USER3's tasks without filtering. It should return 0 task as USER3 is not a member of the GROUP
personManager.setUser(USER3);
response = sendRequest(new GetRequest(MessageFormat.format(URL_USER_TASKS, USER3)), 200);
getJsonArray(response, 0);
}
public void testWorkflowPermissions() throws Exception public void testWorkflowPermissions() throws Exception
{ {
// Start workflow as USER1 and assign task to USER1. // Start workflow as USER1 and assign task to USER1.
@@ -1785,7 +1868,17 @@ public abstract class AbstractWorkflowRestApiTest extends BaseWebScriptTest
assertEquals(maxItems, paging.getInt("maxItems")); assertEquals(maxItems, paging.getInt("maxItems"));
assertEquals(skipCount, paging.getInt("skipCount")); assertEquals(skipCount, paging.getInt("skipCount"));
} }
private JSONArray getJsonArray(Response response, int expectedLength) throws Exception
{
String jsonStr = response.getContentAsString();
JSONObject json = new JSONObject(jsonStr);
JSONArray results = json.getJSONArray("data");
assertNotNull(results);
assertEquals(expectedLength, results.length());
return results;
}
protected abstract String getEngine(); protected abstract String getEngine();
} }