Skip to content

Commit

Permalink
Add "cancel case" also for operation request cases
Browse files Browse the repository at this point in the history
The implementation is preliminary but working.
  • Loading branch information
mederly committed Jun 7, 2019
1 parent 5a14ee1 commit f128bc2
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 61 deletions.
Expand Up @@ -363,7 +363,7 @@ private void stopCaseProcessConfirmed(AjaxRequestTarget target, List<CaseType> c
Task task = createSimpleTask(OPERATION_STOP_CASE_PROCESS);
OperationResult result = new OperationResult(OPERATION_STOP_CASE_PROCESS);
try {
getWorkflowService().stopProcessInstance(caseObject.getOid(), task, result);
getWorkflowService().cancelCase(caseObject.getOid(), task, result);
} catch (Exception ex){
LOGGER.error("Couldn't stop case process, ", ex.getLocalizedMessage());
result.recordFatalError("Couldn't stop case process, ", ex);
Expand Down
Expand Up @@ -226,7 +226,7 @@ void stopApprovalProcessPerformed(AjaxRequestTarget target) {
Task task = parentPage.createSimpleTask(PageProcessInstances.OPERATION_STOP_PROCESS_INSTANCE);
OperationResult result = task.getResult();
try {
parentPage.getWorkflowService().stopProcessInstance(instanceId, task, result);
parentPage.getWorkflowService().cancelCase(instanceId, task, result);
result.computeStatusIfUnknown();
} catch (SchemaException | ObjectNotFoundException | SecurityViolationException | ExpressionEvaluationException | RuntimeException | CommunicationException | ConfigurationException | ObjectAlreadyExistsException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't stop approval process instance {}", e, instanceId);
Expand Down
Expand Up @@ -65,7 +65,7 @@ public abstract class PageProcessInstances extends PageAdminWorkItems {
private static final Trace LOGGER = TraceManager.getTrace(PageProcessInstances.class);
private static final String DOT_CLASS = PageProcessInstances.class.getName() + ".";
private static final String OPERATION_STOP_PROCESS_INSTANCES = DOT_CLASS + "stopProcessInstances";
public static final String OPERATION_STOP_PROCESS_INSTANCE = DOT_CLASS + "stopProcessInstance";
public static final String OPERATION_STOP_PROCESS_INSTANCE = DOT_CLASS + "cancelCase";
private static final String OPERATION_DELETE_PROCESS_INSTANCES = DOT_CLASS + "deleteProcessInstances";
private static final String OPERATION_DELETE_PROCESS_INSTANCE = DOT_CLASS + "deleteProcessInstance";

Expand Down Expand Up @@ -171,7 +171,7 @@ private void stopProcessInstancesPerformed(AjaxRequestTarget target) {
WorkflowService workflowService = getWorkflowService();
for (ProcessInstanceDto instance : selectedStoppableInstances) {
try {
workflowService.stopProcessInstance(instance.getProcessInstanceId(), task, result);
workflowService.cancelCase(instance.getProcessInstanceId(), task, result);
} catch (SchemaException | ObjectNotFoundException | SecurityViolationException | ExpressionEvaluationException | RuntimeException | CommunicationException | ConfigurationException | ObjectAlreadyExistsException ex) {
result.createSubresult(OPERATION_STOP_PROCESS_INSTANCE)
.recordPartialError(createStringResource("pageProcessInstances.message.stopProcessInstancesPerformed.partialError", instance.getName()).getString(), ex);
Expand Down
Expand Up @@ -64,6 +64,7 @@ public T getUserObject() {
return userObject;
}

@SuppressWarnings("unused")
public void setUserObject(T userObject) {
this.userObject = userObject;
}
Expand All @@ -79,10 +80,10 @@ public String debugDump(int indent) {
return sb.toString();
}

public <N> TreeNode<N> tranform(Function<T, N> transformation) {
public <N> TreeNode<N> transform(Function<T, N> transformation) {
TreeNode<N> rv = new TreeNode<>(transformation.apply(userObject));
for (TreeNode<T> child : children) {
rv.add(child.tranform(transformation));
rv.add(child.transform(transformation));
}
return rv;
}
Expand Down
Expand Up @@ -79,6 +79,7 @@ public enum ModelAuthorizationAction implements DisplayableValue<String> {
READ_ALL_WORK_ITEMS("readAllWorkItems", "Read all work items", "READ_ALL_WORK_ITEMS_HELP"), // currently not implemented seriously
STOP_APPROVAL_PROCESS_INSTANCE("stopApprovalProcessInstance", "Stop approval process instance", "STOP_APPROVAL_PROCESS_INSTANCE_HELP"),
CLEANUP_PROCESS_INSTANCES("cleanupProcessInstances", "Cleanup process instances", "CLEANUP_PROCESS_INSTANCES_HELP"),
CANCEL_CASE("cancelCase", "Cancel case", "CANCEL_CASE_HELP"),

AUDIT_READ("auditRead", "Audit Read", "AUDIT_READ_HELP"),
// Authorization to create a user-level (custom) audit record. Does not apply to internal records that are created automatically by the model without
Expand Down
Expand Up @@ -54,8 +54,8 @@ void delegateWorkItem(WorkItemId workItemId, List<ObjectReferenceType> delegates

//endregion

//region Process instances
void stopProcessInstance(String caseOid, Task task, OperationResult parentResult)
//region Cases
void cancelCase(String caseOid, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, SecurityViolationException, ExpressionEvaluationException,
CommunicationException, ConfigurationException, ObjectAlreadyExistsException;
//endregion
Expand Down
Expand Up @@ -97,7 +97,7 @@ public static List<TreeNode<LocalizableMessage>> extractMessages(List<TreeNode<E
MessageKind kind) {
List<TreeNode<LocalizableMessage>> messageTreeList = new ArrayList<>();
for (TreeNode<EvaluatedPolicyRuleTrigger<?>> tree : triggerTreeList) {
messageTreeList.add(tree.tranform(trigger -> getMessage(trigger, kind)));
messageTreeList.add(tree.transform(trigger -> getMessage(trigger, kind)));
}
return messageTreeList;
}
Expand Down Expand Up @@ -147,7 +147,7 @@ public static List<TreeNode<EvaluatedPolicyRuleTriggerType>> arrangeForPresentat
List<TreeNode<AugmentedTrigger<AdditionalData>>> trees = arrangeForPresentationExt(augmentedTriggers, null);
// de-augment
return trees.stream()
.map(tree -> tree.tranform(at -> at.trigger))
.map(tree -> tree.transform(at -> at.trigger))
.collect(Collectors.toList());
}

Expand Down
Expand Up @@ -2031,10 +2031,10 @@ public void completeWorkItem(WorkItemId workItemId, boolean decision, String com
}

@Override
public void stopProcessInstance(String caseOid, Task task, OperationResult parentResult) throws SchemaException,
public void cancelCase(String caseOid, Task task, OperationResult parentResult) throws SchemaException,
ObjectNotFoundException, SecurityViolationException, ExpressionEvaluationException, CommunicationException,
ConfigurationException, ObjectAlreadyExistsException {
getWorkflowManagerChecked().stopProcessInstance(caseOid, task, parentResult);
getWorkflowManagerChecked().cancelCase(caseOid, task, parentResult);
}

@Override
Expand Down
Expand Up @@ -61,9 +61,9 @@ void delegateWorkItem(WorkItemId workItemId, List<ObjectReferenceType> delegates
ExpressionEvaluationException, CommunicationException, ConfigurationException;
//endregion

//region Process instances
//region Process instances (cases)

void stopProcessInstance(String caseOid, Task task, OperationResult parentResult)
void cancelCase(String caseOid, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, SecurityViolationException,
CommunicationException, ConfigurationException, ExpressionEvaluationException;

Expand Down
Expand Up @@ -115,7 +115,7 @@ public void delegateWorkItem(WorkItemId workItemId, List<ObjectReferenceType> de

//region Process instances (cases)
@Override
public void stopProcessInstance(String caseOid, Task task, OperationResult parentResult)
public void cancelCase(String caseOid, Task task, OperationResult parentResult)
throws SchemaException, ObjectNotFoundException, ObjectAlreadyExistsException, SecurityViolationException,
CommunicationException, ConfigurationException, ExpressionEvaluationException {
caseManager.cancelCase(caseOid, task, parentResult);
Expand Down
Expand Up @@ -16,45 +16,69 @@

package com.evolveum.midpoint.wf.impl.access;

import com.evolveum.midpoint.common.Clock;
import com.evolveum.midpoint.model.api.ModelAuthorizationAction;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.enforcer.api.AuthorizationParameters;
import com.evolveum.midpoint.security.enforcer.api.SecurityEnforcer;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.task.api.TaskManager;
import com.evolveum.midpoint.util.TreeNode;
import com.evolveum.midpoint.util.exception.*;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.api.WorkflowManager;
import com.evolveum.midpoint.wf.api.request.CancelCaseRequest;
import com.evolveum.midpoint.wf.impl.engine.WorkflowEngine;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemObjectsType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.xml.datatype.XMLGregorianCalendar;
import java.util.ArrayList;
import java.util.List;

/**
*
*/

@Component("wfCaseManager")
public class CaseManager {

@SuppressWarnings("unused")
private static final transient Trace LOGGER = TraceManager.getTrace(CaseManager.class);

@Autowired private TaskManager taskManager;
@Autowired private PrismContext prismContext;
@Autowired private WorkflowEngine workflowEngine;
@Autowired private SecurityEnforcer securityEnforcer;
@Autowired private Clock clock;
@Autowired
@Qualifier("cacheRepositoryService")
private RepositoryService repositoryService;

private static final String DOT_INTERFACE = WorkflowManager.class.getName() + ".";

private static final String OPERATION_STOP_PROCESS_INSTANCE = DOT_INTERFACE + "stopProcessInstance";
private static final String OPERATION_DELETE_PROCESS_INSTANCE = DOT_INTERFACE + "deleteProcessInstance";
private static final String OPERATION_STOP_PROCESS_INSTANCE = DOT_INTERFACE + "cancelCase";
//private static final String OPERATION_DELETE_PROCESS_INSTANCE = DOT_INTERFACE + "deleteProcessInstance";

public void cancelCase(String caseOid, Task task, OperationResult parentResult)
throws ObjectAlreadyExistsException, ObjectNotFoundException, SchemaException, ConfigurationException,
CommunicationException, SecurityViolationException, ExpressionEvaluationException {
OperationResult result = parentResult.createSubresult(OPERATION_STOP_PROCESS_INSTANCE);
result.addParam("caseOid", caseOid);
try {
CancelCaseRequest request = new CancelCaseRequest(caseOid);
workflowEngine.executeRequest(request, task, result);
TreeNode<CaseType> caseTree = getCaseTree(caseOid, result);
System.out.println(caseTree.debugDump());
cancelCaseTree(caseTree, task, result);
} catch (RuntimeException | SchemaException | ObjectAlreadyExistsException | ObjectNotFoundException |
SecurityViolationException | ExpressionEvaluationException | ConfigurationException | CommunicationException e) {
result.recordFatalError("Case couldn't be stopped: " + e.getMessage(), e);
Expand All @@ -64,7 +88,68 @@ public void cancelCase(String caseOid, Task task, OperationResult parentResult)
}
}

// TODO cleanup and delete cases
private void cancelCaseTree(TreeNode<CaseType> caseTree, Task task, OperationResult result)
throws CommunicationException, ObjectNotFoundException, ConfigurationException, SchemaException,
SecurityViolationException, ObjectAlreadyExistsException, ExpressionEvaluationException {
CaseType rootCase = caseTree.getUserObject();
securityEnforcer.authorize(ModelAuthorizationAction.CANCEL_CASE.getUrl(), null,
AuthorizationParameters.Builder.buildObject(rootCase.asPrismObject()), null, task, result);
if (!SchemaConstants.CASE_STATE_CLOSED.equals(rootCase.getState())) {
if (isApprovalCase(rootCase)) {
CancelCaseRequest request = new CancelCaseRequest(rootCase.getOid());
workflowEngine.executeRequest(request, task, result);
} else {
cancelNonApprovalCase(rootCase, result);
for (TreeNode<CaseType> child : caseTree.getChildren()) {
cancelCaseTree(child, task, result);
}
}
}
}

private void cancelNonApprovalCase(CaseType aCase, OperationResult result)
throws SchemaException, ObjectAlreadyExistsException, ObjectNotFoundException {
List<ItemDelta<?, ?>> modifications = new ArrayList<>();
XMLGregorianCalendar now = clock.currentTimeXMLGregorianCalendar();
for (CaseWorkItemType workItem : aCase.getWorkItem()) {
if (workItem.getCloseTimestamp() == null) {
modifications.add(prismContext.deltaFor(CaseType.class)
.item(CaseType.F_WORK_ITEM, workItem.getId(), CaseWorkItemType.F_CLOSE_TIMESTAMP).replace(now).asItemDelta());
}
}
modifications.addAll(prismContext.deltaFor(CaseType.class)
.item(CaseType.F_STATE).replace(SchemaConstants.CASE_STATE_CLOSED)
.item(CaseType.F_CLOSE_TIMESTAMP).replace(now)
.asItemDeltas());
repositoryService.modifyObject(CaseType.class, aCase.getOid(), modifications, result);
}

private boolean isApprovalCase(CaseType aCase) {
return aCase.getArchetypeRef().stream().anyMatch(ref -> SystemObjectsType.ARCHETYPE_APPROVAL_CASE.value().equals(ref.getOid()));
}

private TreeNode<CaseType> getCaseTree(String caseOid, OperationResult result)
throws SchemaException, ObjectNotFoundException {
PrismObject<CaseType> root = repositoryService.getObject(CaseType.class, caseOid, null, result);
TreeNode<CaseType> tree = new TreeNode<>(root.asObjectable());
addChildren(tree, result);
return tree;
}

private void addChildren(TreeNode<CaseType> tree, OperationResult result) throws SchemaException {
ObjectQuery query = prismContext.queryFor(CaseType.class)
.item(CaseType.F_PARENT_REF).ref(tree.getUserObject().getOid())
.build();
SearchResultList<PrismObject<CaseType>> children = repositoryService
.searchObjects(CaseType.class, query, null, result);
for (PrismObject<CaseType> child : children) {
TreeNode<CaseType> childNode = new TreeNode<>(child.asObjectable());
tree.add(childNode);
addChildren(childNode, result);
}
}

// TODO cleanup and delete cases

// private void deleteCase(String caseOid, OperationResult parentResult) {
// OperationResult result = parentResult.createSubresult(OPERATION_DELETE_PROCESS_INSTANCE);
Expand Down
Expand Up @@ -42,7 +42,7 @@ public Action execute(OperationResult result)
ConfigurationException, ExpressionEvaluationException {
traceEnter(LOGGER);

engine.securityEnforcer.authorize(ModelAuthorizationAction.STOP_APPROVAL_PROCESS_INSTANCE.getUrl(), null,
engine.securityEnforcer.authorize(ModelAuthorizationAction.CANCEL_CASE.getUrl(), null,
AuthorizationParameters.Builder.buildObject(ctx.getCurrentCase().asPrismObject()), null, ctx.getTask(), result);

// TODO consider putting some events and notifications here
Expand Down

0 comments on commit f128bc2

Please sign in to comment.