- Added 'pooledTasks' parameter to GET task-instances REST API

- Added 'definitionId' parameter to GET workflow-instances REST API
- All date parameters now support a value of "null" or an empty string to indicate that a NULL date should be searched for

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21970 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2010-08-24 15:50:45 +00:00
parent 9564af6a2d
commit 8f0876a332
6 changed files with 214 additions and 41 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;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;properties={properties?}&amp;detailed={detailed?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</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;detailed={detailed?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</url>
<format default="json"/> <format default="json"/>
<authentication>user</authentication> <authentication>user</authentication>
<transaction allow="readonly">required</transaction> <transaction allow="readonly">required</transaction>

View File

@@ -1,7 +1,7 @@
<webscript> <webscript>
<shortname>Get Workflow Instance Collection</shortname> <shortname>Get Workflow Instance Collection</shortname>
<description>Retrieves all workflow instances, the returned list can be optionally filtered by the state of the workflow instance and by the authority that initiated the workflow instance.</description> <description>Retrieves all workflow instances, the returned list can be optionally filtered by the state of the workflow instance and by the authority that initiated the workflow instance.</description>
<url>/api/workflow-instances?state={state?}&amp;initiator={initiator?}&amp;priority={priority?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;startedBefore={startedBefore?}&amp;startedAfter={startedAfter?}&amp;completedBefore={completedBefore?}&amp;completedAfter={completedAfter?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</url> <url>/api/workflow-instances?state={state?}&amp;initiator={initiator?}&amp;priority={priority?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;definitionId={workflow_definition_id?}&amp;startedBefore={startedBefore?}&amp;startedAfter={startedAfter?}&amp;completedBefore={completedBefore?}&amp;completedAfter={completedAfter?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</url>
<url>/api/workflow-definitions/{workflow_definition_id}/workflow-instances?state={state?}&amp;initiator={initiator?}&amp;priority={priority?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;startedBefore={startedBefore?}&amp;startedAfter={startedAfter?}&amp;completedBefore={completedBefore?}&amp;completedAfter={completedAfter?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</url> <url>/api/workflow-definitions/{workflow_definition_id}/workflow-instances?state={state?}&amp;initiator={initiator?}&amp;priority={priority?}&amp;dueBefore={dueBefore?}&amp;dueAfter={dueAfter?}&amp;startedBefore={startedBefore?}&amp;startedAfter={startedAfter?}&amp;completedBefore={completedBefore?}&amp;completedAfter={completedAfter?}&amp;maxItems={maxItems?}&amp;skipCount={skipCount?}</url>
<format default="json"/> <format default="json"/>
<authentication>user</authentication> <authentication>user</authentication>

View File

@@ -46,6 +46,9 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
*/ */
public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript
{ {
public static final String NULL = "null";
public static final String EMPTY = "";
public static final String PARAM_MAX_ITEMS = "maxItems"; public static final String PARAM_MAX_ITEMS = "maxItems";
public static final String PARAM_SKIP_COUNT = "skipCount"; public static final String PARAM_SKIP_COUNT = "skipCount";
@@ -119,6 +122,36 @@ public abstract class AbstractWorkflowWebscript extends DeclarativeWebScript
WebScriptRequest req, WebScriptRequest req,
Status status, Cache cache); Status status, Cache cache);
/**
* Processes the given date filter parameter from the provided webscript request.
*
* If the parameter is present but set to an empty string or to "null" the
* date is added to the given filters Map as "", if the parameter
* contains an ISO8601 date it's added as a Date object to the filters.
*
* @param req The WebScript request
* @param paramName The name of the parameter to look for
* @param filters Map of filters to add the date to
*/
protected void processDateFilter(WebScriptRequest req, String paramName, Map<String, Object> filters)
{
// TODO: support other keywords i.e. today, tomorrow
String dateParam = req.getParameter(paramName);
if (dateParam != null)
{
Object date = EMPTY;
if (!EMPTY.equals(dateParam) && !NULL.equals(dateParam))
{
date = getDateParameter(req, paramName);
}
filters.put(paramName, date);
}
}
/** /**
* Retrieves the named paramter as a date. * Retrieves the named paramter as a date.
* *

View File

@@ -49,6 +49,7 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
public static final String PARAM_DUE_BEFORE = "dueBefore"; public static final String PARAM_DUE_BEFORE = "dueBefore";
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_DETAILED = "detailed"; public static final String PARAM_DETAILED = "detailed";
@Override @Override
@@ -58,24 +59,47 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
// authority is not included into filters list as it will be taken into account before filtering // authority is not included into filters list as it will be taken into account before filtering
String authority = getAuthority(req); String authority = getAuthority(req);
// state is also not included into filters list, for the same reason // state is also not included into filters list, for the same reason
WorkflowTaskState state = getState(req); WorkflowTaskState state = getState(req);
filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY));
filters.put(PARAM_DUE_BEFORE, getDateParameter(req, PARAM_DUE_BEFORE)); Boolean pooledTasksOnly = getPooledTasks(req);
filters.put(PARAM_DUE_AFTER, getDateParameter(req, PARAM_DUE_AFTER));
List<String> properties = getProperties(req); List<String> properties = getProperties(req);
boolean detailed = "true".equals(req.getParameter(PARAM_DETAILED)); boolean detailed = "true".equals(req.getParameter(PARAM_DETAILED));
// get filter param values
filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY));
processDateFilter(req, PARAM_DUE_BEFORE, filters);
processDateFilter(req, PARAM_DUE_AFTER, filters);
List<WorkflowTask> allTasks; List<WorkflowTask> allTasks;
if (authority != null) if (authority != null)
{ {
List<WorkflowTask> tasks = workflowService.getAssignedTasks(authority, state); List<WorkflowTask> tasks = workflowService.getAssignedTasks(authority, state);
List<WorkflowTask> pooledTasks = workflowService.getPooledTasks(authority); List<WorkflowTask> pooledTasks = workflowService.getPooledTasks(authority);
allTasks = new ArrayList<WorkflowTask>(tasks.size() + pooledTasks.size()); if (pooledTasksOnly != null)
allTasks.addAll(tasks); {
allTasks.addAll(pooledTasks); if (pooledTasksOnly.booleanValue())
{
// only return pooled tasks the user can claim
allTasks = new ArrayList<WorkflowTask>(pooledTasks.size());
allTasks.addAll(pooledTasks);
}
else
{
// only return tasks assigned to the user
allTasks = new ArrayList<WorkflowTask>(tasks.size());
allTasks.addAll(tasks);
}
}
else
{
// include both assigned and unassigned tasks
allTasks = new ArrayList<WorkflowTask>(tasks.size() + pooledTasks.size());
allTasks.addAll(tasks);
allTasks.addAll(pooledTasks);
}
} }
else else
{ {
@@ -107,6 +131,12 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
return createResultModel(modelBuilder, req, "taskInstances", results); return createResultModel(modelBuilder, req, "taskInstances", results);
} }
/**
* Retrieves the list of property names to include in the response.
*
* @param req The WebScript request
* @return List of property names
*/
private List<String> getProperties(WebScriptRequest req) private List<String> getProperties(WebScriptRequest req)
{ {
String propertiesStr = req.getParameter(PARAM_PROPERTIES); String propertiesStr = req.getParameter(PARAM_PROPERTIES);
@@ -116,7 +146,26 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
} }
return null; return null;
} }
/**
* Retrieves the pooledTasks parameter.
*
* @param req The WebScript request
* @return null if not present, Boolean object otherwise
*/
private Boolean getPooledTasks(WebScriptRequest req)
{
Boolean result = null;
String includePooledTasks = req.getParameter(PARAM_POOLED_TASKS);
if (includePooledTasks != null)
{
result = Boolean.valueOf(includePooledTasks);
}
return result;
}
/** /**
* Gets the specified {@link WorkflowTaskState}, defaults to IN_PROGRESS. * Gets the specified {@link WorkflowTaskState}, defaults to IN_PROGRESS.
* @param req * @param req
@@ -186,18 +235,38 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
{ {
Serializable dueDate = task.getProperties().get(WorkflowModel.PROP_DUE_DATE); Serializable dueDate = task.getProperties().get(WorkflowModel.PROP_DUE_DATE);
if (dueDate == null || ((Date) dueDate).getTime() <= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (dueDate == null)
{
matches = true;
}
}
else
{
if (dueDate == null || ((Date) dueDate).getTime() <= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_DUE_AFTER)) else if (key.equals(PARAM_DUE_AFTER))
{ {
Serializable dueDate = task.getProperties().get(WorkflowModel.PROP_DUE_DATE); Serializable dueDate = task.getProperties().get(WorkflowModel.PROP_DUE_DATE);
if (dueDate == null || ((Date) dueDate).getTime() >= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (dueDate == null)
{
matches = true;
}
}
else
{
if (dueDate == null || ((Date) dueDate).getTime() >= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_PRIORITY)) else if (key.equals(PARAM_PRIORITY))
@@ -214,5 +283,4 @@ public class TaskInstancesGet extends AbstractWorkflowWebscript
return result; return result;
} }
} }

View File

@@ -51,7 +51,8 @@ public class WorkflowInstancesGet extends AbstractWorkflowWebscript
public static final String PARAM_STARTED_AFTER = "startedAfter"; public static final String PARAM_STARTED_AFTER = "startedAfter";
public static final String PARAM_COMPLETED_BEFORE = "completedBefore"; public static final String PARAM_COMPLETED_BEFORE = "completedBefore";
public static final String PARAM_COMPLETED_AFTER = "completedAfter"; public static final String PARAM_COMPLETED_AFTER = "completedAfter";
public static final String PARAM_DEFINITION_ID = "workflow_definition_id"; public static final String PARAM_DEFINITION_ID = "definitionId";
public static final String VAR_DEFINITION_ID = "workflow_definition_id";
@Override @Override
protected Map<String, Object> buildModel(WorkflowModelBuilder modelBuilder, WebScriptRequest req, Status status, Cache cache) protected Map<String, Object> buildModel(WorkflowModelBuilder modelBuilder, WebScriptRequest req, Status status, Cache cache)
@@ -63,14 +64,21 @@ public class WorkflowInstancesGet extends AbstractWorkflowWebscript
filters.put(PARAM_STATE, req.getParameter(PARAM_STATE)); filters.put(PARAM_STATE, req.getParameter(PARAM_STATE));
filters.put(PARAM_INITIATOR, req.getParameter(PARAM_INITIATOR)); filters.put(PARAM_INITIATOR, req.getParameter(PARAM_INITIATOR));
filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY)); filters.put(PARAM_PRIORITY, req.getParameter(PARAM_PRIORITY));
filters.put(PARAM_DUE_BEFORE, getDateParameter(req, PARAM_DUE_BEFORE));
filters.put(PARAM_DUE_AFTER, getDateParameter(req, PARAM_DUE_AFTER)); // process all the date related parameters
filters.put(PARAM_STARTED_BEFORE, getDateParameter(req, PARAM_STARTED_BEFORE)); processDateFilter(req, PARAM_DUE_BEFORE, filters);
filters.put(PARAM_STARTED_AFTER, getDateParameter(req, PARAM_STARTED_AFTER)); processDateFilter(req, PARAM_DUE_AFTER, filters);
filters.put(PARAM_COMPLETED_BEFORE, getDateParameter(req, PARAM_COMPLETED_BEFORE)); processDateFilter(req, PARAM_STARTED_BEFORE, filters);
filters.put(PARAM_COMPLETED_AFTER, getDateParameter(req, PARAM_COMPLETED_AFTER)); processDateFilter(req, PARAM_STARTED_AFTER, filters);
processDateFilter(req, PARAM_COMPLETED_BEFORE, filters);
processDateFilter(req, PARAM_COMPLETED_AFTER, filters);
String workflowDefinitionId = params.get(PARAM_DEFINITION_ID); // determine if there is a definition id to filter by
String workflowDefinitionId = params.get(VAR_DEFINITION_ID);
if (workflowDefinitionId == null)
{
workflowDefinitionId = req.getParameter(PARAM_DEFINITION_ID);
}
List<WorkflowInstance> workflows = new ArrayList<WorkflowInstance>(); List<WorkflowInstance> workflows = new ArrayList<WorkflowInstance>();
@@ -148,9 +156,19 @@ public class WorkflowInstancesGet extends AbstractWorkflowWebscript
WorkflowTask startTask = modelBuilder.getStartTaskForWorkflow(workflowInstance); WorkflowTask startTask = modelBuilder.getStartTaskForWorkflow(workflowInstance);
Serializable dueDate = startTask.getProperties().get(WorkflowModel.PROP_WORKFLOW_DUE_DATE); Serializable dueDate = startTask.getProperties().get(WorkflowModel.PROP_WORKFLOW_DUE_DATE);
if (dueDate == null || ((Date) dueDate).getTime() <= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (dueDate == null)
{
matches = true;
}
}
else
{
if (dueDate != null && ((Date) dueDate).getTime() <= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_DUE_AFTER)) else if (key.equals(PARAM_DUE_AFTER))
@@ -158,51 +176,101 @@ public class WorkflowInstancesGet extends AbstractWorkflowWebscript
WorkflowTask startTask = modelBuilder.getStartTaskForWorkflow(workflowInstance); WorkflowTask startTask = modelBuilder.getStartTaskForWorkflow(workflowInstance);
Serializable dueDate = startTask.getProperties().get(WorkflowModel.PROP_WORKFLOW_DUE_DATE); Serializable dueDate = startTask.getProperties().get(WorkflowModel.PROP_WORKFLOW_DUE_DATE);
if (dueDate == null || ((Date) dueDate).getTime() >= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (dueDate == null)
{
matches = true;
}
}
else
{
if (dueDate != null && ((Date) dueDate).getTime() >= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_STARTED_BEFORE)) else if (key.equals(PARAM_STARTED_BEFORE))
{ {
Date startDate = workflowInstance.getStartDate(); Date startDate = workflowInstance.getStartDate();
if (startDate == null || startDate.getTime() <= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (startDate == null)
{
matches = true;
}
}
else
{
if (startDate != null && startDate.getTime() <= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_STARTED_AFTER)) else if (key.equals(PARAM_STARTED_AFTER))
{ {
Date startDate = workflowInstance.getStartDate(); Date startDate = workflowInstance.getStartDate();
if (startDate == null || startDate.getTime() >= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (startDate == null)
{
matches = true;
}
}
else
{
if (startDate != null && startDate.getTime() >= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_COMPLETED_BEFORE)) else if (key.equals(PARAM_COMPLETED_BEFORE))
{ {
Date endDate = workflowInstance.getEndDate(); Date endDate = workflowInstance.getEndDate();
if (endDate == null || endDate.getTime() <= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (endDate == null)
{
matches = true;
}
}
else
{
if (endDate != null && endDate.getTime() <= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_COMPLETED_AFTER)) else if (key.equals(PARAM_COMPLETED_AFTER))
{ {
Date endDate = workflowInstance.getEndDate(); Date endDate = workflowInstance.getEndDate();
if (endDate == null || endDate.getTime() >= ((Date) filterValue).getTime()) if (filterValue.equals(EMPTY))
{ {
matches = true; if (endDate == null)
{
matches = true;
}
}
else
{
if (endDate != null && endDate.getTime() >= ((Date) filterValue).getTime())
{
matches = true;
}
} }
} }
else if (key.equals(PARAM_INITIATOR)) else if (key.equals(PARAM_INITIATOR))
{ {
if (workflowInstance.getInitiator() != null && nodeService.exists(workflowInstance.getInitiator()) if (workflowInstance.getInitiator() != null && nodeService.exists(workflowInstance.getInitiator()) &&
&& filterValue.equals(nodeService.getProperty(workflowInstance.getInitiator(), ContentModel.PROP_USERNAME))) filterValue.equals(nodeService.getProperty(workflowInstance.getInitiator(), ContentModel.PROP_USERNAME)))
{ {
matches = true; matches = true;
} }
@@ -216,6 +284,7 @@ public class WorkflowInstancesGet extends AbstractWorkflowWebscript
matches = true; matches = true;
} }
} }
// update global result // update global result
result = result || matches; result = result || matches;
} }

View File

@@ -435,6 +435,7 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
assertTrue(tasks.length() > 1); assertTrue(tasks.length() > 1);
} }
@SuppressWarnings("deprecation")
public void testWorkflowInstancesGet() throws Exception public void testWorkflowInstancesGet() throws Exception
{ {
//Start workflow as USER1 and assign task to USER2. //Start workflow as USER1 and assign task to USER2.
@@ -443,15 +444,15 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
Map<QName, Serializable> params = new HashMap<QName, Serializable>(); Map<QName, Serializable> params = new HashMap<QName, Serializable>();
params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2)); params.put(WorkflowModel.ASSOC_ASSIGNEE, personManager.get(USER2));
Date dueDate = new Date(); Date dueDate = new Date();
params.put(WorkflowModel.PROP_DUE_DATE, dueDate); params.put(WorkflowModel.PROP_WORKFLOW_DUE_DATE, dueDate);
params.put(WorkflowModel.PROP_WORKFLOW_PRIORITY, 1); params.put(WorkflowModel.PROP_WORKFLOW_PRIORITY, 1);
params.put(WorkflowModel.ASSOC_PACKAGE, packageRef); params.put(WorkflowModel.ASSOC_PACKAGE, packageRef);
params.put(WorkflowModel.PROP_CONTEXT, packageRef); params.put(WorkflowModel.PROP_CONTEXT, packageRef);
WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params); WorkflowPath adhocPath = workflowService.startWorkflow(adhocDef.getId(), params);
WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0); WorkflowTask startTask = workflowService.getTasksForWorkflowPath(adhocPath.getId()).get(0);
WorkflowInstance adhocInstance = startTask.getPath().getInstance(); WorkflowInstance adhocInstance = startTask.getPath().getInstance();
workflowService.endTask(startTask.getId(), null);
// Get Workflow Instance Collection // Get Workflow Instance Collection
Response response = sendRequest(new GetRequest(URL_WORKFLOW_INSTANCES), 200); Response response = sendRequest(new GetRequest(URL_WORKFLOW_INSTANCES), 200);
@@ -489,7 +490,9 @@ public class WorkflowRestApiTest extends BaseWebScriptTest
checkFiltering(URL_WORKFLOW_INSTANCES + "?startedBefore=" + ISO8601DateFormat.format(adhocInstance.getStartDate())); checkFiltering(URL_WORKFLOW_INSTANCES + "?startedBefore=" + ISO8601DateFormat.format(adhocInstance.getStartDate()));
// filter by dueAfter // filter by dueAfter
checkFiltering(URL_WORKFLOW_INSTANCES + "?dueAfter=" + ISO8601DateFormat.format(dueDate)); Date anHourAgo = new Date(dueDate.getTime());
anHourAgo.setHours(anHourAgo.getHours()-1);
checkFiltering(URL_WORKFLOW_INSTANCES + "?dueAfter=" + ISO8601DateFormat.format(anHourAgo));
// filter by dueBefore // filter by dueBefore
checkFiltering(URL_WORKFLOW_INSTANCES + "?dueBefore=" + ISO8601DateFormat.format(dueDate)); checkFiltering(URL_WORKFLOW_INSTANCES + "?dueBefore=" + ISO8601DateFormat.format(dueDate));