Skip to content

Commit

Permalink
Consolidation of approval schemas: level->stage, order->number.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Apr 6, 2017
1 parent b086ca3 commit 3738e24
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 102 deletions.
Expand Up @@ -16,6 +16,7 @@

package com.evolveum.midpoint.schema.util;

import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -153,8 +154,8 @@ public static List<SchemaAttachedPolicyRuleType> getAttachedPolicyRules(WfContex
return Collections.emptyList();
}
return info.getPolicyRules().getEntry().stream()
.filter(e -> e.getLevelMax() != null && e.getLevelMax() != null
&& order >= e.getLevelMin() && order <= e.getLevelMax())
.filter(e -> e.getStageMax() != null && e.getStageMax() != null
&& order >= e.getStageMin() && order <= e.getStageMax())
.collect(Collectors.toList());
}

Expand All @@ -165,23 +166,28 @@ public static ApprovalStageDefinitionType getCurrentStageDefinition(WfContextTyp
return getStageDefinition(wfc, wfc.getStageNumber());
}

// expects already normalized definition (using non-deprecated items, numbering stages from 1 to N)
public static ApprovalStageDefinitionType getStageDefinition(WfContextType wfc, int stageNumber) {
ItemApprovalProcessStateType info = getItemApprovalProcessInfo(wfc);
if (info == null || info.getApprovalSchema() == null) {
return null;
}
List<ApprovalStageDefinitionType> levels = info.getApprovalSchema().getLevel().stream()
.filter(level -> level.getOrder() != null && level.getOrder() == stageNumber)
ApprovalSchemaType approvalSchema = info.getApprovalSchema();
List<ApprovalStageDefinitionType> stages = approvalSchema.getStage().stream()
.filter(level -> level.getNumber() != null && level.getNumber() == stageNumber)
.collect(Collectors.toList());
if (levels.size() > 1) {
throw new IllegalStateException("More than one level with order of " + stageNumber + ": " + levels);
} else if (levels.isEmpty()) {
if (stages.size() > 1) {
throw new IllegalStateException("More than one level with order of " + stageNumber + ": " + stages);
} else if (stages.isEmpty()) {
return null;
} else {
return levels.get(0);
return stages.get(0);
}
}

private static List<ApprovalStageDefinitionType> getStages(ApprovalSchemaType approvalSchema) {
return !approvalSchema.getStage().isEmpty() ? approvalSchema.getStage() : approvalSchema.getLevel();
}

// we must be strict here; in case of suspicion, throw an exception
@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -230,42 +236,49 @@ public static String getCurrentStageOutcome(WfContextType wfc, List<StageComplet
return event.getOutcome();
}

// expects normalized definition
public static String getStageDiagName(ApprovalStageDefinitionType level) {
return level.getOrder() + ":" + level.getName()
return level.getNumber() + ":" + level.getName()
+ (level.getDisplayName() != null ? " (" + level.getDisplayName() + ")" : "");
}

public static void orderAndRenumberStages(ApprovalSchemaType schema) {
public static void normalizeStages(ApprovalSchemaType schema) {
// Sorting uses set(..) method which is not available on prism structures. So we do sort on a copy (ArrayList).
List<ApprovalStageDefinitionType> levels = new ArrayList<>(schema.getLevel());
levels.sort(Comparator.comparing(level -> level.getOrder(), Comparator.nullsLast(Comparator.naturalOrder())));
for (int i = 0; i < levels.size(); i++) {
levels.get(i).setOrder(i+1);
List<ApprovalStageDefinitionType> stages = new ArrayList<>(getStages(schema));
stages.sort(Comparator.comparing(stage -> getNumber(stage), Comparator.nullsLast(Comparator.naturalOrder())));
for (int i = 0; i < stages.size(); i++) {
stages.get(i).setOrder(null);
stages.get(i).setNumber(i+1);
}
schema.getLevel().clear();
schema.getLevel().addAll(levels);
schema.getStage().clear();
schema.getStage().addAll(CloneUtil.cloneCollectionMembers(stages));
}

public static void checkLevelsOrdering(ApprovalSchemaType schema) {
for (int i = 0; i < schema.getLevel().size(); i++) {
ApprovalStageDefinitionType level = schema.getLevel().get(i);
if (level.getOrder() == null) {
throw new IllegalStateException("Level without order: " + level);
}
if (i > 0 && schema.getLevel().get(i-1).getOrder() >= level.getOrder()) {
throw new IllegalStateException("Level #" + i + " is not before level #" + (i+1) + " in " + schema);
}
}
private static Integer getNumber(ApprovalStageDefinitionType stage) {
return stage.getNumber() != null ? stage.getNumber() : stage.getOrder();
}

public static void checkLevelsOrderingStrict(ApprovalSchemaType schema) {
for (int i = 0; i < schema.getLevel().size(); i++) {
Integer order = schema.getLevel().get(i).getOrder();
if (order == null || order != i+1) {
throw new IllegalStateException("Level #" + (i+1) + " has an incorrect order: " + order + " in " + schema);
}
}
}
// public static void checkLevelsOrdering(ApprovalSchemaType schema) {
// for (int i = 0; i < schema.getLevel().size(); i++) {
// ApprovalStageDefinitionType level = schema.getLevel().get(i);
// if (level.getOrder() == null) {
// throw new IllegalStateException("Level without order: " + level);
// }
// if (i > 0 && schema.getLevel().get(i-1).getOrder() >= level.getOrder()) {
// throw new IllegalStateException("Level #" + i + " is not before level #" + (i+1) + " in " + schema);
// }
// }
// }
//
// public static void checkLevelsOrderingStrict(ApprovalSchemaType schema) {
// for (int i = 0; i < schema.getLevel().size(); i++) {
// Integer order = schema.getLevel().get(i).getOrder();
// if (order == null || order != i+1) {
// throw new IllegalStateException("Level #" + (i+1) + " has an incorrect order: " + order + " in " + schema);
// }
// }
// }

public static OperationBusinessContextType getBusinessContext(WfContextType wfc) {
if (wfc == null) {
Expand Down
Expand Up @@ -601,7 +601,7 @@
<xsd:annotation>
<xsd:documentation>
<p>
More complex (multi-level) approval schema.
More complex (multi-stages) approval schema.
</p>
</xsd:documentation>
</xsd:annotation>
Expand Down Expand Up @@ -677,8 +677,8 @@
Mergeable fragments must have "order" attribute set.

Restrictions on merging schema fragments:
1) Mergeable fragments must contain exactly one level. It is merged with the level(s) of the other
fragment(s) without any further considerations (e.g. regarding level order or name or whatever).
1) Mergeable fragments must contain exactly one stage. It is merged with the stage(s) of the other
fragment(s) without any further considerations (e.g. regarding stage order or name or whatever).
2) It is expected that single policy rule triggered all the fragments that are being merged. So
we consider any of these rules.
</xsd:documentation>
Expand All @@ -693,7 +693,7 @@
<xsd:documentation>
Which fragments should this one be merged with. This is mutually exclusive with the "order" attribute.
If order is set, it is not possible to specify mergeIntoOrder. Both fragments with the same order are
considered "equal". This can be used e.g. to provide a different set of approvers (for the given level).
considered "equal". This can be used e.g. to provide a different set of approvers (for the given stage).
If mergeIntoOrder is set (usually with multiple values), this fragment is considered as an augmentation
of other ones. This can be used e.g. to provide an escalation or notification strategy, custom form information, etc.
</xsd:documentation>
Expand Down Expand Up @@ -728,7 +728,7 @@
<xsd:element name="mergeOverwriting" type="xsd:QName" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
If level B is being merged into existing level A (i.e. mergeOrder B is greater mergeOrder A),
If stage B is being merged into existing stage A (i.e. mergeOrder B is greater mergeOrder A),
all non-null values of B will be added to values of A; overwriting them in case of single-valued items.
For items marked as "mergeOverwriting", the actual values of B (even those that are null or empty)
overwrite those in A.
Expand Down
Expand Up @@ -58,6 +58,20 @@
<xsd:documentation>
Levels, or stages, of the approval process.
</xsd:documentation>
<xsd:appinfo>
<a:deprecated>true</a:deprecated>
<a:deprecatedSince>3.6</a:deprecatedSince>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="stage" type="c:ApprovalStageDefinitionType" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Levels, or stages, of the approval process.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.6</a:since>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
Expand All @@ -76,8 +90,22 @@
<xsd:element name="order" type="xsd:int" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Order of this approval level. (Marked as optional only to ensure backward compatibility. Will change to 'required' eventually.)
Order of this approval stage. DEPRECATED. Use "number" instead.
</xsd:documentation>
<xsd:appinfo>
<a:deprecated>true</a:deprecated>
<a:deprecatedSince>3.6</a:deprecatedSince>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="number" type="xsd:int" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Number of this approval stage. These should go from 1 to N.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.6</a:since>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="name" type="xsd:string" minOccurs="0"/>
Expand Down Expand Up @@ -117,15 +145,15 @@
<xsd:element name="evaluationStrategy" type="c:LevelEvaluationStrategyType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Must all approvers at this level approve the thing (allMustApprove),
Must all approvers at this stage approve the thing (allMustApprove),
or first decision is taken as authoritative (firstDecides)?
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="outcomeIfNoApprovers" type="c:ApprovalLevelOutcomeType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
What is the outcome (of this level) if there are no approvers? E.g. there are no users that have
What is the outcome (of this stage) if there are no approvers? E.g. there are no users that have
been assigned a role as an approver; or a user has no managers, etc.
</xsd:documentation>
<xsd:appinfo>
Expand Down Expand Up @@ -169,7 +197,7 @@
<xsd:element name="automaticallyApproved" type="c:ExpressionType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Condition specifying when this level is automatically approved (e.g. "user is
Condition specifying when this stage is automatically approved (e.g. "user is
from Board of Directors"). This is an expression that should yield a boolean value.

DEPRECATED. Use automaticallyCompleted instead.
Expand All @@ -183,7 +211,7 @@
<xsd:element name="automaticallyCompleted" type="c:ExpressionType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Expression specifying that this level should be automatically processed (approved, rejected, skipped).
Expression specifying that this stage should be automatically processed (approved, rejected, skipped).
If the expression returns null, standard processing by human actors is carried out.
</xsd:documentation>
<xsd:appinfo>
Expand All @@ -194,7 +222,7 @@
<xsd:element name="duration" type="xsd:duration" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Duration of work items created at this level.
Duration of work items created at this stage.
TODO other time units, like business days?
</xsd:documentation>
<xsd:appinfo>
Expand Down Expand Up @@ -311,7 +339,7 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<!-- TODO maybe also completeLevel -->
<!-- TODO maybe also completeStage -->
<xsd:element name="notify" type="tns:WorkItemNotificationActionType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Expand Down Expand Up @@ -582,7 +610,7 @@
<xsd:simpleType name="ApprovalLevelOutcomeType">
<xsd:annotation>
<xsd:documentation>
Result (outcome) of an approval process level.
Result (outcome) of an approval process stage.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumClass/>
Expand All @@ -592,7 +620,7 @@
<xsd:enumeration value="approve">
<xsd:annotation>
<xsd:documentation>
Operation was approved at this level. The approval process will continue at the next level.
Operation was approved at this stage. The approval process will continue at the next stage.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="APPROVE"/>
Expand All @@ -602,7 +630,7 @@
<xsd:enumeration value="reject">
<xsd:annotation>
<xsd:documentation>
Operation was rejected at this level. The approval process will stop.
Operation was rejected at this stage. The approval process will stop.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="REJECT"/>
Expand All @@ -612,7 +640,7 @@
<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
This stage 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.
Expand Down Expand Up @@ -727,7 +755,7 @@
<xsd:simpleType name="LevelEvaluationStrategyType">
<xsd:annotation>
<xsd:documentation>
Enumeration of approval strategies at a particular level.
Enumeration of approval strategies at a particular stage.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumClass/>
Expand All @@ -737,7 +765,7 @@
<xsd:enumeration value="allMustApprove">
<xsd:annotation>
<xsd:documentation>
All approvers at a particular level must approve the operation.
All approvers at a particular stage must approve the operation.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="ALL_MUST_AGREE"/>
Expand All @@ -747,7 +775,7 @@
<xsd:enumeration value="firstDecides">
<xsd:annotation>
<xsd:documentation>
First approver that votes will decide the whole level (either by approving or by rejecting).
First approver that votes will decide the whole stage (either by approving or by rejecting).
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="FIRST_DECIDES"/>
Expand Down Expand Up @@ -1062,7 +1090,7 @@
<xsd:element name="approvalSchema" type="c:ApprovalSchemaType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
More complex (multi-level) approval schema. If used, it overrides both
More complex (multi-stage) approval schema. If used, it overrides both
approverRef and approverExpression elements.
</xsd:documentation>
</xsd:annotation>
Expand Down Expand Up @@ -1850,7 +1878,7 @@
<xsd:documentation>
When was the operation requested, i.e. when the approval process started?

(Normally, this information is relevant at the task/wf-process level.
(Normally, this information is relevant at the task/wf-process stage.
However we put it here to avoid fetching tasks when we want to display
work item list only.)
</xsd:documentation>
Expand All @@ -1873,7 +1901,7 @@
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.
(Normally, this information is relevant at the task/wf-process stage.
However we put it here to avoid fetching tasks when we want to display
work item list only.)
</xsd:documentation>
Expand All @@ -1888,7 +1916,7 @@
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.
(Normally, this information is relevant at the task/wf-process stage.
However we put it here to avoid fetching tasks when we want to display
work item list only.)
</xsd:documentation>
Expand Down Expand Up @@ -1982,8 +2010,8 @@
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="levelMin" type="xsd:int" minOccurs="0"/>
<xsd:element name="levelMax" type="xsd:int" minOccurs="0"/>
<xsd:element name="stageMin" type="xsd:int" minOccurs="0"/>
<xsd:element name="stageMax" type="xsd:int" minOccurs="0"/>
<xsd:element name="rule" type="tns:EvaluatedPolicyRuleType" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
Expand Down
Expand Up @@ -69,7 +69,7 @@ private ApprovalSchemaType getSchema(ApprovalSchemaType schema, List<ObjectRefer
stageDef.getApproverRef().addAll(CloneUtil.cloneCollectionMembers(approverRef));
stageDef.getApproverExpression().addAll(approverExpression);
stageDef.setAutomaticallyApproved(automaticallyApproved);
schema.getLevel().add(stageDef);
schema.getStage().add(stageDef);
return schema;
}
}
Expand Down

0 comments on commit 3738e24

Please sign in to comment.