From ca5367ded4996eac109dc5a4bad14ab73a0a4d4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vanesa=20Smo=C4=BEakov=C3=A1?= Date: Fri, 15 May 2026 18:06:41 +0200 Subject: [PATCH] MID-9879 Add inline type support for collection filters --- .../xml/ns/public/common/common-gui-3.xsd | 14 +++ .../impl/controller/CollectionProcessor.java | 48 ++++++++++- .../intest/archetypes/TestCollections.java | 85 +++++++++++++++++++ release-notes.adoc | 1 + 4 files changed, 145 insertions(+), 3 deletions(-) diff --git a/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd b/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd index 1d717405b5b..6666faa7f90 100644 --- a/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd +++ b/infra/schema/src/main/resources/xml/ns/public/common/common-gui-3.xsd @@ -4091,6 +4091,20 @@ + + + + Type of objects that belong to this collection. This is used when the collection + is specified by an inline filter and the type cannot be derived from a referenced + collection or from the containing configuration. + + + 4.11 + CollectionSpecificationType.type + 5 + + + diff --git a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java index daaf042d6ea..19fd1121b65 100644 --- a/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java +++ b/model/model-impl/src/main/java/com/evolveum/midpoint/model/impl/controller/CollectionProcessor.java @@ -298,16 +298,19 @@ private void compileObjectCollectionView( } else if (collectionSpec.getFilter() != null) { SearchFilterType filter = collectionSpec.getFilter(); + Class effectiveTargetTypeClass = determineTargetTypeClass(collectionSpec, targetTypeClass); CollectionRefSpecificationType baseCollectionSpec = collectionSpec.getBaseCollectionRef(); if (baseCollectionSpec == null) { - if (targetTypeClass == null) { + if (effectiveTargetTypeClass == null) { throw new IllegalArgumentException("UndefinedTypeForCollection type: " + collectionSpec); } - ObjectFilter objectFilter = prismContext.getQueryConverter().parseFilter(filter, targetTypeClass); + ObjectFilter objectFilter = prismContext.getQueryConverter().parseFilter(filter, effectiveTargetTypeClass); existingView.setFilter(objectFilter); + setContainerTypeIfNeeded(existingView, collectionSpec.getType()); } else { - compileBaseCollectionSpec(filter, existingView, baseCollectionSpec, targetTypeClass, task, result); + compileBaseCollectionSpec(filter, existingView, baseCollectionSpec, effectiveTargetTypeClass, task, result); + setContainerTypeIfNeeded(existingView, collectionSpec.getType()); } } else { // E.g. the case of empty domain specification. Nothing to do. Just return what we have. @@ -316,6 +319,45 @@ private void compileObjectCollectionView( } } + /** + * Determines the type to be used for parsing an inline collection filter. + * + * The type can come either from the surrounding configuration, e.g. object + * list view or base collection, or from the collection specification itself. + * The explicit collection type is mainly needed for standalone inline filters, + * where there is no referenced object collection that would provide the type. + * + * If both types are present, the explicit collection type may only keep or + * narrow the surrounding type. It must not broaden it or point to an unrelated + * type, because the filter is evaluated in the context of the effective type. + */ + private Class determineTargetTypeClass( + CollectionRefSpecificationType collectionSpec, + Class targetTypeClass) throws ConfigurationException { + + QName explicitType = collectionSpec.getType(); + if (explicitType == null) { + return targetTypeClass; + } + + Class explicitTargetTypeClass = + prismContext.getSchemaRegistry().determineClassForType(explicitType); + + if (targetTypeClass != null && !targetTypeClass.isAssignableFrom(explicitTargetTypeClass)) { + throw new ConfigurationException( + "Conflicting collection types: " + targetTypeClass.getSimpleName() + + " and " + explicitType); + } + + return explicitTargetTypeClass; + } + + private void setContainerTypeIfNeeded(CompiledObjectCollectionView existingView, QName collectionType) { + if (existingView.getContainerType() == null && collectionType != null) { + existingView.setContainerType(collectionType); + } + } + private void compileArchetypeCollectionView( CompiledObjectCollectionView existingView, Class targetTypeClass, diff --git a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/archetypes/TestCollections.java b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/archetypes/TestCollections.java index ea7ac90c840..a7a6e729548 100644 --- a/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/archetypes/TestCollections.java +++ b/model/model-intest/src/test/java/com/evolveum/midpoint/model/intest/archetypes/TestCollections.java @@ -8,10 +8,12 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; import java.util.Collection; import java.util.stream.Collectors; +import com.evolveum.midpoint.prism.Containerable; import com.evolveum.midpoint.schema.util.MiscSchemaUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.*; @@ -24,10 +26,12 @@ import com.evolveum.midpoint.model.api.authentication.CompiledObjectCollectionView; import com.evolveum.midpoint.model.api.context.EvaluatedPolicyRule; import com.evolveum.midpoint.prism.PrismObject; +import com.evolveum.midpoint.prism.query.ObjectFilter; import com.evolveum.midpoint.schema.SearchResultList; import com.evolveum.midpoint.schema.constants.SchemaConstants; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.Task; +import com.evolveum.midpoint.util.exception.ConfigurationException; /** * Test behavior of connectors that have several instances (poolable connectors). @@ -116,6 +120,70 @@ public void test102SearchCollectionUsers() throws Exception { assertEquals("Wrong number of users in collection", getNumberOfUsers(), users.size()); } + /** + * MID-9879: Inline collection filter can be evaluated when the collection spec provides its object type. + * The explicit type is also allowed to narrow a broader containing target type. + */ + @Test + public void test103CompileInlineTypedCollectionView() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + ObjectFilter enabledUsersFilter = prismContext.queryFor(UserType.class) + .item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS).eq(ActivationStatusType.ENABLED) + .buildFilter(); + CollectionRefSpecificationType inlineTypedCollection = new CollectionRefSpecificationType() + .type(UserType.COMPLEX_TYPE) + .filter(prismContext.getQueryConverter().createSearchFilterType(enabledUsersFilter)); + + // WHEN + when(); + CompiledObjectCollectionView inlineTypedCollectionView = + modelInteractionService.compileObjectCollectionView(inlineTypedCollection, null, task, result); + CompiledObjectCollectionView inlineTypedCollectionViewFromFocus = + modelInteractionService.compileObjectCollectionView(inlineTypedCollection, FocusType.class, task, result); + CollectionStats stats = modelInteractionService.determineCollectionStats(inlineTypedCollectionView, task, result); + + // THEN + then(); + displayDumpable("Inline typed collection view", inlineTypedCollectionView); + assertSuccess(result); + + assertObjectCollectionView(inlineTypedCollectionView) + .assertFilter(); + assertEquals("Wrong container type", UserType.COMPLEX_TYPE, inlineTypedCollectionView.getContainerType()); + assertEquals("Wrong narrowed container type", UserType.COMPLEX_TYPE, inlineTypedCollectionViewFromFocus.getContainerType()); + assertEquals("Wrong target class", UserType.class, inlineTypedCollectionView.getTargetClass()); + assertEquals("Wrong narrowed target class", UserType.class, inlineTypedCollectionViewFromFocus.getTargetClass()); + assertEquals("Wrong object count", (Integer) getNumberOfUsers(), stats.getObjectCount()); + } + + /** + * MID-9879: Inline collection type must not conflict with the containing target type. + */ + @Test + public void test104RejectConflictingInlineCollectionType() throws Exception { + // GIVEN + Task task = getTestTask(); + OperationResult result = task.getResult(); + + ObjectFilter enabledUsersFilter = prismContext.queryFor(UserType.class) + .item(UserType.F_ACTIVATION, ActivationType.F_EFFECTIVE_STATUS).eq(ActivationStatusType.ENABLED) + .buildFilter(); + CollectionRefSpecificationType inlineTypedCollection = new CollectionRefSpecificationType() + .type(UserType.COMPLEX_TYPE) + .filter(prismContext.getQueryConverter().createSearchFilterType(enabledUsersFilter)); + + // WHEN + when(); + assertConflictingInlineCollectionType(inlineTypedCollection, RoleType.class, task, result); + + CollectionRefSpecificationType inlineBroaderTypedCollection = inlineTypedCollection.clone() + .type(FocusType.COMPLEX_TYPE); + assertConflictingInlineCollectionType(inlineBroaderTypedCollection, UserType.class, task, result); + } + @Test public void test110CollectionStatsAllEnabled() throws Exception { // GIVEN @@ -201,4 +269,21 @@ public void test122EvaluateRulesOneDisabled() throws Exception { .assertConstraintKind(PolicyConstraintKindType.COLLECTION_STATS); } + + private void assertConflictingInlineCollectionType( + CollectionRefSpecificationType inlineTypedCollection, Class targetTypeClass, + Task task, OperationResult result) + throws Exception { + + try { + modelInteractionService.compileObjectCollectionView(inlineTypedCollection, targetTypeClass, task, result); + } catch (ConfigurationException e) { + displayExpectedException(e); + assertTrue("Unexpected exception message: " + e.getMessage(), + e.getMessage().contains("Conflicting collection types")); + return; + } + throw new AssertionError("Expected conflicting inline collection type to be rejected for " + + targetTypeClass.getSimpleName()); + } } diff --git a/release-notes.adoc b/release-notes.adoc index c606d71c93d..e1eeb8d4a32 100644 --- a/release-notes.adoc +++ b/release-notes.adoc @@ -102,6 +102,7 @@ Overall, midPoint 4.10 opens up the world of identity management and governance * Delineation suggestions: filter parsing broken after recent GUI change. See bug:MID-11175[] * Fixed work item search by name causing repository mapping error. See bug:MID-8834[] * Fixed translation of archetype display labels in assignment picker and summary panel. See bug:MID-11177[] +* Fixed dashboard widget collections with inline filters by allowing explicit object type specification. See bug:MID-9879[] === Releases Of Other Components