/*
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.evolveum.midpoint.wf.impl.processes.itemApproval;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.util.WfContextUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* TODO
*
* Generator of default 'additional information' section for specific constraints (exclusion, assignment, ...).
*
* @author mederly
*/
class AdditionalInformationGenerator {
@NotNull
List<InformationType> getDefaultAdditionalInformation(Task wfTask, int order) {
// later here may be more rules (stage amalgamation)
List<SchemaAttachedPolicyRuleType> attachedRules = WfContextUtil.getAttachedPolicyRules(wfTask.getWorkflowContext(), order);
List<EvaluatedPolicyRuleTriggerType> triggers = new ArrayList<>();
attachedRules.forEach(rule -> collectPrimitiveTriggers(triggers, rule.getRule()));
List<InformationType> rv = new ArrayList<>();
generateAssignmentMessages(rv, extractTriggers(triggers, PolicyConstraintKindType.ASSIGNMENT));
generateExclusionMessages(rv, extractTriggers(triggers, PolicyConstraintKindType.EXCLUSION));
generateOtherMessages(rv, triggers);
return rv;
}
private void collectPrimitiveTriggers(List<EvaluatedPolicyRuleTriggerType> triggers, EvaluatedPolicyRuleType attachedRule) {
for (EvaluatedPolicyRuleTriggerType trigger : attachedRule.getTrigger()) {
if (trigger instanceof EvaluatedSituationTriggerType) {
((EvaluatedSituationTriggerType) trigger).getSourceRule().forEach(r -> collectPrimitiveTriggers(triggers, r));
} else {
triggers.add(trigger);
}
}
}
private List<EvaluatedPolicyRuleTriggerType> extractTriggers(List<EvaluatedPolicyRuleTriggerType> triggers,
PolicyConstraintKindType kind) {
List<EvaluatedPolicyRuleTriggerType> rv = new ArrayList<>();
for (Iterator<EvaluatedPolicyRuleTriggerType> iterator = triggers.iterator(); iterator.hasNext(); ) {
EvaluatedPolicyRuleTriggerType trigger = iterator.next();
if (trigger.getConstraintKind() == kind) {
rv.add(trigger);
iterator.remove();
}
}
return rv;
}
@SuppressWarnings("unused")
private void generateAssignmentMessages(List<InformationType> infoList, List<EvaluatedPolicyRuleTriggerType> triggers) {
// Nothing to do here. The information about assignments to be added/removed is obvious from the delta.
// if (triggers.isEmpty()) {
// return;
// }
// InformationType info = new InformationType();
// info.setTitle("New assignment(s) to be approved");
// for (EvaluatedPolicyRuleTriggerType trigger : triggers) {
// InformationPartType part = new InformationPartType();
// // Note: this is the name of role that has the respective policy directly assigned. (Currently it is the same
// // as the target of the assignment being approved.)
// part.setText("Assignment of " + getObjectTypeAndName(trigger.getDirectOwnerRef(), trigger.getDirectOwnerDisplayName()) + " is to be approved.");
// info.getPart().add(part);
// }
// infoList.add(info);
}
private void generateExclusionMessages(List<InformationType> infoList, List<EvaluatedPolicyRuleTriggerType> triggers) {
if (triggers.isEmpty()) {
return;
}
InformationType info = new InformationType();
info.setTitle("Exclusions to be approved");
for (EvaluatedPolicyRuleTriggerType trigger : triggers) {
EvaluatedExclusionTriggerType exclusion = (EvaluatedExclusionTriggerType) trigger;
InformationPartType part = new InformationPartType();
StringBuilder sb = new StringBuilder();
sb.append("Assignment of ")
.append(getObjectTypeAndName(exclusion.getDirectOwnerRef(), exclusion.getDirectOwnerDisplayName()));
String thisPathInfo = getObjectPathIfRelevant(exclusion.getAssignmentPath());
if (thisPathInfo != null) {
sb.append(" (").append(thisPathInfo).append(")");
}
sb.append(" is in conflict with assignment of ")
.append(getObjectTypeAndName(exclusion.getConflictingObjectRef(), exclusion.getConflictingObjectDisplayName()));
String conflictingPathInfo = getObjectPathIfRelevant(exclusion.getConflictingObjectPath());
if (conflictingPathInfo != null) {
sb.append(" (").append(conflictingPathInfo).append(")");
}
sb.append(".");
part.setText(sb.toString());
info.getPart().add(part);
}
infoList.add(info);
}
// We simply show all messages from triggers.
private void generateOtherMessages(List<InformationType> infoList, List<EvaluatedPolicyRuleTriggerType> triggers) {
if (triggers.isEmpty()) {
return;
}
InformationType info = new InformationType();
info.setTitle("Notes");
for (EvaluatedPolicyRuleTriggerType trigger : triggers) {
InformationPartType part = new InformationPartType();
part.setText(trigger.getMessage());
info.getPart().add(part);
}
infoList.add(info);
}
private Stream<String> getRuleMessages(EvaluatedPolicyRuleType rule) {
if (rule == null) {
return null;
}
return rule.getTrigger().stream()
.flatMap(this::getTriggerMessages);
}
private Stream<String> getTriggerMessages(EvaluatedPolicyRuleTriggerType t) {
if (t instanceof EvaluatedSituationTriggerType) {
return ((EvaluatedSituationTriggerType) t).getSourceRule().stream()
.flatMap(this::getRuleMessages);
} else {
return t.getMessage() != null ? Stream.of(t.getMessage()) : Stream.empty();
}
}
private String getObjectTypeAndName(ObjectReferenceType ref, PolyStringType displayNamePoly) {
String name = PolyString.getOrig(ref.getTargetName());
String displayName = PolyString.getOrig(displayNamePoly);
String typeName = ObjectTypes.getDisplayNameForTypeName(ref.getType(), null);
StringBuilder sb = new StringBuilder();
if (typeName != null) {
sb.append(typeName.toLowerCase()).append(" ");
}
if (name != null && displayName != null) {
sb.append(displayName).append(" (").append(name).append(")");
} else if (name != null) {
sb.append(name);
} else if (displayName != null) {
sb.append(displayName);
} else {
sb.append(ref.getOid());
}
return sb.toString();
}
private String getObjectPathIfRelevant(AssignmentPathType path) {
if (path == null) {
return null;
}
List<AssignmentPathSegmentType> matchingOrder = path.getSegment().stream()
.filter(seg -> seg.isMatchingOrder()).collect(Collectors.toList());
if (matchingOrder.size() < 2) {
return null;
}
return matchingOrder.stream()
.map(seg -> getDisplayableName(seg.getTargetRef(), seg.getTargetDisplayName()))
.collect(Collectors.joining(" -> "));
}
private String getDisplayableName(ObjectReferenceType ref, PolyStringType displayName) {
if (displayName != null) {
return displayName.getOrig();
} else if (ref == null) {
return null;
} else if (ref.getTargetName() != null) {
return ref.getTargetName().getOrig();
} else {
return ref.getOid();
}
}
}