Skip to content

Commit

Permalink
Fixed bug of skipping process with no approvers (even with outcomeIfN…
Browse files Browse the repository at this point in the history
…oApprovers = reject).
  • Loading branch information
mederly committed Jan 30, 2017
1 parent eb47078 commit 5e74101
Show file tree
Hide file tree
Showing 10 changed files with 74 additions and 56 deletions.
Expand Up @@ -200,6 +200,20 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="skip">
<xsd:annotation>
<xsd:documentation>
This level is silently skipped. This is useful for situations where we don't even want to start
an approval process if there are no approvers in it.

Skipping whole approval process is currently supported only partly: when using approver relations.
For approver expressions, these are always evaluated within context of a workflow process.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="SKIP"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

Expand Down
Expand Up @@ -57,7 +57,7 @@ public interface ApprovalLevel extends DebugDumpable {

ApprovalLevelType toApprovalLevelType(PrismContext prismContext);

boolean isEmpty();
boolean shouldBeSkipped();

String getDebugName();
}
Expand Up @@ -269,6 +269,7 @@ public String toString() {
", automaticallyApproved=" + automaticallyApproved +
", approverRefs=" + approverRefs +
", approverExpressions=" + approverExpressions +
", outcomeIfNoApprovers=" + outcomeIfNoApprovers +
'}';
}

Expand All @@ -293,7 +294,7 @@ public String getDebugName() {
return order + "/" + name;
}

public boolean isEmpty() {
return approverRefs.isEmpty() && approverExpressions.isEmpty();
public boolean shouldBeSkipped() {
return outcomeIfNoApprovers == ApprovalLevelOutcomeType.SKIP && approverRefs.isEmpty() && approverExpressions.isEmpty();
}
}
Expand Up @@ -42,8 +42,33 @@ public class ApprovalRequestImpl<I extends Serializable> implements ApprovalRequ
private transient PrismContext prismContext;

private ApprovalSchema approvalSchema;
private ApprovalSchemaType approvalSchemaType;

private ApprovalRequestImpl(I itemToApprove, @NotNull PrismContext prismContext) {
public ApprovalRequestImpl(I itemToApprove, PcpAspectConfigurationType config, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApprove, prismContext);
setSchemaFromConfig(config, prismContext, relationResolver, referenceResolver);
}

public ApprovalRequestImpl(SerializationSafeContainer itemToApproveWrapped, PcpAspectConfigurationType config,
ApprovalSchemaType approvalSchema, List<ObjectReferenceType> approverRef,
List<ExpressionType> approverExpression, ExpressionType automaticallyApproved,
@NotNull PrismContext prismContext, RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApproveWrapped, prismContext);
setSchemaFromConfigAndParameters(config, approvalSchema, approverRef, approverExpression, automaticallyApproved,
prismContext, relationResolver, referenceResolver);
}

public ApprovalRequestImpl(I itemToApprove, PcpAspectConfigurationType config, ApprovalSchemaType approvalSchema,
List<ObjectReferenceType> approverRef, List<ExpressionType> approverExpression,
ExpressionType automaticallyApproved, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApprove, prismContext);
setSchemaFromConfigAndParameters(config, approvalSchema, approverRef, approverExpression, automaticallyApproved,
prismContext, relationResolver, referenceResolver);
}

private ApprovalRequestImpl(I itemToApprove, @NotNull PrismContext prismContext) {
setPrismContext(prismContext);
setItemToApprove(itemToApprove);
}
Expand All @@ -53,17 +78,6 @@ private ApprovalRequestImpl(SerializationSafeContainer wrappedValue, @NotNull Pr
setItemToApprove(wrappedValue);
}

// public ApprovalRequestImpl(SerializationSafeContainer itemToApproveWrapped, PcpAspectConfigurationType config, PrismContext prismContext) {
// this(itemToApproveWrapped, prismContext);
// setSchemaFromConfig(config, prismContext);
// }

public ApprovalRequestImpl(I itemToApprove, PcpAspectConfigurationType config, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApprove, prismContext);
setSchemaFromConfig(config, prismContext, relationResolver, referenceResolver);
}

private void setSchemaFromConfig(PcpAspectConfigurationType config, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
if (config != null) {
Expand All @@ -73,24 +87,6 @@ private void setSchemaFromConfig(PcpAspectConfigurationType config, @NotNull Pri
}
}

public ApprovalRequestImpl(SerializationSafeContainer itemToApproveWrapped, PcpAspectConfigurationType config,
ApprovalSchemaType approvalSchema, List<ObjectReferenceType> approverRef,
List<ExpressionType> approverExpression, ExpressionType automaticallyApproved,
@NotNull PrismContext prismContext, RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApproveWrapped, prismContext);
setSchemaFromConfigAndParameters(config, approvalSchema, approverRef, approverExpression, automaticallyApproved,
prismContext, relationResolver, referenceResolver);
}

public ApprovalRequestImpl(I itemToApprove, PcpAspectConfigurationType config, ApprovalSchemaType approvalSchema,
List<ObjectReferenceType> approverRef, List<ExpressionType> approverExpression,
ExpressionType automaticallyApproved, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
this(itemToApprove, prismContext);
setSchemaFromConfigAndParameters(config, approvalSchema, approverRef, approverExpression, automaticallyApproved,
prismContext, relationResolver, referenceResolver);
}

