Skip to content

Commit

Permalink
Support for multiple merge configurations (MID-3460)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Oct 21, 2016
1 parent 52e391e commit cbb33c6
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 31 deletions.
Expand Up @@ -9361,12 +9361,10 @@
</xsd:annotation>
</xsd:element>

<xsd:element name="mergeConfiguration" type="tns:MergeConfigurationType" minOccurs="0" maxOccurs="1">
<xsd:element name="mergeConfiguration" type="tns:MergeConfigurationType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Configuration for object merging. E.g. for merging two users.
Note: this is a single-valued item now. But it will most likely be
switched to multi-valued item in future midPoint versions.
Configurations for object merging. E.g. for merging two users.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.5</a:since>
Expand Down Expand Up @@ -12336,17 +12334,51 @@
<xsd:complexType name="MergeConfigurationType">
<xsd:annotation>
<xsd:documentation>
TODO
Configuration that specifies automatic merging of two objects.
</xsd:documentation>
<xsd:appinfo>
<a:container/>
</xsd:appinfo>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="name" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="description" type="xsd:string" minOccurs="0" maxOccurs="1"/>
<xsd:element name="item" type="tns:ItemRefMergeConfigurationType" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="default" type="tns:ItemMergeConfigurationType" minOccurs="0" maxOccurs="1"/>
<xsd:element name="name" type="xsd:string" minOccurs="1" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Short name of the merge cofiguration that also works as identifier for this configuration.
It has to be unique among all the applicable merge configurations.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="displayName" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Free form-name that can be displayed in user interfaces.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="description" type="xsd:string" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Free form description that can be displayed in user interfaces. It may be
longer text (more than few lines).
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="item" type="tns:ItemRefMergeConfigurationType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
Item merge configuration. It will be applied to the specified (named) item.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="default" type="tns:ItemMergeConfigurationType" minOccurs="0" maxOccurs="1">
<xsd:annotation>
<xsd:documentation>
Default merge configuration. It will be applied to all the items that are not
explicitly specified in the item merge configurations.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

