From cb2fcde9294e0ed6244edfd3a89a451b4c6838a2 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Fri, 3 Feb 2017 00:43:11 +0100 Subject: [PATCH] Primitive escalation implementation. --- .../midpoint/schema/util/ObjectTypeUtil.java | 9 + .../ns/public/common/common-workflows-3.xsd | 184 +++++++++++++----- .../test/AbstractModelIntegrationTest.java | 17 +- .../api/events/WorkItemEvent.java | 17 +- .../api/events/WorkflowEventCreator.java | 4 + .../notifications/impl/WorkflowListener.java | 11 +- .../workflow/DefaultWorkflowEventCreator.java | 11 +- .../midpoint/wf/api/WorkItemListener.java | 19 +- .../midpoint/wf/util/ApprovalUtils.java | 13 ++ .../wf/impl/activiti/dao/WorkItemManager.java | 2 +- .../impl/activiti/dao/WorkItemProvider.java | 3 + .../common/WfTimedActionTriggerHandler.java | 91 ++++++++- .../processes/itemApproval/MidpointUtil.java | 2 +- .../itemApproval/TaskCreateListener.java | 6 + .../wf/impl/tasks/WfTaskController.java | 8 + .../wf/impl/policy/AbstractWfTestPolicy.java | 4 + .../wf/impl/policy/other/TestEscalation.java | 93 ++++++++- .../resources/common/task-trigger-scanner.xml | 38 ++++ .../policy/escalation/metarole-escalated.xml | 23 ++- 19 files changed, 470 insertions(+), 85 deletions(-) create mode 100644 model/workflow-impl/src/test/resources/common/task-trigger-scanner.xml diff --git a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java index 33606810e39..630b18dc74a 100644 --- a/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java +++ b/infra/schema/src/main/java/com/evolveum/midpoint/schema/util/ObjectTypeUtil.java @@ -520,4 +520,13 @@ public static ObjectType toObjectable(PrismObject object) { public static boolean containsOid(Collection values, @NotNull String oid) { return values.stream().anyMatch(v -> oid.equals(v.getOid())); } + + @SuppressWarnings("unchecked") + public static T getExtensionItemRealValue(@Nullable ExtensionType extension, @NotNull QName itemName) { + if (extension == null) { + return null; + } + Item item = extension.asPrismContainerValue().findItem(itemName); + return item != null ? (T) item.getRealValue() : null; + } } \ No newline at end of file diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-workflows-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-workflows-3.xsd index a8d5c0a9eb2..d6c2d44897b 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-workflows-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-workflows-3.xsd @@ -207,6 +207,8 @@ + + @@ -237,7 +239,7 @@ - + Complete this work item with a given result. @@ -246,8 +248,8 @@ - - + + Delegate (escalate) this work item. @@ -257,10 +259,26 @@ - - - + + + + + 0 + 1 + + + + + + + 0 + 1 + + + + + TODO @@ -272,55 +290,115 @@ - - - - Specific approver to replace the current one. - - - - - - - Expression giving an approver to replace the current one. - - - - - - -

- What relation(s) to use when determining approvers? E.g. "approver", "owner", - "securityApprover", and so on. -

-
-
-
- - - - What is the outcome (of this work item) if there is no approver to delegate/escalate to? - - - - - - - Duration of the work item after delegation. If not specified, the deadline of the work item - is not changed (this is meaningful only if the delegation occurred before the original deadline). - - - - - + + +
+
+ + + + + TODO + EXPERIMENTAL + + + 3.6 + + + + + + + + + + + + + + + + TODO + EXPERIMENTAL + + + 3.6 + + + + + + + + + + + + + + + TODO + EXPERIMENTAL + + + 3.6 + + + + + + + + + + Specific approver to replace the current one. + + + + + + + Expression giving an approver to replace the current one. + + + + + + +

+ What relation(s) to use when determining approvers? E.g. "approver", "owner", + "securityApprover", and so on. +

