From f46962577fd43e3d78faa7be67840f07e38dc1b3 Mon Sep 17 00:00:00 2001 From: Jamal Kaabi-Mofrad Date: Wed, 7 Sep 2016 13:06:32 +0000 Subject: [PATCH] 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 --- .../workflow/task-instances.get.desc.xml | 6 +- .../scripts/workflow/TaskInstancesGet.java | 104 ++++++++----- .../workflow/AbstractWorkflowRestApiTest.java | 147 ++++++++++++++---- 3 files changed, 190 insertions(+), 67 deletions(-) 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 index d6ae21b735..3c633a713a 100644 --- 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 @@ -4,7 +4,7 @@ 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-instances?authority={authority?}&state={state?}&priority={priority?}&pooledTasks={pooledTasks?}&dueBefore={dueBefore?}&dueAfter={dueAfter?}&properties={properties?}&maxItems={maxItems?}&skipCount={skipCount?}&exclude={exclude?} + /api/task-instances?authority={authority?}&state={state?}&priority={priority?}&pooledTasks={pooledTasks?}&dueBefore={dueBefore?}&dueAfter={dueAfter?}&properties={properties?}&maxItems={maxItems?}&skipCount={skipCount?}&exclude={exclude?}&property={propQName/propValue?} /api/workflow-instances/{workflow_instance_id}/task-instances?authority={authority?}&state={state?}&priority={priority?}&dueBefore={isoDate?}&dueAfter={isoDate?}&properties={prop1, prop2, prop3...?}&maxItems={maxItems?}&skipCount={skipCount?}&exclude={exclude?} user @@ -55,6 +55,10 @@ workflow_instance_id Restricts the returned tasks to those that belong to the given workflow process instance id. + + property + 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 + diff --git a/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java b/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java index e50df02ca2..11c844f78e 100644 --- a/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java +++ b/source/java/org/alfresco/repo/web/scripts/workflow/TaskInstancesGet.java @@ -1,30 +1,31 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.web.scripts.workflow; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; 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.OrderBy; 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.springframework.extensions.webscripts.Cache; 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_PROPERTIES = "properties"; 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"; private WorkflowTaskDueAscComparator taskComparator = new WorkflowTaskDueAscComparator(); @@ -96,6 +100,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript // get filter param values 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_AFTER, filters); @@ -104,7 +109,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript { filters.put(PARAM_EXCLUDE, new ExcludeFilter(excludeParam)); } - + List allTasks; if (workflowInstanceId != null) @@ -179,20 +184,18 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript ArrayList> results = new ArrayList>(); // Filter results - WorkflowTask task = null; - for(int i=0; i skipCount && (maxItems < 0 || maxItems > results.size())) - { - // 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 - results.add(modelBuilder.buildSimple(task, properties)); - } + // Total-count needs to be based on matching tasks only, so we can't just use allTasks.size() for this + totalCount++; + 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 + // drastically improve performance over paging after building the model + results.add(modelBuilder.buildSimple(task, properties)); + } } } @@ -341,12 +344,35 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript 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; } - + /** * Comparator to sort workflow tasks by due date in ascending order. */ diff --git a/source/test-java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java b/source/test-java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java index 22b4ad1d29..28bdd30bd3 100644 --- a/source/test-java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java +++ b/source/test-java/org/alfresco/repo/web/scripts/workflow/AbstractWorkflowRestApiTest.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Remote API - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * 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 . - * #L% - */ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ package org.alfresco.repo.web.scripts.workflow; import java.io.Serializable; @@ -305,7 +305,90 @@ public abstract class AbstractWorkflowRestApiTest extends BaseWebScriptTest JSONArray resultArray = json.getJSONArray("data"); 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 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 { // 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(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(); }