Skip to content

Commit

Permalink
Improve internal manual correlation
Browse files Browse the repository at this point in the history
1. Introducing "composite" correlator that can invoke component
(child) correlators. They can have different levels of authority
(principal, authoritative, or non-authoritative). Their responses
are combined - according to those levels - and provided as an overall
correlation result.

Work in progress! Preliminary implementation.

2. Changed correlation case completion process. Now we complete the
correlation right at that time - recording timestamps, correlation
situation, and resulting owner. The next synchronization does not need
to invoke the correlator(s) anymore; it just takes this data and uses
it.

Unrelated changes:
- Slightly improved working with CsvResource (in tests).
  • Loading branch information
mederly committed Feb 26, 2022
1 parent f21878b commit 9ee46ed
Show file tree
Hide file tree
Showing 40 changed files with 1,945 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,15 @@ public static boolean isIndestructible(@NotNull PrismObject<? extends ObjectType
return isIndestructible(object.asObjectable());
}

// Currently ignoring reference definition (target type limitations)
public static Class<? extends ObjectType> getTargetClassFromReference(@NotNull ObjectReferenceType ref) {
if (ref.getType() != null) {
return ObjectTypes.getObjectTypeClass(ref.getType());
} else {
return ObjectType.class;
}
}

@FunctionalInterface
private interface ExtensionItemRemover {
// Removes item (known from the context) from the extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ public static void normalizeStages(ApprovalSchemaType schema) {
@NotNull
private static List<ApprovalStageDefinitionType> getSortedStages(ApprovalSchemaType schema) {
List<ApprovalStageDefinitionType> stages = new ArrayList<>(schema.getStage());
stages.sort(Comparator.comparing(stage -> getNumber(stage), Comparator.nullsLast(Comparator.naturalOrder())));
stages.sort(Comparator.comparing(ApprovalContextUtil::getNumber, Comparator.nullsLast(Comparator.naturalOrder())));
return stages;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.WorkItemDelegationMethodType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.stream.Collectors;

/**
* Utility methods related to case management. (Not directly tied to any of the particular prism objects or containers.)
Expand Down Expand Up @@ -49,4 +50,10 @@ public static void computeAssignees(
}
}
}

public static @NotNull List<CaseWorkItemType> getOpenWorkItems(@NotNull CaseType aCase) {
return aCase.getWorkItem().stream()
.filter(wi -> wi.getCloseTimestamp() == null)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@
package com.evolveum.midpoint.schema.util.cases;

import java.util.List;
import java.util.stream.Collectors;

import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.util.MiscUtil;

import com.evolveum.midpoint.util.exception.SchemaException;

import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectOwnerOptionType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceObjectOwnerOptionsType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

public class CorrelationCaseUtil {

Expand All @@ -34,7 +33,7 @@ public class CorrelationCaseUtil {
}

public static @NotNull List<ResourceObjectOwnerOptionType> getOwnerOptionsList(@NotNull CaseType aCase) {
var info = getOwnerOptions(aCase);
ResourceObjectOwnerOptionsType info = getOwnerOptions(aCase);
return info != null ? info.getOption() : List.of();
}

Expand All @@ -45,4 +44,25 @@ public class CorrelationCaseUtil {
.getOid(), () -> "No shadow OID in " + aCase);

}

public static AbstractWorkItemOutputType createDefaultOutput(String ownerOid) {
return new AbstractWorkItemOutputType(PrismContext.get())
.outcome(ownerOid != null ?
OwnerOptionIdentifier.forExistingOwner(ownerOid).getStringValue() :
OwnerOptionIdentifier.forNoOwner().getStringValue());
}

// Throws an exception if there's a problem.
public static @Nullable ObjectReferenceType getResultingOwnerRef(@NotNull CaseType aCase) throws SchemaException {
String outcomeUri = MiscUtil.requireNonNull(
aCase.getOutcome(), () -> "No outcome in " + aCase);
List<ResourceObjectOwnerOptionType> matchingOptions = getOwnerOptionsList(aCase).stream()
.filter(option -> outcomeUri.equals(option.getIdentifier()))
.collect(Collectors.toList());
ResourceObjectOwnerOptionType matchingOption = MiscUtil.extractSingletonRequired(matchingOptions,
() -> new SchemaException("Multiple matching options for outcome " + outcomeUri + ": "
+ matchingOptions + " in " + aCase),
() -> new SchemaException("No matching option for outcome " + outcomeUri + " in " + aCase));
return matchingOption.getCandidateOwnerRef();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23637,6 +23637,13 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="composite" type="tns:CompositeCorrelatorType" minOccurs="0" maxOccurs="unbounded">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:long"/>
</xsd:complexType>
Expand Down Expand Up @@ -23669,6 +23676,18 @@
</xsd:annotation>
</xsd:element>

<xsd:element name="authority" type="tns:CorrelatorAuthorityLevelType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:displayName>AbstractCorrelatorType.authority</a:displayName>
<a:displayOrder>15</a:displayOrder>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>

<xsd:element name="ref" type="xsd:string" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Expand Down Expand Up @@ -23726,6 +23745,56 @@
<xsd:attribute name="id" type="xsd:long"/>
</xsd:complexType>

<xsd:simpleType name="CorrelatorAuthorityLevelType">
<xsd:annotation>
<xsd:documentation>
TODO

Note to implementers: The ordering of values in this enum is of decreasing authoritativeness.
If two correlators have the same order value, they will be ordered according to the value of authority level.
</xsd:documentation>
</xsd:annotation>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="principal">
<xsd:annotation>
<xsd:documentation>
If the correlator provides an owner (must be 0 or 1), it is taken immediately as the result
of the correlation.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="PRINCIPAL"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="authoritative">
<xsd:annotation>
<xsd:documentation>
If the correlator provides an owner (must be 0 or 1), it is considered along with other
results from authoritative correlators. If - by chance - they return contradicting answers,
they are presented to the operator.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="AUTHORITATIVE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
<xsd:enumeration value="nonAuthoritative">
<xsd:annotation>
<xsd:documentation>
TODO

Note that the evaluation stops before the first non-authoritative correlator, if there is
an authoritative owner found at that time. So all authoritative/principal should be evaluated
before the first non-authoritative correlator.
</xsd:documentation>
<xsd:appinfo>
<jaxb:typesafeEnumMember name="NON_AUTHORITATIVE"/>
</xsd:appinfo>
</xsd:annotation>
</xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="NoOpCorrelatorType">
<xsd:annotation>
<xsd:documentation>
Expand Down Expand Up @@ -23962,6 +24031,33 @@
</xsd:complexType>
<xsd:element name="idMatchCorrelator" type="tns:IdMatchCorrelatorType"/>

<xsd:complexType name="CompositeCorrelatorType">
<xsd:annotation>
<xsd:documentation>
TODO
</xsd:documentation>
<xsd:appinfo>
<a:since>4.5</a:since>
<a:container>true</a:container>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
<xsd:complexContent>
<xsd:extension base="tns:AbstractCorrelatorType">
<xsd:sequence>
<xsd:element name="components" type="tns:CorrelatorsType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Children correlators.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="compositeCorrelator" type="tns:CompositeCorrelatorType"/>

<xsd:complexType name="CorrelationPropertiesDefinitionType">
<xsd:annotation>
<xsd:documentation>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.jetbrains.annotations.Nullable;

import java.io.Serializable;
import java.util.Objects;

import static com.evolveum.midpoint.xml.ns._public.common.common_3.CorrelationSituationType.*;

Expand Down Expand Up @@ -88,10 +89,18 @@ public static CorrelationResult error(@NotNull ErrorDetails details) {
return owner;
}

public @NotNull ObjectType getOwnerRequired() {
return Objects.requireNonNull(owner, "No existing owner");
}

public @Nullable ResourceObjectOwnerOptionsType getOwnerOptions() {
return ownerOptions;
}

public @NotNull ResourceObjectOwnerOptionsType getOwnerOptionsRequired() {
return Objects.requireNonNull(ownerOptions, "No owner options");
}

public boolean isUncertain() {
return situation == UNCERTAIN;
}
Expand All @@ -100,8 +109,16 @@ public boolean isError() {
return situation == ERROR;
}

public boolean isExistingOwner() {
return situation == EXISTING_OWNER;
}

public boolean isNoOwner() {
return situation == NO_OWNER;
}

public boolean isDone() {
return situation == NO_OWNER || situation == EXISTING_OWNER;
return isExistingOwner() || isNoOwner();
}

@Override
Expand All @@ -123,6 +140,16 @@ public String debugDump(int indent) {
return sb.toString();
}

@Override
public String toString() {
return "CorrelationResult{" +
"situation=" + situation +
", owner=" + owner +
", ownerOptions=" + ownerOptions +
", errorDetails=" + errorDetails +
'}';
}

/**
* Throws a {@link CommonException} or a {@link RuntimeException}, if the state is "error".
* Normally returns otherwise.
Expand All @@ -133,6 +160,10 @@ public void throwCommonOrRuntimeExceptionIfPresent() throws CommonException {
}
}

public @Nullable String getErrorMessage() {
return errorDetails != null ? errorDetails.message : null;
}

public enum Status {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public interface Correlator {
* @param correlationContext Additional information about the overall context for correlation (e.g. type of focal objects)
* @param result Operation result where the method should record its operation
*/
CorrelationResult correlate(
@NotNull CorrelationResult correlate(
@NotNull CorrelationContext correlationContext,
@NotNull OperationResult result)
throws SchemaException, ExpressionEvaluationException, CommunicationException, SecurityViolationException,
Expand All @@ -52,11 +52,19 @@ default void update(
/**
* Resolves a correlation case using provided work item output.
*
* This includes the processing that needs to be done in the correlator.
* For the majority of correlators, there's nothing to be done here.
*
* Correlators with external and/or internal state (like ID Match) can update that state here.
*
* @param outcomeUri It is the same value as in the case. It is mentioned explicitly just to show it's not null.
*/
void resolve(
default void resolve(
@NotNull CaseType aCase,
@NotNull String outcomeUri,
@NotNull Task task,
@NotNull OperationResult result) throws SchemaException, CommunicationException, SecurityViolationException;
@NotNull OperationResult result)
throws SchemaException, CommunicationException, SecurityViolationException, ObjectNotFoundException {
// Nothing to do by default.
}
}

0 comments on commit 9ee46ed

Please sign in to comment.