private void setSchemaFromConfigAndParameters(PcpAspectConfigurationType config, ApprovalSchemaType approvalSchema,
List<ObjectReferenceType> approverRef, List<ExpressionType> approverExpression, ExpressionType automaticallyApproved,
@NotNull PrismContext prismContext, RelationResolver relationResolver, ReferenceResolver referenceResolver) {
Expand Down
Expand Up @@ -41,5 +41,5 @@ public interface ApprovalSchema extends DebugDumpable {

ApprovalSchemaType toApprovalSchemaType();

boolean isEmpty();
boolean shouldBeSkipped();
}
Expand Up @@ -17,7 +17,6 @@
package com.evolveum.midpoint.wf.impl.processes.itemApproval;

import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.schema.util.SchemaDebugUtil;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -45,12 +44,6 @@ public ApprovalSchemaImpl(@NotNull PrismContext prismContext) {
setPrismContext(prismContext);
}

ApprovalSchemaImpl(ApprovalSchemaType approvalSchemaType, @NotNull PrismContext prismContext,
RelationResolver relationResolver, ReferenceResolver referenceResolver) {
setPrismContext(prismContext);
initFromApprovalSchemaType(approvalSchemaType, relationResolver, referenceResolver);
}

ApprovalSchemaImpl(ApprovalSchemaType approvalSchema, List<ObjectReferenceType> approverRefList,
List<ExpressionType> approverExpressionList, ExpressionType automaticallyApproved,
@NotNull PrismContext prismContext, RelationResolver relationResolver, ReferenceResolver referenceResolver) {
Expand Down Expand Up @@ -155,7 +148,7 @@ public String debugDump(int indent) {
}

@Override
public boolean isEmpty() {
return levels.stream().allMatch(ApprovalLevelImpl::isEmpty);
public boolean shouldBeSkipped() {
return levels.stream().allMatch(ApprovalLevelImpl::shouldBeSkipped);
}
}
Expand Up @@ -106,14 +106,22 @@ public void execute(DelegateExecution execution) {
execution.getVariable(CommonProcessVariableNames.VARIABLE_PROCESS_INSTANCE_NAME),
execution.getProcessInstanceId(), level.getOutcomeIfNoApprovers());
predeterminedOutcome = level.getOutcomeIfNoApprovers();
if (predeterminedOutcome == ApprovalLevelOutcomeType.APPROVE) {
recordAutoApprovalDecision(wfTask, true,
"Approved automatically because there were no approvers found.", stageNumber, level,
prismContext);
} else {
recordAutoApprovalDecision(wfTask, false,
"Rejected automatically because there were no approvers found.", stageNumber, level,
prismContext);
switch (predeterminedOutcome) {
case APPROVE:
recordAutoApprovalDecision(wfTask, true,
"Approved automatically because there were no approvers found.", stageNumber, level,
prismContext);
break;
case REJECT:
recordAutoApprovalDecision(wfTask, false,
"Rejected automatically because there were no approvers found.", stageNumber, level,
prismContext);
break;
case SKIP:
// do nothing, just silently skip the level
break;
default:
throw new IllegalStateException("Unexpected outcome: " + level.getOutcomeIfNoApprovers() + " in " + level);
}
}
}
Expand Down
Expand Up @@ -69,7 +69,8 @@ public void execute(DelegateExecution execution) {
throw new IllegalStateException("Unknown level evaluation strategy: " + level.getEvaluationStrategy());
}
} else {
approved = predeterminedOutcome == ApprovalLevelOutcomeType.APPROVE;
approved = predeterminedOutcome == ApprovalLevelOutcomeType.APPROVE
|| predeterminedOutcome == ApprovalLevelOutcomeType.SKIP;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Approval process instance {} (id {}), level {}: result of this level: {}",
Expand Down
Expand Up @@ -141,7 +141,7 @@ private PcpChildWfTaskCreationInstruction<ItemApprovalSpecificContent> createIns

// Let's construct the approval schema plus supporting triggered approval policy rule information
ApprovalSchemaBuilder.Result approvalSchemaResult = createSchemaWithRules(triggeredApprovalActionRules, targetObject, ctx, result);
if (approvalSchemaResult.schema.isEmpty()) {
if (approvalSchemaResult.schema.shouldBeSkipped()) {
return null;
}

Expand Down Expand Up @@ -190,7 +190,7 @@ private ApprovalSchemaBuilder.Result createSchemaWithRules(List<EvaluatedPolicyR
level.getApproverRef().addAll(CloneUtil.cloneCollectionMembers(abstractRole.getApproverRef()));
level.getApproverExpression().addAll(CloneUtil.cloneCollectionMembers(abstractRole.getApproverExpression()));
level.setAutomaticallyApproved(abstractRole.getAutomaticallyApproved());
// consider default (if expression returns no approvers) -- currently it is "reject"
// consider default (if expression returns no approvers) -- currently it is "reject"; it is probably correct
builder.addPredefined(targetObject, level);
LOGGER.trace("Added legacy approval schema");
}
Expand Down Expand Up @@ -286,7 +286,7 @@ private void extractObjectBasedInstructions(@NotNull ObjectTreeDeltas objectTree
Set<ItemPath> itemsProcessed = null;
for (Map.Entry<Set<ItemPath>, ApprovalSchemaBuilder> entry : schemaBuilders.entrySet()) {
ApprovalSchemaBuilder.Result builderResult = entry.getValue().buildSchema(ctx, result);
if (builderResult.schema.isEmpty()) {
if (builderResult.schema.shouldBeSkipped()) {
continue;
}
Set<ItemPath> items = entry.getKey();
Expand Down
Expand Up @@ -28,7 +28,12 @@
<compositionStrategy>
<order>1</order>
</compositionStrategy>
<approverRelation>special-approver</approverRelation>
<approvalSchema>
<level>
<approverRelation>special-approver</approverRelation>
<outcomeIfNoApprovers>skip</outcomeIfNoApprovers> <!-- so e.g. for role4b no approval process is started -->
</level>
</approvalSchema>
</approval>
</policyActions>
</policyRule>
Expand Down

0 comments on commit 5e74101

Please sign in to comment.