From a77ce7d43384e4c59a3e77ec2bb3b86fc134a91e Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 2 Mar 2016 23:59:05 +0100 Subject: [PATCH] Work items in task wf context. --- .../midpoint/schema/SelectorOptions.java | 6 +- .../ns/public/common/common-workflows-3.xsd | 166 ++++++++++++++++++ .../activiti/dao/ProcessInstanceProvider.java | 19 +- .../impl/activiti/dao/WorkItemProvider.java | 81 ++++++++- .../midpoint/wf/impl/jobs/WfUtil.java | 10 ++ .../common/CommonProcessVariableNames.java | 8 + .../wf/impl/TestUserChangeApproval.java | 22 ++- 7 files changed, 294 insertions(+), 18 deletions(-) diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/SelectorOptions.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/SelectorOptions.java index 19f6ac7a0da..16d854e78d4 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/SelectorOptions.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/SelectorOptions.java @@ -28,10 +28,7 @@ import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.path.ItemPathSegment; import com.evolveum.midpoint.prism.path.NameItemPathSegment; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AccessCertificationCampaignType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.LookupTableType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; /** * @author semancik @@ -144,6 +141,7 @@ public static boolean hasToLoadPath(QName itemName, Collection + + + + Currently open work items for this process. + + + @@ -913,5 +920,164 @@ + + + + WorkItemType contains information about a human task (or a notification). + + + + + + + + + + Internal identifier of a work item, assigned by the underlying workflow engine + (currently Activiti). MidPoint WorkItem is Activiti Task. So workItemId is + taskId. + + + + + + + Descriptive name of the work item. E.g. "Assign role1 to user1". + + + + + + + When was the operation requested, i.e. when the approval process started? + + (Normally, this information is relevant at the task/wf-process level. + However we put it here to avoid fetching tasks when we want to display + work item list only.) + + + + + + + When was this work item created? + + + + + + + Task that wraps process instance this work item is part of. + + + tns:TaskType + + + + + + + User to which the work item is assigned. Other users do not see such a work item in their work lists. + + + tns:UserType + + + + + + + Users to which the work item MAY BE assigned. + + + tns:UserType + + + + + + + Organizations/roles to users of which the work item MAY BE assigned. + + + tns:AbstractRoleType + + + + + + + Object that is being modified (added, deleted) by the operation requested. + Typically a user, but might be also a role, org, resource, etc. + + (Normally, this information is relevant at the task/wf-process level. + However we put it here to avoid fetching tasks when we want to display + work item list only.) + + + tns:ObjectType + + + + + + + Object that is being attached to/detached from the object modified. + Typically a role but might be also a resource, org, ... or it might be null. + + (Normally, this information is relevant at the task/wf-process level. + However we put it here to avoid fetching tasks when we want to display + work item list only.) + + + tns:ObjectType + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/ProcessInstanceProvider.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/ProcessInstanceProvider.java index 7e9925eb4ca..8dcca668737 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/ProcessInstanceProvider.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/ProcessInstanceProvider.java @@ -17,6 +17,7 @@ package com.evolveum.midpoint.wf.impl.activiti.dao; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.SearchResultList; @@ -62,6 +63,9 @@ import javax.xml.bind.JAXBException; import java.util.*; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_WORK_ITEM; + /** * @author mederly */ @@ -222,7 +226,7 @@ private WfContextType getWfContextType(String instanceId, boolean getWorkItems, if (historicProcessInstance == null) { throw new ObjectNotFoundException("Process instance " + instanceId + " couldn't be found."); } else { - return activitiToMidpointWfContextHistory(historicProcessInstance, result); + return activitiToMidpointWfContextHistory(historicProcessInstance, getWorkItems, result); } } @@ -349,7 +353,7 @@ public WfProcessInstanceType activitiToMidpointProcessInstanceHistory(HistoricPr } } - private WfContextType activitiToMidpointWfContextHistory(HistoricProcessInstance instance, OperationResult result) + private WfContextType activitiToMidpointWfContextHistory(HistoricProcessInstance instance, boolean getWorkItems, OperationResult result) throws WorkflowException, SchemaException { WfContextType wfc = new WfContextType(); @@ -377,7 +381,13 @@ private WfContextType activitiToMidpointWfContextHistory(HistoricProcessInstance wfc.setProcessorSpecificState(cp.externalizeProcessorSpecificState(vars)); wfc.setProcessSpecificState(pmi.externalizeProcessSpecificState(vars)); - //wfc.setState(cp.externalizeProcessInstanceState(vars).asObjectable()); + if (getWorkItems) { + TaskService ts = activitiEngine.getTaskService(); + List tasks = ts.createTaskQuery() + .processInstanceId(instance.getId()) + .list(); + wfc.getWorkItem().addAll(workItemProvider.tasksToWorkItemsNew(tasks, vars, false, true, true, result)); // "no" to task forms, "yes" to assignee and candidate details + } return wfc; } @@ -405,7 +415,8 @@ public void augmentTaskObject(PrismObject object, if (instanceId == null) { throw new SchemaException("No process instance ID in workflow context"); } - WfContextType wfContextType = getWfContextType(instanceId, false, result); + boolean retrieveWorkItems = SelectorOptions.hasToLoadPath(new ItemPath(F_WORKFLOW_CONTEXT, F_WORK_ITEM), options); + WfContextType wfContextType = getWfContextType(instanceId, retrieveWorkItems, result); taskType.setWorkflowContext(wfContextType); } catch (RuntimeException|SchemaException|ObjectNotFoundException|WorkflowException e) { result.recordFatalError(e.getMessage(), e); diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemProvider.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemProvider.java index f65b193f6f3..fc2086853b7 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemProvider.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemProvider.java @@ -21,9 +21,11 @@ import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.repo.api.RepositoryService; +import com.evolveum.midpoint.schema.constants.ObjectTypes; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; import com.evolveum.midpoint.util.exception.SchemaException; import com.evolveum.midpoint.util.exception.SystemException; @@ -36,16 +38,13 @@ import com.evolveum.midpoint.wf.impl.WorkflowManagerImpl; import com.evolveum.midpoint.wf.impl.activiti.ActivitiEngine; import com.evolveum.midpoint.wf.impl.activiti.ActivitiEngineDataHelper; +import com.evolveum.midpoint.wf.impl.jobs.WfUtil; import com.evolveum.midpoint.wf.impl.messages.TaskEvent; import com.evolveum.midpoint.wf.impl.processes.common.CommonProcessVariableNames; +import com.evolveum.midpoint.wf.impl.processes.common.LightweightObjectRef; import com.evolveum.midpoint.wf.impl.processors.ChangeProcessor; import com.evolveum.midpoint.wf.impl.util.MiscDataUtil; -import com.evolveum.midpoint.xml.ns._public.common.common_3.AbstractRoleType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TrackingDataType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import com.evolveum.prism.xml.ns._public.types_3.PolyStringType; import org.activiti.engine.ActivitiException; @@ -218,6 +217,19 @@ List tasksToWorkItems(List tasks, boolean getTaskDetails, bo return retval; } + List tasksToWorkItemsNew(List tasks, Map processVariables, boolean getTaskDetails, boolean getAssigneeDetails, + boolean getCandidateDetails, OperationResult result) { + List retval = new ArrayList<>(); + for (Task task : tasks) { + try { + retval.add(taskToWorkItemNew(task, processVariables, getTaskDetails, getAssigneeDetails, getCandidateDetails, result)); + } catch (WorkflowException e) { + LoggingUtils.logException(LOGGER, "Couldn't get information on activiti task {}", e, task.getId()); + } + } + return retval; + } + // should not throw ActivitiException /** @@ -291,6 +303,15 @@ private class TaskExtract { candidateGroups = task.getCandidateGroups(); } + public TaskExtract(Task task, Map processVariables) { + this(task); + for (Map.Entry variable: processVariables.entrySet()) { + if (!variables.containsKey(variable.getKey())) { + variables.put(variable.getKey(), variable.getValue()); + } + } + } + String getId() { return id; } @@ -378,6 +399,24 @@ private WorkItemType taskToWorkItem(Task task, boolean getTaskDetails, boolean g return wi; } + private WorkItemNewType taskToWorkItemNew(Task task, Map processVariables, boolean getTaskDetails, boolean getAssigneeDetails, + boolean getCandidateDetails, OperationResult parentResult) throws WorkflowException { + OperationResult result = parentResult.createSubresult(OPERATION_ACTIVITI_TASK_TO_WORK_ITEM); + result.addParam("task id", task.getId()); + result.addParam("getTaskDetails", getTaskDetails); + result.addParam("getAssigneeDetails", getAssigneeDetails); + + try { + TaskExtract taskExtract = new TaskExtract(task, processVariables); + WorkItemNewType wi = taskExtractToWorkItemNew(taskExtract, getAssigneeDetails, getCandidateDetails, result); + return wi; + } catch (RuntimeException|WorkflowException e) { + throw e; + } finally { + result.computeStatusIfUnknown(); + } + } + // this method should reside outside activiti-related packages // we'll deal with it when we implement support for multiple wf providers public WorkItemType taskEventToWorkItem(TaskEvent taskEvent, boolean getAssigneeDetails, boolean getCandidateDetails, OperationResult parentResult) throws WorkflowException { @@ -437,6 +476,36 @@ private WorkItemType taskExtractToWorkItem(TaskExtract task, boolean getAssignee return wi; } + private WorkItemNewType taskExtractToWorkItemNew(TaskExtract task, boolean getAssigneeDetails, boolean getCandidateDetails, OperationResult result) throws WorkflowException { + WorkItemNewType wi = new WorkItemNewType(prismContext); + try { + final Map variables = task.getVariables(); + + wi.setWorkItemId(task.getId()); + wi.setName(task.getName()); + wi.setWorkItemCreatedTimestamp(XmlTypeConverter.createXMLGregorianCalendar(task.getCreateTime())); + wi.setProcessStartedTimestamp(XmlTypeConverter.createXMLGregorianCalendar((Date) variables.get(CommonProcessVariableNames.VARIABLE_START_TIME))); + String taskOid = (String) variables.get(CommonProcessVariableNames.VARIABLE_MIDPOINT_TASK_OID); + if (taskOid != null) { + wi.setTaskRef(ObjectTypeUtil.createObjectRef(taskOid, ObjectTypes.TASK)); + } + if (task.getAssignee() != null) { + wi.setAssigneeRef(MiscSchemaUtil.createObjectReference(task.getAssignee(), SchemaConstants.C_USER_TYPE)); + } + for (String candidateUser : task.getCandidateUsers()) { + wi.getCandidateUsersRef().add(MiscSchemaUtil.createObjectReference(candidateUser, SchemaConstants.C_USER_TYPE)); + } + for (String candidateGroup : task.getCandidateGroups()) { + wi.getCandidateRolesRef().add(miscDataUtil.groupIdToObjectReference(candidateGroup)); + } + wi.setObjectRef(WfUtil.toObjectReferenceType((LightweightObjectRef) variables.get(CommonProcessVariableNames.VARIABLE_OBJECT_REF))); + wi.setTargetRef(WfUtil.toObjectReferenceType((LightweightObjectRef) variables.get(CommonProcessVariableNames.VARIABLE_TARGET_REF))); + } catch (ActivitiException e) { // not sure if any of the above methods can throw this exception, but for safety we catch it here + throw new WorkflowException("Couldn't get information on activiti task " + task.getId(), e); + } + return wi; + } + private T asObjectable(PrismObject prismObject) { return prismObject != null ? prismObject.asObjectable() : null; } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/jobs/WfUtil.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/jobs/WfUtil.java index f7e1f81f807..51c2404ce5e 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/jobs/WfUtil.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/jobs/WfUtil.java @@ -1,7 +1,9 @@ package com.evolveum.midpoint.wf.impl.jobs; import com.evolveum.midpoint.wf.impl.processes.common.CommonProcessVariableNames; +import com.evolveum.midpoint.wf.impl.processes.common.LightweightObjectRef; import com.evolveum.midpoint.wf.impl.processes.itemApproval.ProcessVariableNames; +import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType; import java.util.Map; @@ -12,4 +14,12 @@ */ public class WfUtil { + public static ObjectReferenceType toObjectReferenceType(LightweightObjectRef ref) { + if (ref != null) { + return ref.toObjectReferenceType(); + } else { + return null; + } + } + } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/CommonProcessVariableNames.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/CommonProcessVariableNames.java index 97d8d4e14ab..b87a999bb18 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/CommonProcessVariableNames.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/CommonProcessVariableNames.java @@ -66,4 +66,12 @@ public class CommonProcessVariableNames { // Name of process interface bean (ProcessMidPointInterface implementation) that is related to this process [String] public static final String VARIABLE_MIDPOINT_PROCESS_INTERFACE_BEAN_NAME = "midPointProcessInterfaceBeanName"; + + // TODO fill-in these variables! + + // Object of the operation - if can be specified like this [LightweightObjectRef] + public static final String VARIABLE_OBJECT_REF = "objectRef"; + + // Target of the operation - if any [LightweightObjectRef] + public static final String VARIABLE_TARGET_REF = "targetRef"; } diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/TestUserChangeApproval.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/TestUserChangeApproval.java index 3aecccd84e8..5400b32e948 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/TestUserChangeApproval.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/TestUserChangeApproval.java @@ -33,6 +33,9 @@ import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.prism.xnode.PrimitiveXNode; +import com.evolveum.midpoint.schema.GetOperationOptions; +import com.evolveum.midpoint.schema.RetrieveOption; +import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.task.api.Task; @@ -67,7 +70,10 @@ import java.io.File; import java.util.*; +import static com.evolveum.midpoint.schema.GetOperationOptions.createRetrieve; import static com.evolveum.midpoint.test.IntegrationTestTools.display; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType.F_WORKFLOW_CONTEXT; +import static com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType.F_WORK_ITEM; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; @@ -155,14 +161,18 @@ boolean decideOnApproval(String executionId) throws Exception { } protected void assertWfContextAfterClockworkRun(Task rootTask, List subtasks, OperationResult result, String... processNames) throws Exception { + + final Collection> options = + SelectorOptions.createCollection(new ItemPath(F_WORKFLOW_CONTEXT, F_WORK_ITEM), createRetrieve()); + Task opTask = taskManager.createTaskInstance(); - TaskType rootTaskType = modelService.getObject(TaskType.class, rootTask.getOid(), null, opTask, result).asObjectable(); + TaskType rootTaskType = modelService.getObject(TaskType.class, rootTask.getOid(), options, opTask, result).asObjectable(); assertNull("Unexpected workflow context in root task: " + rootTaskType, rootTaskType.getWorkflowContext()); assertEquals("Wrong # of wf subtasks w.r.t processNames (" + Arrays.asList(processNames) + ")", processNames.length, subtasks.size()); int i = 0; for (Task subtask : subtasks) { - TaskType subtaskType = modelService.getObject(TaskType.class, subtask.getOid(), null, opTask, result).asObjectable(); + TaskType subtaskType = modelService.getObject(TaskType.class, subtask.getOid(), options, opTask, result).asObjectable(); display("Subtask #"+(i+1)+": ", subtaskType); assertNull("Unexpected fetch result in wf subtask: " + subtask, subtaskType.getFetchResult()); WfContextType wfc = subtaskType.getWorkflowContext(); @@ -178,14 +188,18 @@ protected void assertWfContextAfterClockworkRun(Task rootTask, List subtas } protected void assertWfContextAfterRootTaskFinishes(Task rootTask, List subtasks, OperationResult result, String... processNames) throws Exception { + + final Collection> options = + SelectorOptions.createCollection(new ItemPath(F_WORKFLOW_CONTEXT, F_WORK_ITEM), createRetrieve()); + Task opTask = taskManager.createTaskInstance(); - TaskType rootTaskType = modelService.getObject(TaskType.class, rootTask.getOid(), null, opTask, result).asObjectable(); + TaskType rootTaskType = modelService.getObject(TaskType.class, rootTask.getOid(), options, opTask, result).asObjectable(); assertNull("Unexpected workflow context in root task: " + rootTaskType, rootTaskType.getWorkflowContext()); assertEquals("Wrong # of wf subtasks w.r.t processNames (" + Arrays.asList(processNames) + ")", processNames.length, subtasks.size()); int i = 0; for (Task subtask : subtasks) { - TaskType subtaskType = modelService.getObject(TaskType.class, subtask.getOid(), null, opTask, result).asObjectable(); + TaskType subtaskType = modelService.getObject(TaskType.class, subtask.getOid(), options, opTask, result).asObjectable(); display("Subtask #"+(i+1)+": ", subtaskType); assertNull("Unexpected fetch result in wf subtask: " + subtask, subtaskType.getFetchResult()); WfContextType wfc = subtaskType.getWorkflowContext();