+
+
+
+ + + + What is the outcome (of this work item) if there is no approver to delegate/escalate to? + + + + + + + Duration of the work item after delegation. If not specified, the deadline of the work item + is not changed (this is meaningful only if the delegation occurred before the original deadline). + + + + + - - - + + + - - -
+ + +
+ + diff --git a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java index 0de33bab5b3..ca1309bc354 100644 --- a/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java +++ b/model/model-test/src/main/java/com/evolveum/midpoint/model/test/AbstractModelIntegrationTest.java @@ -2420,19 +2420,30 @@ protected OperationResult waitForTaskNextRunAssertSuccess(Task origTask, final b } protected OperationResult waitForTaskNextRun(final String taskOid, final boolean checkSubresult, final int timeout) throws Exception { + return waitForTaskNextRun(taskOid, checkSubresult, timeout, false); + } + + protected OperationResult waitForTaskNextRun(final String taskOid, final boolean checkSubresult, final int timeout, boolean kickTheTask) throws Exception { final OperationResult waitResult = new OperationResult(AbstractIntegrationTest.class+".waitForTaskNextRun"); Task origTask = taskManager.getTask(taskOid, waitResult); - return waitForTaskNextRun(origTask, checkSubresult, timeout, waitResult); + return waitForTaskNextRun(origTask, checkSubresult, timeout, waitResult, kickTheTask); } protected OperationResult waitForTaskNextRun(final Task origTask, final boolean checkSubresult, final int timeout) throws Exception { + return waitForTaskNextRun(origTask, checkSubresult, timeout, false); + } + + protected OperationResult waitForTaskNextRun(final Task origTask, final boolean checkSubresult, final int timeout, boolean kickTheTask) throws Exception { final OperationResult waitResult = new OperationResult(AbstractIntegrationTest.class+".waitForTaskNextRun"); - return waitForTaskNextRun(origTask, checkSubresult, timeout, waitResult); + return waitForTaskNextRun(origTask, checkSubresult, timeout, waitResult, kickTheTask); } - protected OperationResult waitForTaskNextRun(final Task origTask, final boolean checkSubresult, final int timeout, final OperationResult waitResult) throws Exception { + protected OperationResult waitForTaskNextRun(final Task origTask, final boolean checkSubresult, final int timeout, final OperationResult waitResult, boolean kickTheTask) throws Exception { final Long origLastRunStartTimestamp = origTask.getLastRunStartTimestamp(); final Long origLastRunFinishTimestamp = origTask.getLastRunFinishTimestamp(); + if (kickTheTask) { + taskManager.scheduleTaskNow(origTask, waitResult); + } final Holder taskResultHolder = new Holder<>(); Checker checker = new Checker() { @Override diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java index 15c18eb3aaa..168ffb2806a 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkItemEvent.java @@ -20,10 +20,7 @@ import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; -import com.evolveum.midpoint.xml.ns._public.common.common_3.DecisionType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.EventCategoryType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.apache.commons.lang.Validate; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +36,7 @@ public class WorkItemEvent extends WorkflowEvent { @NotNull private final WorkItemType workItem; private final SimpleObjectRef assignee; + private WorkItemNotificationActionType notificationAction; // temporary implementation public WorkItemEvent(LightweightIdentifierGenerator lightweightIdentifierGenerator, ChangeType changeType, @NotNull WorkItemType workItem, @Nullable SimpleObjectRef assignee, WfContextType workflowContext) { @@ -66,7 +64,16 @@ public SimpleObjectRef getAssignee() { return assignee; } - @Override + public WorkItemNotificationActionType getNotificationAction() { + return notificationAction; + } + + public void setNotificationAction( + WorkItemNotificationActionType notificationAction) { + this.notificationAction = notificationAction; + } + + @Override public void createExpressionVariables(Map variables, OperationResult result) { super.createExpressionVariables(variables, result); variables.put(SchemaConstants.C_ASSIGNEE, assignee != null ? assignee.resolveObjectType(result, false) : null); diff --git a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEventCreator.java b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEventCreator.java index 953ac34e28b..6ed73b24744 100644 --- a/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEventCreator.java +++ b/model/notifications-api/src/main/java/com/evolveum/midpoint/notifications/api/events/WorkflowEventCreator.java @@ -18,6 +18,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; /** @@ -42,4 +43,7 @@ public interface WorkflowEventCreator { WorkItemEvent createWorkItemCreateEvent(WorkItemType workItem, Task wfTask, OperationResult result); WorkItemEvent createWorkItemCompleteEvent(WorkItemType workItem, Task wfTask, OperationResult result); + + WorkItemEvent createWorkItemEventForNotificationAction(WorkItemType workItem, WorkItemNotificationActionType notificationAction, + Task wfTask, OperationResult result); } diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListener.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListener.java index 7a263c9ea87..f4ccd41ad0b 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListener.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/WorkflowListener.java @@ -29,6 +29,7 @@ import com.evolveum.midpoint.wf.api.ProcessListener; import com.evolveum.midpoint.wf.api.WorkItemListener; import com.evolveum.midpoint.wf.api.WorkflowManager; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -94,7 +95,15 @@ public void onWorkItemCompletion(WorkItemType workItem, Task wfTask, OperationRe processEvent(event); } - private void processEvent(WorkflowEvent event, OperationResult result) { + @Override + public void onWorkItemNotificationAction(WorkItemType workItem, WorkItemNotificationActionType notificationAction, + Task wfTask, OperationResult result) { + WorkflowEventCreator workflowEventCreator = notificationManager.getWorkflowEventCreator(wfTask); + WorkItemEvent event = workflowEventCreator.createWorkItemEventForNotificationAction(workItem, notificationAction, wfTask, result); + processEvent(event); + } + + private void processEvent(WorkflowEvent event, OperationResult result) { try { notificationManager.processEvent(event); } catch (RuntimeException e) { diff --git a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/workflow/DefaultWorkflowEventCreator.java b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/workflow/DefaultWorkflowEventCreator.java index 93de5c1b6d1..65c85c3a27d 100644 --- a/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/workflow/DefaultWorkflowEventCreator.java +++ b/model/notifications-impl/src/main/java/com/evolveum/midpoint/notifications/impl/events/workflow/DefaultWorkflowEventCreator.java @@ -28,6 +28,7 @@ import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.xml.ns._public.common.common_3.WfContextType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -82,7 +83,15 @@ public WorkItemEvent createWorkItemCompleteEvent(WorkItemType workItem, Task wfT return createWorkItemEvent(workItem, wfTask, ChangeType.DELETE); } - private WorkItemEvent createWorkItemEvent(WorkItemType workItemType, Task wfTask, ChangeType changeType) { + @Override + public WorkItemEvent createWorkItemEventForNotificationAction(WorkItemType workItem, + WorkItemNotificationActionType notificationAction, Task wfTask, OperationResult result) { + WorkItemEvent event = createWorkItemEvent(workItem, wfTask, ChangeType.MODIFY); + event.setNotificationAction(notificationAction); + return event; + } + + private WorkItemEvent createWorkItemEvent(WorkItemType workItemType, Task wfTask, ChangeType changeType) { // TODO!!! SimpleObjectRefImpl assignee = workItemType.getOriginalAssigneeRef() != null ? new SimpleObjectRefImpl(notificationsUtil, workItemType.getOriginalAssigneeRef()) : null; diff --git a/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/api/WorkItemListener.java b/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/api/WorkItemListener.java index 64dc3131db7..61619fd8da9 100644 --- a/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/api/WorkItemListener.java +++ b/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/api/WorkItemListener.java @@ -18,6 +18,7 @@ import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; /** @@ -38,19 +39,19 @@ public interface WorkItemListener { /** * This method is called by wf module when a work item is created. * - * @param workItemName name of the work item - * @param assigneeOid OID of the user to which the work item is assigned - * @param instanceState externalized process instance state + * @param workItem the work item */ - public void onWorkItemCreation(WorkItemType workItem, Task wfTask, OperationResult result); + void onWorkItemCreation(WorkItemType workItem, Task wfTask, OperationResult result); /** * This method is called by wf module when a work item is completed. * - * @param workItemName name of the work item - * @param assigneeOid OID of the user to which the work item is assigned - * @param instanceState externalized process instance state - * @param decision decision of the user + * @param workItem the work item */ - public void onWorkItemCompletion(WorkItemType workItem, Task wfTask, OperationResult result); + void onWorkItemCompletion(WorkItemType workItem, Task wfTask, OperationResult result); + + /** + * EXPERIMENTAL + */ + void onWorkItemNotificationAction(WorkItemType workItem, WorkItemNotificationActionType notificationAction, Task wfTask, OperationResult result); } diff --git a/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/util/ApprovalUtils.java b/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/util/ApprovalUtils.java index b907494ee31..49cae9e5d14 100644 --- a/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/util/ApprovalUtils.java +++ b/model/workflow-api/src/main/java/com/evolveum/midpoint/wf/util/ApprovalUtils.java @@ -16,6 +16,8 @@ package com.evolveum.midpoint.wf.util; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemOutcomeType; + /** * @author mederly */ @@ -63,4 +65,15 @@ public static String makeNice(String decision) { return decision; } } + + public static String approvalStringValue(WorkItemOutcomeType outcome) { + if (outcome == null) { + return null; + } + switch (outcome) { + case APPROVE: return DECISION_APPROVED; + case REJECT: return DECISION_REJECTED; + default: throw new IllegalArgumentException("outcome: " + outcome); + } + } } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemManager.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemManager.java index aa3da6dd891..c45716f8d28 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemManager.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/activiti/dao/WorkItemManager.java @@ -246,7 +246,7 @@ public void delegateWorkItem(String workItemId, List delega throw new IllegalArgumentException("Couldn't add no-OID reference as a delegate: " + delegate); } if (!ObjectTypeUtil.containsOid(currentAssignees, delegate.getOid())) { - currentAssignees.add(delegate); + currentAssignees.add(delegate.clone()); } } // TODO 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 6ae2655c23b..af332414676 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 @@ -415,6 +415,9 @@ public String toString() { private WorkItemType taskToWorkItem(Task task, Map processVariables, boolean resolveTask, boolean resolveAssignee, boolean resolveCandidates, boolean fetchAllVariables, OperationResult result) { + if (task == null) { + return null; + } TaskExtract taskExtract = new TaskExtract(task, processVariables); return taskExtractToWorkItem(taskExtract, resolveTask, resolveAssignee, resolveCandidates, fetchAllVariables, result); } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/WfTimedActionTriggerHandler.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/WfTimedActionTriggerHandler.java index cd755ffb382..1bc5515f6a9 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/WfTimedActionTriggerHandler.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/common/WfTimedActionTriggerHandler.java @@ -19,36 +19,119 @@ import com.evolveum.midpoint.model.impl.trigger.TriggerHandler; import com.evolveum.midpoint.model.impl.trigger.TriggerHandlerRegistry; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.schema.SchemaConstantsGenerated; import com.evolveum.midpoint.schema.result.OperationResult; +import com.evolveum.midpoint.schema.util.ObjectTypeUtil; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.task.api.TaskManager; +import com.evolveum.midpoint.util.exception.ObjectNotFoundException; +import com.evolveum.midpoint.util.exception.SchemaException; +import com.evolveum.midpoint.util.exception.SecurityViolationException; +import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.api.WorkflowConstants; -import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; -import com.evolveum.midpoint.xml.ns._public.common.common_3.TriggerType; +import com.evolveum.midpoint.wf.impl.activiti.dao.WorkItemManager; +import com.evolveum.midpoint.wf.impl.activiti.dao.WorkItemProvider; +import com.evolveum.midpoint.wf.impl.tasks.WfTaskController; +import com.evolveum.midpoint.wf.util.ApprovalUtils; +import com.evolveum.midpoint.xml.ns._public.common.common_3.*; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** * @author mederly */ +@Component public class WfTimedActionTriggerHandler implements TriggerHandler { - public static final String HANDLER_URI = WorkflowConstants.NS_WORKFLOW_TRIGGER_PREFIX + "/timedAction/handler-3"; + public static final String HANDLER_URI = WorkflowConstants.NS_WORKFLOW_TRIGGER_PREFIX + "/timed-action/handler-3"; private static final transient Trace LOGGER = TraceManager.getTrace(WfTimedActionTriggerHandler.class); @Autowired private TriggerHandlerRegistry triggerHandlerRegistry; + @Autowired + private WorkItemProvider workItemProvider; + + @Autowired + private WorkItemManager workItemManager; + + @Autowired + private WfTaskController wfTaskController; + + @Autowired + private TaskManager taskManager; + @PostConstruct private void initialize() { triggerHandlerRegistry.register(HANDLER_URI, this); } @Override - public void handle(PrismObject object, TriggerType trigger, Task task, OperationResult result) { + public void handle(PrismObject object, TriggerType trigger, Task triggerScannerTask, OperationResult parentResult) { + if (!(object.asObjectable() instanceof TaskType)) { + throw new IllegalArgumentException("Unexpected object type: should be TaskType: " + object); + } + TaskType wfTaskType = (TaskType) object.asObjectable(); + WfContextType wfc = wfTaskType.getWorkflowContext(); + if (wfc == null) { + LOGGER.warn("Task without workflow context; ignoring it: " + object); + return; + } + String workItemId = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstantsGenerated.C_WORK_ITEM_ID); + WorkItemActionsType actions = ObjectTypeUtil.getExtensionItemRealValue(trigger.getExtension(), SchemaConstantsGenerated.C_WORK_ITEM_ACTIONS); + if (workItemId == null || actions == null) { + LOGGER.warn("Trigger without workItemId and workItemActions; ignoring it: " + trigger); + return; + } + OperationResult result = parentResult.createSubresult(WfTimedActionTriggerHandler.class.getName() + ".handle"); + try { + WorkItemType workItem = workItemProvider.getWorkItem(workItemId, result); + if (workItem == null) { + throw new ObjectNotFoundException("No work item with ID " + workItemId); + } + Task wfTask = taskManager.createTaskInstance(wfTaskType.asPrismObject(), result); + if (actions.getNotify() != null) { + executeNotificationAction(workItem, actions.getNotify(), wfTask, result); + } + if (actions.getDelegate() != null) { + executeDelegateAction(workItem, actions.getDelegate(), wfTask, result); + } + if (actions.getComplete() != null) { + executeCompleteAction(workItem, actions.getComplete(), wfTask, result); + } + } catch (RuntimeException|ObjectNotFoundException|SchemaException|SecurityViolationException e) { + String message = "Exception while handling work item trigger for ID " + workItemId + ": " + e.getMessage(); + result.recordFatalError(message, e); + throw new SystemException(message, e); + } finally { + result.computeStatusIfUnknown(); + } + } + + private void executeCompleteAction(WorkItemType workItem, CompleteWorkItemActionType completeAction, Task wfTask, + OperationResult result) throws SchemaException, SecurityViolationException { + WorkItemOutcomeType outcome = completeAction.getOutcome() != null ? completeAction.getOutcome() : WorkItemOutcomeType.REJECT; + // TODO distinguish from regular user complete action + workItemManager.completeWorkItem(workItem.getWorkItemId(), ApprovalUtils.approvalStringValue(outcome), + null, null, result); + } + + private void executeDelegateAction(WorkItemType workItem, DelegateWorkItemActionType delegateAction, Task wfTask, + OperationResult result) throws SecurityViolationException, ObjectNotFoundException { + workItemManager.delegateWorkItem(workItem.getWorkItemId(), delegateAction.getApproverRef(), + WorkItemDelegationMethodType.ADD_ASSIGNEES, result); + } + private void executeNotificationAction(WorkItemType workItem, WorkItemNotificationActionType notificationAction, Task wfTask, + OperationResult result) throws SchemaException { + wfTaskController.executeWorkItemNotificationAction(workItem, notificationAction, wfTaskController.recreateWfTask(wfTask), result); } + + + } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/MidpointUtil.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/MidpointUtil.java index b49d627c0ed..862af6a115d 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/MidpointUtil.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/MidpointUtil.java @@ -177,7 +177,7 @@ public static void createTriggersForTimedActions(DelegateTask delegateTask, Task workItemIdProp.addRealValue(delegateTask.getId()); extension.asPrismContainerValue().add(workItemIdProp); PrismContainer workItemActionsCont = workItemActionsDef.instantiate(); - workItemActionsCont.add(timedAction.asPrismContainerValue().clone()); + workItemActionsCont.add(timedAction.getActions().asPrismContainerValue().clone()); extension.asPrismContainerValue().add(workItemActionsCont); triggers.add(trigger); } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/TaskCreateListener.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/TaskCreateListener.java index be2619f7ef6..362a8971e0f 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/TaskCreateListener.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/processes/itemApproval/TaskCreateListener.java @@ -35,6 +35,8 @@ public class TaskCreateListener implements TaskListener { public void notify(DelegateTask delegateTask) { OperationResult result = new OperationResult(TaskCreateListener.class.getName() + ".notify"); Task wfTask = ActivitiUtil.getTask(delegateTask.getExecution(), result); + + // duration/deadline ApprovalLevelType level = WfContextUtil.getCurrentApprovalLevel(wfTask.getWorkflowContext()); if (level == null) { throw new IllegalStateException("No approval level information in " + delegateTask); @@ -42,12 +44,16 @@ public void notify(DelegateTask delegateTask) { if (level.getDuration() != null) { MidpointUtil.setTaskDeadline(delegateTask, level.getDuration(), result); } + + // triggers MidpointUtil.createTriggersForTimedActions(delegateTask, wfTask, level.getTimedActions(), result); + // originalAssignee String assignee = delegateTask.getAssignee(); if (assignee != null) { delegateTask.setVariableLocal(CommonProcessVariableNames.VARIABLE_ORIGINAL_ASSIGNEE, assignee); } + new MidPointTaskListener().notify(delegateTask); } } diff --git a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java index 1c395ab3a8e..990ef8da2ad 100644 --- a/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java +++ b/model/workflow-impl/src/main/java/com/evolveum/midpoint/wf/impl/tasks/WfTaskController.java @@ -46,6 +46,7 @@ import com.evolveum.midpoint.wf.impl.util.MiscDataUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.TaskType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WfConfigurationType; +import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemNotificationActionType; import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemType; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; @@ -377,6 +378,13 @@ private void notifyWorkItemCreated(WorkItemType workItem, WfTask wfTask, Operati } } + public void executeWorkItemNotificationAction(WorkItemType workItem, WorkItemNotificationActionType notificationAction, + WfTask wfTask, OperationResult result) throws SchemaException { + for (WorkItemListener workItemListener : workItemListeners) { + workItemListener.onWorkItemNotificationAction(workItem, notificationAction, wfTask.getTask(), result); + } + } + private void notifyWorkItemCompleted(WorkItemType workItem, WfTask wfTask, OperationResult result) throws SchemaException { for (WorkItemListener workItemListener : workItemListeners) { workItemListener.onWorkItemCompletion(workItem, wfTask.getTask(), result); diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/AbstractWfTestPolicy.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/AbstractWfTestPolicy.java index ca9b9ed49f8..8039bbdf789 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/AbstractWfTestPolicy.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/AbstractWfTestPolicy.java @@ -477,6 +477,10 @@ protected PrismReference ref(List orts) { return rv; } + protected PrismReference ref(ObjectReferenceType ort) { + return ref(Collections.singletonList(ort)); + } + protected abstract class TestDetails { protected LensContext createModelContext(OperationResult result) throws Exception { return null; diff --git a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestEscalation.java b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestEscalation.java index 21967020b1c..3dc9af4a02a 100644 --- a/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestEscalation.java +++ b/model/workflow-impl/src/test/java/com/evolveum/midpoint/wf/impl/policy/other/TestEscalation.java @@ -18,6 +18,7 @@ import com.evolveum.midpoint.model.api.WorkflowService; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.util.PrismAsserts; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.test.IntegrationTestTools; @@ -34,6 +35,8 @@ import java.io.File; +import static org.testng.AssertJUnit.assertEquals; + /** * @author mederly */ @@ -52,14 +55,18 @@ protected PrismObject getDefaultActor() { @Autowired private WorkflowService workflowService; + protected static final File TASK_TRIGGER_SCANNER_FILE = new File(COMMON_DIR, "task-trigger-scanner.xml"); + protected static final String TASK_TRIGGER_SCANNER_OID = "00000000-0000-0000-0000-000000000007"; + protected static final File TEST_ESCALATION_RESOURCE_DIR = new File("src/test/resources/policy/escalation"); protected static final File METAROLE_ESCALATED_FILE = new File(TEST_ESCALATION_RESOURCE_DIR, "metarole-escalated.xml"); protected static final File ROLE_E1_FILE = new File(TEST_ESCALATION_RESOURCE_DIR, "role-e1.xml"); protected String metaroleEscalatedOid; protected String roleE1Oid; - private PrismObject userLead1, userLead3; + private PrismObject userLead1, userLead2; private String workItemId; + private String approvalTaskOid; @Override public void initSystem(Task initTask, OperationResult initResult) throws Exception { @@ -67,8 +74,10 @@ public void initSystem(Task initTask, OperationResult initResult) throws Excepti metaroleEscalatedOid = repoAddObjectFromFile(METAROLE_ESCALATED_FILE, initResult).getOid(); roleE1Oid = repoAddObjectFromFile(ROLE_E1_FILE, initResult).getOid(); + importObjectFromFile(TASK_TRIGGER_SCANNER_FILE); + userLead1 = getUser(userLead1Oid); - userLead3 = getUser(userLead3Oid); + userLead2 = getUser(userLead2Oid); } @Test @@ -86,13 +95,87 @@ public void test100CreateTask() throws Exception { WorkItemType workItem = getWorkItem(task, result); workItemId = workItem.getWorkItemId(); - String wfTaskOid = workItem.getTaskRef().getOid(); - PrismObject wfTask = getTask(wfTaskOid); + approvalTaskOid = workItem.getTaskRef().getOid(); + PrismObject wfTask = getTask(approvalTaskOid); IntegrationTestTools.display("work item", workItem); IntegrationTestTools.display("workflow task", wfTask); - //PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid); + assertEquals("Wrong # of triggers", 3, wfTask.asObjectable().getTrigger().size()); + + PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid); + PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid); + } + + @Test + public void test110Notify() throws Exception { + final String TEST_NAME = "test110Notify"; + TestUtil.displayTestTile(this, TEST_NAME); + login(userAdministrator); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + clock.overrideDuration("P6D"); // at P5D there's a notify action + waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); + + // TODO assert notifications + + WorkItemType workItem = getWorkItem(task, result); + String wfTaskOid = workItem.getTaskRef().getOid(); + PrismObject wfTask = getTask(wfTaskOid); + assertEquals("Wrong # of triggers", 2, wfTask.asObjectable().getTrigger().size()); + + PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid); + PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid); + } + + @Test + public void test120Delegate() throws Exception { + final String TEST_NAME = "test120Delegate"; + TestUtil.displayTestTile(this, TEST_NAME); + login(userAdministrator); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + clock.resetOverride(); + clock.overrideDuration("P13D"); // at -P2D (i.e. P12D) there is a delegate action + waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); + + WorkItemType workItem = getWorkItem(task, result); + + PrismObject wfTask = getTask(workItem.getTaskRef().getOid()); + assertEquals("Wrong # of triggers", 1, wfTask.asObjectable().getTrigger().size()); + + PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid, userLead2Oid); + PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid); + + } + + @Test + public void test130Complete() throws Exception { + final String TEST_NAME = "test130Complete"; + TestUtil.displayTestTile(this, TEST_NAME); + login(userAdministrator); + + Task task = createTask(TEST_NAME); + OperationResult result = task.getResult(); + + clock.resetOverride(); + clock.overrideDuration("P15D"); // at 0 (i.e. P14D) there is a delegate action + waitForTaskNextRun(TASK_TRIGGER_SCANNER_OID, true, 20000, true); + +// WorkItemType workItem = getWorkItem(task, result); + + PrismObject wfTask = getTask(approvalTaskOid); + assertEquals("Wrong # of triggers", 0, wfTask.asObjectable().getTrigger().size()); + + assertAssignedRole(userJackOid, roleE1Oid, task, result); + +// PrismAsserts.assertReferenceValues(ref(workItem.getAssigneeRef()), userLead1Oid, userLead2Oid); +// PrismAsserts.assertReferenceValue(ref(workItem.getOriginalAssigneeRef()), userLead1Oid); + } } diff --git a/model/workflow-impl/src/test/resources/common/task-trigger-scanner.xml b/model/workflow-impl/src/test/resources/common/task-trigger-scanner.xml new file mode 100644 index 00000000000..dcf044cf1d5 --- /dev/null +++ b/model/workflow-impl/src/test/resources/common/task-trigger-scanner.xml @@ -0,0 +1,38 @@ + + + + + + Trigger Scanner + + 00000000-0000-0000-0000-000000000007 + + runnable + + http://midpoint.evolveum.com/xml/ns/public/model/trigger/scanner/handler-3 + recurring + tight + + + + + diff --git a/model/workflow-impl/src/test/resources/policy/escalation/metarole-escalated.xml b/model/workflow-impl/src/test/resources/policy/escalation/metarole-escalated.xml index 8d6e16c6aa1..d478c9f4de5 100644 --- a/model/workflow-impl/src/test/resources/policy/escalation/metarole-escalated.xml +++ b/model/workflow-impl/src/test/resources/policy/escalation/metarole-escalated.xml @@ -30,10 +30,29 @@ - PT10M + P14D + First, send a notification + - approve + + + + + 2 days before deadline, delegate + + + + + + + + + Finally - approve + + + approve +