Expand Down
Expand Up @@ -180,11 +180,11 @@ ConnectorOperationalStatus getConnectorOperationalStatus(String resourceOid, Ope
throws SchemaException, ObjectNotFoundException, CommunicationException, ConfigurationException;

<O extends ObjectType> ObjectDelta<O> mergeObjectsPreviewDelta(Class<O> type,
String leftOid, String rightOid, Task task, OperationResult result)
String leftOid, String rightOid, String mergeConfigurationName, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException;

<O extends ObjectType> PrismObject<O> mergeObjectsPreviewObject(Class<O> type,
String leftOid, String rightOid, Task task, OperationResult result)
String leftOid, String rightOid, String mergeConfigurationName, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException;

}
Expand Up @@ -664,10 +664,13 @@ <O extends ObjectType> CompareResultType compareObject(PrismObject<O> object,
* @param type object type
* @param leftOid left-side object OID
* @param rightOid right-side object OID
* @param mergeConfigurationName name of the merge configuration to use
* @param task
* @param result
* @return
*/
<O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type, String leftOid, String rightOid, Task task, OperationResult result) throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException;
<O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type, String leftOid, String rightOid,
String mergeConfigurationName, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException;

}
Expand Up @@ -2076,8 +2076,9 @@ public AccessCertificationCampaignType createCampaign(String definitionOid, Task
//endregion

@Override
public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type, String leftOid, String rightOid, Task task,
OperationResult parentResult) throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException {
public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type,
String leftOid, String rightOid, String mergeConfigurationName, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException, ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException, PolicyViolationException, SecurityViolationException {

OperationResult result = parentResult.createSubresult(MERGE_OBJECTS);
result.addParam("leftOid", leftOid);
Expand All @@ -2089,7 +2090,7 @@ public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectTy
try {

Collection<ObjectDeltaOperation<? extends ObjectType>> deltas =
objectMerger.mergeObjects(type, leftOid, rightOid, task, result);
objectMerger.mergeObjects(type, leftOid, rightOid, mergeConfigurationName, task, result);

result.computeStatus();
return deltas;
Expand Down
Expand Up @@ -696,13 +696,13 @@ public ConnectorOperationalStatus getConnectorOperationalStatus(String resourceO

@Override
public <O extends ObjectType> ObjectDelta<O> mergeObjectsPreviewDelta(Class<O> type, String leftOid,
String rightOid, Task task, OperationResult parentResult)
String rightOid, String mergeConfigurationName, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
OperationResult result = parentResult.createMinorSubresult(MERGE_OBJECTS_PREVIEW_DELTA);

try {

ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, task, result);
ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, mergeConfigurationName, task, result);

result.computeStatus();
return objectDelta;
Expand All @@ -715,13 +715,13 @@ public <O extends ObjectType> ObjectDelta<O> mergeObjectsPreviewDelta(Class<O> t

@Override
public <O extends ObjectType> PrismObject<O> mergeObjectsPreviewObject(Class<O> type, String leftOid,
String rightOid, Task task, OperationResult parentResult)
String rightOid, String mergeConfigurationName, Task task, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException, ConfigurationException {
OperationResult result = parentResult.createMinorSubresult(MERGE_OBJECTS_PREVIEW_OBJECT);

try {

ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, task, result);
ObjectDelta<O> objectDelta = objectMerger.computeMergeDelta(type, leftOid, rightOid, mergeConfigurationName, task, result);

final PrismObject<O> objectLeft = objectResolver.getObjectSimple(type, leftOid, null, task, result).asPrismObject();

Expand Down
Expand Up @@ -19,6 +19,7 @@
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

Expand All @@ -31,7 +32,6 @@
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismObjectDefinition;
import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.Visitable;
import com.evolveum.midpoint.prism.Visitor;
Expand Down Expand Up @@ -88,11 +88,11 @@ public class ObjectMerger {
private ModelController modelController;

public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectType>> mergeObjects(Class<O> type,
String leftOid, String rightOid, Task task, OperationResult result)
String leftOid, String rightOid, String mergeConfigurationName, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException,
ObjectAlreadyExistsException, ExpressionEvaluationException, CommunicationException,
PolicyViolationException, SecurityViolationException {
ObjectDelta<O> objectDelta = computeMergeDelta(type, leftOid, rightOid, task, result);
ObjectDelta<O> objectDelta = computeMergeDelta(type, leftOid, rightOid, mergeConfigurationName, task, result);

if (objectDelta != null && !objectDelta.isEmpty()) {
Collection<ObjectDeltaOperation<? extends ObjectType>> executedDeltas =
Expand All @@ -115,14 +115,14 @@ public <O extends ObjectType> Collection<ObjectDeltaOperation<? extends ObjectTy
}

public <O extends ObjectType> ObjectDelta<O> computeMergeDelta(Class<O> type, String leftOid, String rightOid,
Task task, OperationResult result)
String mergeConfigurationName, Task task, OperationResult result)
throws ObjectNotFoundException, SchemaException, ConfigurationException {

final PrismObject<O> objectLeft = objectResolver.getObjectSimple(type, leftOid, null, task, result).asPrismObject();
final PrismObject<O> objectRight = objectResolver.getObjectSimple(type, rightOid, null, task, result).asPrismObject();

PrismObject<SystemConfigurationType> systemConfiguration = systemObjectCache.getSystemConfiguration(result);
MergeConfigurationType mergeConfiguration = systemConfiguration.asObjectable().getMergeConfiguration();
MergeConfigurationType mergeConfiguration = selectConfiguration(systemConfiguration, mergeConfigurationName);
if (mergeConfiguration == null) {
throw new ConfigurationException("No merge configuration defined");
}
Expand Down Expand Up @@ -294,4 +294,17 @@ private <O extends ObjectType, I extends Item> ItemDelta mergeItem(PrismObject<O
}
}

private MergeConfigurationType selectConfiguration(
PrismObject<SystemConfigurationType> systemConfiguration, String mergeConfigurationName) throws ConfigurationException {
if (StringUtils.isBlank(mergeConfigurationName)) {
throw new IllegalArgumentException("Merge configuration name not specified");
}
for (MergeConfigurationType mergeConfiguration: systemConfiguration.asObjectable().getMergeConfiguration()) {
if (mergeConfigurationName.equals(mergeConfiguration.getName())) {
return mergeConfiguration;
}
}
throw new ConfigurationException("Merge configuration with name '"+mergeConfigurationName+"' was not found");
}

}
Expand Up @@ -29,13 +29,11 @@
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.prism.util.PrismTestUtil;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.FocusTypeUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ActivationType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;

/**
Expand All @@ -47,6 +45,8 @@
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";

@Override
public void initSystem(Task initTask, OperationResult initResult) throws Exception {
Expand Down Expand Up @@ -85,7 +85,8 @@ public void test100MergeJackGuybrushPreviewDelta() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);
ObjectDelta<UserType> delta =
modelInteractionService.mergeObjectsPreviewDelta(UserType.class, USER_JACK_OID, USER_GUYBRUSH_OID, task, result);
modelInteractionService.mergeObjectsPreviewDelta(UserType.class,
USER_JACK_OID, USER_GUYBRUSH_OID, MERGE_CONFIG_DEFAULT_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand Down Expand Up @@ -129,7 +130,8 @@ public void test102MergeJackGuybrushPreviewObject() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);
PrismObject<UserType> object =
modelInteractionService.mergeObjectsPreviewObject(UserType.class, USER_JACK_OID, USER_GUYBRUSH_OID, task, result);
modelInteractionService.mergeObjectsPreviewObject(UserType.class,
USER_JACK_OID, USER_GUYBRUSH_OID, MERGE_CONFIG_DEFAULT_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand Down Expand Up @@ -176,7 +178,8 @@ public void test110MergeGuybrushJackPreviewDelta() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);
ObjectDelta<UserType> delta =
modelInteractionService.mergeObjectsPreviewDelta(UserType.class, USER_GUYBRUSH_OID, USER_JACK_OID, task, result);
modelInteractionService.mergeObjectsPreviewDelta(UserType.class,
USER_GUYBRUSH_OID, USER_JACK_OID, MERGE_CONFIG_DEFAULT_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand Down Expand Up @@ -223,7 +226,8 @@ public void test112MergeGuybrushJackPreviewObject() throws Exception {
// WHEN
TestUtil.displayWhen(TEST_NAME);
PrismObject<UserType> object =
modelInteractionService.mergeObjectsPreviewObject(UserType.class, USER_GUYBRUSH_OID, USER_JACK_OID, task, result);
modelInteractionService.mergeObjectsPreviewObject(UserType.class,
USER_GUYBRUSH_OID, USER_JACK_OID, MERGE_CONFIG_DEFAULT_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand Down Expand Up @@ -265,7 +269,8 @@ public void test200MergeJackGuybrush() throws Exception {

// WHEN
TestUtil.displayWhen(TEST_NAME);
modelService.mergeObjects(UserType.class, USER_JACK_OID, USER_GUYBRUSH_OID, task, result);
modelService.mergeObjects(UserType.class,
USER_JACK_OID, USER_GUYBRUSH_OID, MERGE_CONFIG_DEFAULT_NAME, task, result);

// THEN
TestUtil.displayThen(TEST_NAME);
Expand Down
Expand Up @@ -184,6 +184,7 @@
</objectForms>
<defaultTimezone>Jamaica</defaultTimezone>
</adminGuiConfiguration>

<mergeConfiguration>
<name>default</name>
<item>
Expand Down Expand Up @@ -233,4 +234,22 @@
<right>take</right>
</default>
</mergeConfiguration>

<mergeConfiguration>
<name>empty</name>
</mergeConfiguration>

<mergeConfiguration>
<name>allRight</name>
<default>
<right>take</right>
</default>
</mergeConfiguration>

<mergeConfiguration>
<name>allLeft</name>
<default>
<left>take</left>
</default>
</mergeConfiguration>
</systemConfiguration>

0 comments on commit cbb33c6

Please sign in to comment.