Skip to content

Commit

Permalink
Make lower-level tests pass
Browse files Browse the repository at this point in the history
This continues the work on normalizable shadow attributes represented
as PolyStrings. Major changes:

1. Strictly differentiating between checking shadows obtained from the
repository and via provisioning (in tests). The former may have
PolyString attributes, whereas the latter have the same schema as
on resource.

2. Differentiating between repo and non-repo shadow deltas
in provisioning-impl (see RepoShadowModifications class).

3. Fixed some recently-introduced and older bugs in provisioning-impl.

This makes tests up to model-impl pass, currently only with the native
repository. Work in progress.

Related to MID-2119 (shadow caching).

See also 2d85325d114f85824752dd8aec0a2a7ad09d762d (prism).
  • Loading branch information
mederly committed Dec 22, 2023
1 parent e43995b commit 4ac0632
Show file tree
Hide file tree
Showing 59 changed files with 949 additions and 616 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element minOccurs="0" name="internalId" type="xsd:string">
<xsd:element minOccurs="0" name="internalId" type="xsd:int">
<xsd:annotation>
<xsd:appinfo>
<a:displayOrder>180</a:displayOrder>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ public String runScript(String language, String scriptCode, Map<String, Object>
public void populateWithDefaultSchema() {
accountObjectClass.clear();
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_FULLNAME_NAME, String.class, true, false);
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_INTERNAL_ID, String.class, false, false);
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_INTERNAL_ID, Integer.class, false, false);
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_DESCRIPTION_NAME, String.class, false, false);
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_INTERESTS_NAME, String.class, false, true);
accountObjectClass.addAttributeDefinition(DummyAccount.ATTR_PRIVILEGES_NAME, String.class, false, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import com.evolveum.midpoint.schema.util.AbstractShadow;

import com.evolveum.midpoint.schema.util.SchemaDebugUtil;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

import org.testng.Assert;
import org.testng.annotations.BeforeSuite;
Expand All @@ -53,7 +52,6 @@
import com.evolveum.midpoint.test.util.TestUtil;
import com.evolveum.midpoint.tools.testng.AbstractUnitTest;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

Expand Down Expand Up @@ -601,7 +599,7 @@ public void test140ParseFromResourcePosix() throws Exception {
LayerType.SCHEMA, LayerType.PRESENTATION);

assertAttributeDef(rAccountAttrs, QNAME_DN,
PolyStringType.COMPLEX_TYPE, 1, 1, "Distinguished Name", 110,
DOMUtil.XSD_STRING, 1, 1, "Distinguished Name", 110,
true, false,
true, true, true, // Access: create, read, update
LayerType.SCHEMA, LayerType.PRESENTATION);
Expand All @@ -625,7 +623,7 @@ public void test140ParseFromResourcePosix() throws Exception {
LayerType.SCHEMA, LayerType.PRESENTATION);

assertAttributeDef(rAccountAttrs, QNAME_UID,
PolyStringType.COMPLEX_TYPE, 0, -1, "Login Name", 300,
DOMUtil.XSD_STRING, 0, -1, "Login Name", 300,
true, false,
true, true, true, // Access: create, read, update
LayerType.SCHEMA, LayerType.PRESENTATION);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2010-2023 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
*/

package com.evolveum.midpoint.schema.processor;

import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.path.ItemPathImpl;

import com.evolveum.midpoint.util.annotation.Experimental;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.List;
import java.util.Optional;

import static com.evolveum.midpoint.util.MiscUtil.argCheck;

/**
* Path pointing to a specific {@link ResourceAttribute}.
* It is always in the form of `attributes/<name>`.
*/
@Experimental
public class AttributePath implements ItemPath {

@NotNull private final ItemName attributeName;
@NotNull private final List<Object> segments;

private AttributePath(@NotNull ItemName attributeName) {
this.attributeName = attributeName;
this.segments = List.of(ShadowType.F_ATTRIBUTES, attributeName);
}

public static @NotNull AttributePath of(ItemPath path) {
if (path instanceof AttributePath attributePath) {
return attributePath;
}
argCheck(path.startsWith(ShadowType.F_ATTRIBUTES), "Path is not related to attributes: %s", path);
argCheck(path.size() == 2, "Supposed attribute-related path has wrong format: %s", path);
return new AttributePath(ItemPath.toName(path.getSegment(1)));
}

public static @NotNull Optional<AttributePath> optionalOf(ItemPath path) {
if (path instanceof AttributePath attributePath) {
return Optional.of(attributePath);
}
if (path.startsWith(ShadowType.F_ATTRIBUTES)) {
return Optional.of(of(path));
} else {
return Optional.empty();
}
}

@Override
public boolean isEmpty() {
return false;
}

@Override
public int size() {
return 2;
}

@Override
public @NotNull List<?> getSegments() {
return segments;
}

@Override
public @Nullable Object getSegment(int i) {
return segments.get(i);
}

@Override
public @Nullable Object first() {
return ShadowType.F_ATTRIBUTES;
}

@Override
public @NotNull ItemPath rest(int n) {
if (n == 0) {
return this;
} else if (n == 1) {
return attributeName;
} else {
return ItemPathImpl.EMPTY_PATH;
}
}

@Override
public ItemPath firstAsPath() {
return ShadowType.F_ATTRIBUTES;
}

@Override
public @NotNull Object last() {
return attributeName;
}

@Override
public @NotNull ItemPath allExceptLast() {
return ShadowType.F_ATTRIBUTES;
}

@Override
public ItemPath subPath(int from, int to) {
// Hopefully not used too often (as it's not optimized much)
return ItemPath.create(segments)
.subPath(from, to);
}

@Override
public ItemName lastName() {
return attributeName;
}

@Override
public @NotNull ItemPath namedSegmentsOnly() {
return this;
}

@Override
public @NotNull ItemPath removeIds() {
return this;
}

public @NotNull ItemName getAttributeName() {
return attributeName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@

import java.util.Collection;

import com.evolveum.midpoint.prism.delta.PropertyDelta;

import com.evolveum.midpoint.prism.util.CloneUtil;

import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.PrismContext;
Expand Down Expand Up @@ -155,6 +159,14 @@ void addNormalizedValues(@NotNull Collection<?> realValues, @NotNull ResourceAtt
.adoptRealValuesAndInstantiate(getRealValues());
}

/** Creates a delta that would enforce (via REPLACE operation) the values of this attribute. */
default @NotNull PropertyDelta<T> createReplaceDelta() {
var delta = getDefinitionRequired().createEmptyDelta();
delta.setValuesToReplace(
CloneUtil.cloneCollectionMembers(getValues()));
return delta;
}

/** TODO decide on this. */
default void checkDefinitionConsistence(@NotNull ResourceObjectDefinition objectDefinition) {
ResourceAttributeDefinition<Object> expectedDefinition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package com.evolveum.midpoint.schema.processor;

import java.io.Serial;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
Expand Down Expand Up @@ -38,7 +39,7 @@
*/
@SuppressWarnings("rawtypes")
public final class ResourceAttributeContainerImpl extends PrismContainerImpl<ShadowAttributesType> implements ResourceAttributeContainer {
private static final long serialVersionUID = 8878851067509560312L;
@Serial private static final long serialVersionUID = 8878851067509560312L;

/**
* The constructors should be used only occasionally (if used at all).
Expand Down Expand Up @@ -93,17 +94,17 @@ public ResourceAttribute<?> getPrimaryIdentifier() {

@Override
public @NotNull Collection<ResourceAttribute<?>> getPrimaryIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getPrimaryIdentifiers());
return extractAttributesByDefinitions(getDefinitionRequired().getPrimaryIdentifiers());
}

@Override
public @NotNull Collection<ResourceAttribute<?>> getSecondaryIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getSecondaryIdentifiers());
return extractAttributesByDefinitions(getDefinitionRequired().getSecondaryIdentifiers());
}

@Override
public @NotNull Collection<ResourceAttribute<?>> getAllIdentifiers() {
return extractAttributesByDefinitions(getDefinition().getAllIdentifiers());
return extractAttributesByDefinitions(getDefinitionRequired().getAllIdentifiers());
}

@Override
Expand Down Expand Up @@ -170,6 +171,7 @@ public <X> ResourceAttribute<X> findAttribute(QName attributeQName) {

@Override
public <X> ResourceAttribute<X> findAttribute(ResourceAttributeDefinition attributeDefinition) {
//noinspection unchecked
return (ResourceAttribute<X>) getValue().findProperty(attributeDefinition);
}

Expand Down Expand Up @@ -207,22 +209,19 @@ protected void copyValues(CloneStrategy strategy, ResourceAttributeContainerImpl


@Override
public void checkConsistenceInternal(Itemable rootItem, boolean requireDefinitions, boolean prohibitRaw,
ConsistencyCheckScope scope) {
public void checkConsistenceInternal(
Itemable rootItem, boolean requireDefinitions, boolean prohibitRaw, ConsistencyCheckScope scope) {
super.checkConsistenceInternal(rootItem, requireDefinitions, prohibitRaw, scope);
List<PrismContainerValue<ShadowAttributesType>> values = getValues();
if (values == null) {
throw new IllegalStateException("Null values in ResourceAttributeContainer");
}
if (values.isEmpty()) {
return;
}
if (values.size() > 1) {
throw new IllegalStateException(values.size()+" values in ResourceAttributeContainer, expected just one");
}
PrismContainerValue value = values.get(0);
PrismContainerValue<ShadowAttributesType> value = values.get(0);
Collection<Item<?,?>> items = value.getItems();
for (Item item: items) {
for (Item item : items) {
if (!(item instanceof ResourceAttribute)) {
throw new IllegalStateException("Found illegal item in ResourceAttributeContainer: "+item+" ("+item.getClass()+")");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import com.evolveum.midpoint.model.api.correlation.CorrelationCaseDescription.Match;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -196,7 +197,7 @@ private CorrelationPropertyValuesDescription createCandidateProperty(
Collection<PrismValue> preFocusValues = preFocus.asPrismContainerValue().getAllValues(itemPath);
IndexingItemConfiguration indexing = templateCorrelationConfiguration.getIndexingConfiguration().getForPath(itemPath);
QName defaultMatchingRule = templateCorrelationConfiguration.getDefaultMatchingRuleName(itemPath);
CorrelationCaseDescription.Match match =
Match match =
new MatchDetermination(
candidate, correlationProperty, preFocusValues, primaryValues, allValues, indexing, defaultMatchingRule)
.determine(task, result);
Expand Down Expand Up @@ -232,14 +233,19 @@ private CorrelationPropertyValuesDescription createCandidateProperty(
}
}

/** Determines the {@link Match} (e.g. full, partial, none) for given candidate, property, and situation. */
private class MatchDetermination {

@NotNull private final F candidate;
@NotNull private final CorrelationCaseDescription.CorrelationProperty correlationProperty;
@NotNull private final Collection<PrismValue> preFocusValues;
@NotNull private final Set<PrismValue> primaryValues;
@NotNull private final Set<PrismValue> allValues;

/** Indexing configuration (from object template) for given item. */
@Nullable private final IndexingItemConfiguration indexing;

/** Default matching rule defined in the object template for given item. */
@Nullable private final QName defaultMatchingRule;

private MatchDetermination(
Expand All @@ -259,7 +265,7 @@ private MatchDetermination(
this.defaultMatchingRule = defaultMatchingRule;
}

CorrelationCaseDescription.Match determine(Task task, OperationResult result)
Match determine(Task task, OperationResult result)
throws ConfigurationException, SchemaException, ExpressionEvaluationException, CommunicationException,
SecurityViolationException, ObjectNotFoundException {
LOGGER.trace("Determining match for {}, pre-focus: {}, primary: {}, all: {}, indexing: {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import com.evolveum.midpoint.prism.normalization.StringNormalizer;

import com.evolveum.midpoint.util.QNameUtil;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -179,6 +181,14 @@ private Collection<PrismValue> collectAllValues(@NotNull FocusType focus, @NotNu
}

public static ValueNormalizer getNormalizerFor(@Nullable QName matchingRuleName) throws SchemaException {
// Special cases - TODO decide about these! MID-2119
if (QNameUtil.match(matchingRuleName, PrismConstants.POLY_STRING_NORM_MATCHING_RULE_NAME)) {
return (input, task, result) ->
PrismContext.get().getDefaultPolyStringNormalizer().normalize(stringify(input));
} else if (QNameUtil.match(matchingRuleName, PrismConstants.POLY_STRING_ORIG_MATCHING_RULE_NAME)) {
return (input, task, result) -> stringify(input);
}

Normalizer<?> normalizer =
SchemaService.get().matchingRuleRegistry()
.getMatchingRule(matchingRuleName, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public void test300EvaluateAxiomAgainstShadowAttributes() throws Exception {
assertMessageContains(e.getMessage(), "attributes/drink");
}
var coordinates = "resourceRef matches (oid = '10000000-0000-0000-0000-000000000004') "
+ "and kind = 'account' and intent = 'default' )";
+ "and kind = 'account' and intent = 'default'";

var query = TypedQuery.parse(ShadowType.class, baseQuery + " and " + coordinates);
var result = modelService.searchObjects(query, getTestTask(), createOperationResult());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public void test020ModifyLootAbsolute() throws Exception {
setDebugListener();

DummyAccount dummyAccount = getDummyResource().getAccountByUsername(ACCOUNT_JACK_DUMMY_USERNAME);
dummyAccount.addAttributeValues(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME, "999");
dummyAccount.addAttributeValue(DummyResourceContoller.DUMMY_ACCOUNT_ATTRIBUTE_LOOT_NAME, 999);

ResourceObjectShadowChangeDescription change = new ResourceObjectShadowChangeDescription();
PrismObject<ShadowType> accountShadowJack = provisioningService.getObject(ShadowType.class, accountShadowJackDummyOid, null, task, result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,7 @@ public void test133SearchAccountShadows() throws Exception {
Task task = getTestTask();
OperationResult result = task.getResult();

ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(
RESOURCE_DUMMY_UPCASE_OID,
new QName(MidPointConstants.NS_RI, SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME));
ObjectQuery query = ObjectQueryUtil.createResourceAndObjectClassQuery(RESOURCE_DUMMY_UPCASE_OID, RI_ACCOUNT_OBJECT_CLASS);
rememberCounter(InternalCounters.SHADOW_FETCH_OPERATION_COUNT);

// WHEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void test100ImportFromResource() throws Exception {

// WHEN
when();
modelService.importFromResource(RESOURCE_DUMMY_AUTOGREEN_OID, new QName(MidPointConstants.NS_RI, SchemaConstants.ACCOUNT_OBJECT_CLASS_LOCAL_NAME), task, result);
modelService.importFromResource(RESOURCE_DUMMY_AUTOGREEN_OID, RI_ACCOUNT_OBJECT_CLASS, task, result);

// THEN
then();
Expand Down

0 comments on commit 4ac0632

Please sign in to comment.