Skip to content

Commit

Permalink
Preliminary implementation of MID-3414: ranges for mappings.
Browse files Browse the repository at this point in the history
(cherry picked from commit 278d1f8)
  • Loading branch information
mederly committed Sep 21, 2016
1 parent 9cb8c34 commit e7d1ac2
Show file tree
Hide file tree
Showing 13 changed files with 860 additions and 102 deletions.
Expand Up @@ -18,11 +18,8 @@

import com.evolveum.midpoint.prism.delta.ContainerDelta;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.path.IdItemPathSegment;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPathSegment;
import com.evolveum.midpoint.prism.path.NameItemPathSegment;
import com.evolveum.midpoint.util.DebugUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
Expand Down Expand Up @@ -855,4 +852,14 @@ protected String getDebugDumpClassName() {
return "PC";
}

public static <V extends PrismContainerValue> void createParentIfNeeded(V value, ItemDefinition definition) throws SchemaException {
if (value.getParent() != null) {
return;
}
if (!(definition instanceof PrismContainerDefinition)) {
throw new SchemaException("Missing or invalid definition for a PrismContainer: " + definition);
}
PrismContainer<?> rv = (PrismContainer) definition.instantiate();
rv.add(value);
}
}
Expand Up @@ -43,6 +43,7 @@ public class ExpressionConstants {
public static final QName VAR_PRISM_CONTEXT = new QName(SchemaConstants.NS_C, "prismContext");
public static final QName VAR_CONFIGURATION = new QName(SchemaConstants.NS_C, "configuration");
public static final QName VAR_ACTOR = new QName(SchemaConstants.NS_C, "actor");
public static final QName VAR_VALUE = new QName(SchemaConstants.NS_C, "value");

public static final QName VAR_LEGAL = new QName(SchemaConstants.NS_C, "legal");
public static final QName VAR_ASSIGNED = new QName(SchemaConstants.NS_C, "assigned");
Expand Down
Expand Up @@ -5300,6 +5300,19 @@
<xsd:element name="condition" type="tns:ExpressionType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="inputFilter" type="tns:ValueFilterType" minOccurs="0"/>
<xsd:element name="outputFilter" type="tns:ValueFilterType" minOccurs="0"/>
<xsd:element name="range" minOccurs="0" type="tns:ValueSetSpecificationType">
<xsd:annotation>
<xsd:documentation>
<p>
Specifies the range of the mapping (in mathematical sense). I.e. this specifies the values
that the mapping can produce. Range specification makes sense only for authoritative mappings.
If the range is specified then the mapping will scan existing values of the target property.
It will look for values that are there and that are also in the range of the mapping. If such
values are not in the expression results, then such values will be removed (placed in the minus set).
</p>
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="mapping" type="tns:MappingType"/>
Expand Down Expand Up @@ -5416,6 +5429,9 @@
are faulty and the ability of filters to do "technical" things
may come handy.
</xsd:documentation>
<xsd:appinfo>
<a:deprecated>true</a:deprecated>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:any minOccurs="0" maxOccurs="unbounded" processContents="lax">
Expand All @@ -5436,6 +5452,34 @@
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>

<xsd:complexType name="ValueSetSpecificationType">
<xsd:annotation>
<xsd:documentation>
Specifies set of prism item values. E.g. specifies a set of string values,
container values (e.g. assignments) and so on. It is used at places where
we need to select particular values based on a flexible set of criteria.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element ref="tns:description" minOccurs="0" maxOccurs="1"/>
<xsd:element name="isInSetExpression" type="tns:ExpressionType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Expression that is evaluated for each candidate value.
The value is passed to the expression in the 'input' variable.
If this expression returns true then the value is part of the set.
If it returns false (or no value) then the value is not part of the set.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<!-- More criteria specification may come here in the future.
E.g. expression that generates all possible values,
reference to lookup that has all the values,
query that works on top of the values,
string patterns, etc. -->
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="ExpressionType">
<xsd:annotation>
Expand Down
Expand Up @@ -192,14 +192,22 @@ public static <O extends ObjectType> void assertHasOrg(PrismObject<O> object, St
AssertJUnit.fail(object + " does not have org " + orgOid);
}

public static <O extends ObjectType> void assertHasOrg(PrismObject<O> user, String orgOid, QName relation) {
public static <O extends ObjectType> boolean hasOrg(PrismObject<O> user, String orgOid, QName relation) {
for (ObjectReferenceType orgRef: user.asObjectable().getParentOrgRef()) {
if (orgOid.equals(orgRef.getOid()) &&
MiscSchemaUtil.compareRelation(orgRef.getRelation(), relation)) {
return;
return true;
}
}
AssertJUnit.fail(user + " does not have org " + orgOid + ", relation "+relation);
return false;
}

public static <O extends ObjectType> void assertHasOrg(PrismObject<O> user, String orgOid, QName relation) {
AssertJUnit.assertTrue(user + " does not have org " + orgOid + ", relation "+relation, hasOrg(user, orgOid, relation));
}

public static <O extends ObjectType> void assertHasNoOrg(PrismObject<O> user, String orgOid, QName relation) {
AssertJUnit.assertFalse(user + " has org " + orgOid + ", relation "+relation+ " even if should NOT have it", hasOrg(user, orgOid, relation));
}

public static <O extends ObjectType> void assertHasNoOrg(PrismObject<O> user) {
Expand Down
Expand Up @@ -39,6 +39,8 @@
import javax.naming.ldap.Rdn;
import javax.xml.datatype.XMLGregorianCalendar;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
Expand All @@ -47,10 +49,6 @@

import com.evolveum.midpoint.model.common.expression.script.ScriptExpression;
import com.evolveum.midpoint.model.common.expression.script.ScriptExpressionEvaluationContext;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.Referencable;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.prism.path.ItemPath;
Expand All @@ -66,10 +64,6 @@
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.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;

Expand Down
Expand Up @@ -26,9 +26,14 @@
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.schema.SchemaConstantsGenerated;
import com.evolveum.midpoint.schema.constants.ExpressionConstants;
import com.evolveum.midpoint.security.api.SecurityEnforcer;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;

import org.jetbrains.annotations.NotNull;
import org.w3c.dom.Element;

import com.evolveum.midpoint.common.InternalsConfig;
Expand All @@ -44,18 +49,6 @@
import com.evolveum.midpoint.model.common.expression.ObjectDeltaObject;
import com.evolveum.midpoint.model.common.expression.Source;
import com.evolveum.midpoint.model.common.expression.StringPolicyResolver;
import com.evolveum.midpoint.prism.Item;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.OriginType;
import com.evolveum.midpoint.prism.Visitable;
import com.evolveum.midpoint.prism.Visitor;
import com.evolveum.midpoint.prism.delta.PrismValueDeltaSetTriple;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
Expand All @@ -73,16 +66,6 @@
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ExpressionVariableDefinitionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingSourceDeclarationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingStrengthType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingTargetDeclarationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingTimeDeclarationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ValueFilterType;

/**
*
Expand All @@ -99,31 +82,34 @@ public class Mapping<V extends PrismValue,D extends ItemDefinition> implements D
private static final QName CONDITION_OUTPUT_NAME = new QName(SchemaConstants.NS_C, "condition");

// configuration properties (unmodifiable)
private final MappingType mappingType;
private final ExpressionFactory expressionFactory;
private final ExpressionVariables variables;
private final MappingType mappingType;
private final ObjectResolver objectResolver;
private final SecurityEnforcer securityEnforcer; // in order to get c:actor variable
private final Source<?,?> defaultSource;
private final D defaultTargetDefinition;
private final ItemPath defaultTargetPath;

private final ObjectDeltaObject<?> sourceContext;
private final PrismObjectDefinition<?> targetContext;
private final Collection<Source<?,?>> sources;
private final Source<?,?> defaultSource;

private final PrismObjectDefinition<?> targetContext;
private final ItemPath defaultTargetPath;
private final D defaultTargetDefinition;
private final Collection<V> originalTargetValues;

private final ObjectResolver objectResolver;
private final SecurityEnforcer securityEnforcer; // in order to get c:actor variable
private final OriginType originType;
private final ObjectType originObject;
private final FilterManager<Filter> filterManager;
private final StringPolicyResolver stringPolicyResolver;
private final boolean conditionMaskOld;
private final boolean conditionMaskNew;
private final XMLGregorianCalendar defaultReferenceTime;
private final XMLGregorianCalendar now;
private final boolean profiling;
private final String contextDescription;
private final XMLGregorianCalendar now;

// This is sometimes used to identify the element that mapping produces
// if it is different from itemName. E.g. this happens with associations.
private final QName mappingQName;
private final QName mappingQName; // This is sometimes used to identify the element that mapping produces
// if it is different from itemName. E.g. this happens with associations.
private final RefinedObjectClassDefinition refinedObjectClassDefinition;

// working and output properties
Expand Down Expand Up @@ -154,6 +140,7 @@ private Mapping(Builder<V,D> builder) {
defaultSource = builder.defaultSource;
defaultTargetDefinition = builder.defaultTargetDefinition;
defaultTargetPath = builder.defaultTargetPath;
originalTargetValues = builder.originalTargetValues;
sourceContext = builder.sourceContext;
targetContext = builder.targetContext;
sources = builder.sources;
Expand Down Expand Up @@ -392,18 +379,17 @@ public void evaluate(Task task, OperationResult parentResult) throws ExpressionE
boolean conditionOutputNew = computeConditionResult(conditionOutputTriple.getNonNegativeValues());
boolean conditionResultNew = conditionOutputNew && conditionMaskNew;

if (!conditionResultOld && !conditionResultNew) {
result.recordSuccess();
traceSuccess(conditionResultOld, conditionResultNew);
return;
if (conditionResultOld || conditionResultNew) {
// TODO: input filter
evaluateExpression(task, result, conditionResultOld, conditionResultNew);
fixDefinition();
recomputeValues();
setOrigin();
// TODO: output filter
}
// TODO: input filter
evaluateExpression(task, result, conditionResultOld, conditionResultNew);
fixDefinition();
recomputeValues();
setOrigin();
// TODO: output filter


checkRange(task, result);

result.recordSuccess();
traceSuccess(conditionResultOld, conditionResultNew);

Expand All @@ -413,7 +399,56 @@ public void evaluate(Task task, OperationResult parentResult) throws ExpressionE
throw e;
}
}


private void checkRange(Task task, OperationResult result)
throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
if (mappingType.getRange() == null) {
return;
}
if (originalTargetValues == null) {
throw new IllegalStateException("Couldn't check range for mapping in " + contextDescription + ", as original target values are not known.");
}
for (V originalValue : originalTargetValues) {
if (!isInRange(originalValue, task, result)) {
continue;
}
if (outputTriple != null && (outputTriple.presentInPlusSet(originalValue) || outputTriple.presentInZeroSet(originalValue))) {
continue;
}
// remove it!
if (outputTriple == null) {
outputTriple = new PrismValueDeltaSetTriple<>();
}
LOGGER.trace("Original value is not in the mapping range, adding it to minus set: {}", originalValue);
outputTriple.addToMinusSet(originalValue);
}
}

private boolean isInRange(V value, Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException, SchemaException {
@NotNull ValueSetSpecificationType range = mappingType.getRange();
if (range.getIsInSetExpression() == null) {
return false;
}
ExpressionVariables variables = new ExpressionVariables();
variables.addVariableDefinitions(this.variables); // TODO is this ok?

if (value instanceof PrismContainerValue) {
// artifically create parent for PCV in order to pass it to expression
PrismContainer.createParentIfNeeded((PrismContainerValue) value, outputDefinition);
}
variables.addVariableDefinition(ExpressionConstants.VAR_VALUE, value);

PrismPropertyDefinition<Boolean> outputDef = new PrismPropertyDefinition<>(SchemaConstantsGenerated.C_VALUE, DOMUtil.XSD_BOOLEAN, getPrismContext(), null, false);
PrismPropertyValue<Boolean> rv = ExpressionUtil.evaluateExpression(variables, outputDef, range.getIsInSetExpression(), expressionFactory, "isInSet expression in " + contextDescription, task, result);

// but now remove the parent!
if (value.getParent() != null) {
value.setParent(null);
}

return rv != null && rv.getValue() != null ? rv.getValue() : Boolean.FALSE;
}

public boolean isSatisfyCondition() {
boolean conditionOutputOld = computeConditionResult(conditionOutputTriple.getNonPositiveValues());
boolean conditionResultOld = conditionOutputOld && conditionMaskOld;
Expand Down Expand Up @@ -1147,6 +1182,7 @@ public static final class Builder<V extends PrismValue, D extends ItemDefinition
private Source<?, ?> defaultSource;
private D defaultTargetDefinition;
private ItemPath defaultTargetPath;
private Collection<V> originalTargetValues;
private ObjectDeltaObject<?> sourceContext;
private PrismObjectDefinition<?> targetContext;
private Collection<Source<?, ?>> sources = new ArrayList<>();
Expand Down Expand Up @@ -1205,6 +1241,11 @@ public Builder<V,D> defaultTargetPath(ItemPath val) {
return this;
}

public Builder<V,D> originalTargetValues(Collection<V> values) {
originalTargetValues = values;
return this;
}

public Builder<V,D> sourceContext(ObjectDeltaObject<?> val) {
sourceContext = val;
return this;
Expand Down Expand Up @@ -1321,6 +1362,10 @@ public ItemPath getDefaultTargetPath() {
return defaultTargetPath;
}

public Collection<V> getOriginalTargetValues() {
return originalTargetValues;
}

public ObjectDeltaObject<?> getSourceContext() {
return sourceContext;
}
Expand Down

0 comments on commit e7d1ac2

Please sign in to comment.