Skip to content

Commit

Permalink
Support for situation in object merge (MID-3460)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Oct 28, 2016
1 parent 2686442 commit 0904ce8
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 10 deletions.
Expand Up @@ -12905,6 +12905,7 @@
<xsd:complexContent>
<xsd:extension base="tns:ItemMergeConfigurationType">
<xsd:sequence>
<xsd:element name="situation" type="tns:ProjectionMergeSituationType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="projectionDiscriminator" type="tns:ShadowDiscriminatorType" minOccurs="0" maxOccurs="1"/>
</xsd:sequence>
</xsd:extension>
Expand Down Expand Up @@ -12952,6 +12953,49 @@
</xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="ProjectionMergeSituationType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="existing">
<xsd:annotation>
<xsd:documentation>
Projection exists on the left side. There is no conflicting
projection on the right side.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="EXISTING"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="mergeable">
<xsd:annotation>
<xsd:documentation>
Projection exists on the right side. There is no conflicting
projection on the left side. The projection can be merged.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="MERGEABLE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="conflict">
<xsd:annotation>
<xsd:documentation>
There are two conflicting projections, one on the left side
other on the right side.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="CONFLICT"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="CachingPolicyType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Expand Up @@ -78,6 +78,7 @@
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.ProjectionMergeConfigurationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ProjectionMergeSituationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowDiscriminatorType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SystemConfigurationType;
Expand Down Expand Up @@ -308,12 +309,13 @@ private <O extends ObjectType> void computeProjectionDeltas(final ObjectDelta<O>

ProjectionMergeConfigurationType defaultProjectionMergeConfig = null;
for (ProjectionMergeConfigurationType projectionMergeConfig: mergeConfiguration.getProjection()) {
ShadowDiscriminatorType discriminatorType = projectionMergeConfig.getProjectionDiscriminator();
if (discriminatorType == null) {
if (projectionMergeConfig.getProjectionDiscriminator() == null && projectionMergeConfig.getSituation() == null) {
defaultProjectionMergeConfig = projectionMergeConfig;
} else {
takeProjections(projectionMergeConfig.getLeft(), mergedProjections, matchedProjections, projectionsLeft, discriminatorType);
takeProjections(projectionMergeConfig.getRight(), mergedProjections, matchedProjections, projectionsRight, discriminatorType);
takeProjections(projectionMergeConfig.getLeft(), mergedProjections, matchedProjections,
projectionsLeft, projectionsLeft, projectionsRight, projectionMergeConfig);
takeProjections(projectionMergeConfig.getRight(), mergedProjections, matchedProjections,
projectionsRight, projectionsLeft, projectionsRight, projectionMergeConfig);
}
}

Expand Down Expand Up @@ -410,13 +412,19 @@ private <O extends ObjectType> List<ShadowType> getProjections(PrismObject<O> ob

private void takeProjections(MergeStategyType strategy, List<ShadowType> mergedProjections,
List<ShadowType> matchedProjections, List<ShadowType> candidateProjections,
ShadowDiscriminatorType discriminatorType) {
List<ShadowType> projectionsLeft, List<ShadowType> projectionsRight,
ProjectionMergeConfigurationType projectionMergeConfig) {

LOGGER.trace("TAKE: Evaluating discriminator: {}", discriminatorType);
if (LOGGER.isTraceEnabled()) {

LOGGER.trace("TAKE: Evaluating situation {}, discriminator: {}",
projectionMergeConfig.getSituation(), projectionMergeConfig.getProjectionDiscriminator());
}

for (ShadowType candidateProjection: candidateProjections) {
if (ShadowUtil.matchesPattern(candidateProjection, discriminatorType)) {
LOGGER.trace("Discriminator matches {}", candidateProjection);

if (projectionMatches(candidateProjection, projectionsLeft, projectionsRight, projectionMergeConfig)) {
LOGGER.trace("Projection matches {}", candidateProjection);
matchedProjections.add(candidateProjection);

if (strategy == MergeStategyType.TAKE) {
Expand All @@ -437,6 +445,23 @@ private void takeProjections(MergeStategyType strategy, List<ShadowType> mergedP

}

private boolean projectionMatches(ShadowType candidateProjection,
List<ShadowType> projectionsLeft, List<ShadowType> projectionsRight,
ProjectionMergeConfigurationType projectionMergeConfig) {
ShadowDiscriminatorType discriminatorType = projectionMergeConfig.getProjectionDiscriminator();
if (discriminatorType != null && !ShadowUtil.matchesPattern(candidateProjection, discriminatorType)) {
return false;
}
ProjectionMergeSituationType situationPattern = projectionMergeConfig.getSituation();
if (situationPattern != null) {
ProjectionMergeSituationType projectionSituation = determineSituation(candidateProjection, projectionsLeft, projectionsRight);
if (situationPattern != projectionSituation) {
return false;
}
}
return true;
}

private void takeUnmatchedProjections(MergeStategyType strategy, List<ShadowType> mergedProjections,
List<ShadowType> matchedProjections, List<ShadowType> candidateProjections) {
if (strategy == MergeStategyType.TAKE) {
Expand All @@ -453,6 +478,31 @@ private void takeUnmatchedProjections(MergeStategyType strategy, List<ShadowType
throw new UnsupportedOperationException("Merge strategy "+strategy+" is not supported");
}
}

private ProjectionMergeSituationType determineSituation(ShadowType candidateProjection, List<ShadowType> projectionsLeft,
List<ShadowType> projectionsRight) {
boolean matchLeft = hasMatchingProjection(candidateProjection, projectionsLeft);
boolean matchRight = hasMatchingProjection(candidateProjection, projectionsRight);
if (matchLeft && matchRight) {
return ProjectionMergeSituationType.CONFLICT;
} else if (matchLeft) {
return ProjectionMergeSituationType.EXISTING;
} else if (matchRight) {
return ProjectionMergeSituationType.MERGEABLE;
} else {
throw new IllegalStateException("Booom! The universe has imploded.");
}
}

private boolean hasMatchingProjection(ShadowType cprojection, List<ShadowType> projections) {
for (ShadowType projection: projections) {
if (ShadowUtil.isConflicting(projection, cprojection)) {
return true;
}
}
return false;
}


private void checkConflict(List<ShadowType> projections) throws SchemaException {
for (ShadowType projection: projections) {
Expand All @@ -464,9 +514,8 @@ private void checkConflict(List<ShadowType> projections) throws SchemaException
throw new SchemaException("Merge would result in projection conflict between "+projection+" and "+cprojection);
}
}
}
}
}


private ShadowType getProjection(ObjectReferenceType linkRef, Task task, OperationResult result) throws ObjectNotFoundException, CommunicationException, SchemaException, ConfigurationException, SecurityViolationException {
Collection<SelectorOptions<GetOperationOptions>> options = SelectorOptions.createCollection(GetOperationOptions.createNoFetch());
Expand Down
Expand Up @@ -48,6 +48,7 @@ public class TestMerge extends AbstractInitializedModelIntegrationTest {
public static final File TEST_DIR = new File("src/test/resources/merge");

public static final String MERGE_CONFIG_DEFAULT_NAME = "default";
public static final String MERGE_CONFIG_DEFAULT_SPECIFIC_NAME = "default-specific";
public static final String MERGE_CONFIG_EXPRESSION_NAME = "expression";

private String jackDummyAccountOid;
Expand Down Expand Up @@ -430,6 +431,70 @@ public void test202MergeJackGuybrushExpressionPreviewObject() throws Exception {
}


/**
* The default-specific config is almost the same as default (test1XX),
* just the projections are selected by specific resource.
* MID-3460
*/
@Test
public void test300MergeJackGuybrushPreviewDeltaDefaultSpecific() throws Exception {
final String TEST_NAME = "test300MergeJackGuybrushPreviewDeltaDefaultSpecific";
TestUtil.displayTestTile(this, TEST_NAME);

Task task = taskManager.createTaskInstance(TestMerge.class.getName() + "." + TEST_NAME);
OperationResult result = task.getResult();

PrismObject<UserType> userJackBefore = getUser(USER_JACK_OID);
display("Jack before", userJackBefore);

PrismObject<UserType> userGuybrushBefore = getUser(USER_GUYBRUSH_OID);
display("Guybrush before", userGuybrushBefore);

// WHEN
TestUtil.displayWhen(TEST_NAME);
MergeDeltas<UserType> deltas =
modelInteractionService.mergeObjectsPreviewDeltas(UserType.class,
USER_JACK_OID, USER_GUYBRUSH_OID, MERGE_CONFIG_DEFAULT_SPECIFIC_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
result.computeStatus();
TestUtil.assertSuccess(result);

display("Deltas", deltas);

ObjectDelta<UserType> leftObjectdelta = deltas.getLeftObjectDelta();
PrismAsserts.assertIsModify(leftObjectdelta);
assertEquals("Wrong delta OID", USER_JACK_OID, leftObjectdelta.getOid());
PrismAsserts.assertNoItemDelta(leftObjectdelta, UserType.F_NAME);
PrismAsserts.assertNoItemDelta(leftObjectdelta, UserType.F_GIVEN_NAME);
PrismAsserts.assertPropertyReplace(leftObjectdelta, UserType.F_FAMILY_NAME);
PrismAsserts.assertPropertyReplace(leftObjectdelta, UserType.F_FULL_NAME,
createPolyString(USER_GUYBRUSH_FULL_NAME));
PrismAsserts.assertPropertyReplace(leftObjectdelta, UserType.F_ADDITIONAL_NAME);
PrismAsserts.assertPropertyReplace(leftObjectdelta, UserType.F_LOCALITY,
createPolyString(USER_GUYBRUSH_LOCALITY));
PrismAsserts.assertPropertyAdd(leftObjectdelta, UserType.F_EMPLOYEE_TYPE,
"SAILOR", "PIRATE WANNABE");
PrismAsserts.assertPropertyAdd(leftObjectdelta, UserType.F_ORGANIZATION,
createPolyString("Pirate Wannabes"), createPolyString("Lovers"));
PrismAsserts.assertNoItemDelta(leftObjectdelta, UserType.F_ACTIVATION);
PrismAsserts.assertNoItemDelta(leftObjectdelta,
new ItemPath(UserType.F_ACTIVATION, ActivationType.F_ADMINISTRATIVE_STATUS));
PrismAsserts.assertNoItemDelta(leftObjectdelta, UserType.F_ROLE_MEMBERSHIP_REF);

PrismAsserts.assertContainerAdd(leftObjectdelta, UserType.F_ASSIGNMENT,
FocusTypeUtil.createRoleAssignment(ROLE_THIEF_OID));

PrismAsserts.assertNoItemDelta(leftObjectdelta, UserType.F_LINK_REF);

ObjectDelta<UserType> leftLinkDelta = deltas.getLeftLinkDelta();
PrismAsserts.assertReferenceAdd(leftLinkDelta, UserType.F_LINK_REF, guybrushDummyAccountCyanOid);

ObjectDelta<UserType> rightLinkDelta = deltas.getRightLinkDelta();
PrismAsserts.assertReferenceDelete(rightLinkDelta, UserType.F_LINK_REF, guybrushDummyAccountCyanOid);

}

/**
* MID-3460
Expand Down
Expand Up @@ -226,6 +226,64 @@
<left>take</left>
<right>take</right>
</item>
<projection>
<left>take</left>
<situation>conflict</situation>
</projection>
<projection>
<left>take</left>
<right>take</right>
</projection>
<default>
<right>take</right>
</default>
</mergeConfiguration>

<mergeConfiguration>
<name>default-specific</name>
<description>
The default-specific config is almost the same as default, just the projections are selected
by specific resource.
</description>
<item>
<ref>name</ref>
<left>take</left>
</item>
<item>
<ref>givenName</ref>
<left>take</left>
<right>ignore</right>
</item>
<item>
<ref>familyName</ref>
</item>
<item>
<ref>fullName</ref>
<right>take</right>
</item>
<item>
<ref>employeeType</ref>
<left>take</left>
<right>take</right>
</item>
<item>
<ref>organization</ref>
<left>take</left>
<right>take</right>
</item>
<item>
<ref>organizationalUnit</ref>
<left>take</left>
</item>
<item>
<ref>activation</ref>
<left>take</left>
</item>
<item>
<ref>assignment</ref>
<left>take</left>
<right>take</right>
</item>
<projection>
<left>take</left>
<projectionDiscriminator>
Expand Down

0 comments on commit 0904ce8

Please sign in to comment.