Skip to content

Commit

Permalink
ReferenceRestriction: added support for target filter in old-repo
Browse files Browse the repository at this point in the history
There are serious limitations for multi-value refs documented in tests;
these stem from HQL->SQL conversion, probably beyond our control.
  • Loading branch information
virgo47 committed Jun 15, 2022
1 parent 5c6a32f commit 216f1eb
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1399,11 +1399,54 @@ public void test941AssignmentsWithSpecifiedTargetUsingItem() throws SchemaExcept
.matches(a -> a.getTargetRef().getOid().equals("00000000-8888-6666-0000-100000000085"));
}

@Test(enabled = false) // TODO implement inner filter in ReferenceRestriction
public void test950AssignmentsWithSpecifiedTargetUsingRef() throws SchemaException {
@Test(enabled = false, description = "Broken where condition using a wrong joined table")
public void test942MultiValueRefFilterWithItem() throws SchemaException {
ObjectQuery query = prismContext.queryFor(UserType.class)
.item(UserType.F_PARENT_ORG_REF, T_OBJECT_REFERENCE, F_NAME).eq("F0085")
.build();
OperationResult result = new OperationResult("search");
display("QUERY: " + query);

when("executing container search");
queryListener.clear().start();
List<PrismObject<UserType>> users = repositoryService.searchObjects(UserType.class, query, null, result);
// WHERE name condition is constructed on m_acc_cert_campaign table (error in HQL->SQL)
queryListener.dumpAndStop();

then("only assignments with specified target object name are returned");
assertThatOperationResult(result).isSuccess();
// two refs match, no DISTINCT use, so it's doubled result
assertThat(users).extracting(u -> u.getName().getOrig())
.containsExactlyInAnyOrder("elaine123", "elaine123");
}

@Test
public void test943MultiValueRefTargetWithTargetTypeSpecificCondition() throws SchemaException {
ObjectQuery query = prismContext.queryFor(UserType.class)
// Example with multi-value linkRef from Query API docs, this works as expected:
.item(UserType.F_LINK_REF, T_OBJECT_REFERENCE, ShadowType.F_RESOURCE_REF).ref("ef2bc95b-76e0-48e2-86d6-3d4f02d3e1a2")
// While the shadow resource ref condition targets the m_shadow, the name condition would break again.
//.item(UserType.F_LINK_REF, T_OBJECT_REFERENCE, ShadowType.F_NAME).eq("7754e27c-a7cb-4c23-850d-a9a15f71199a")
.build();
OperationResult result = new OperationResult("search");
display("QUERY: " + query);

when("executing container search");
queryListener.clear().start();
List<PrismObject<UserType>> users = repositoryService.searchObjects(UserType.class, query, null, result);
queryListener.dumpAndStop();

then("only assignments with specified target object name are returned");
assertThatOperationResult(result).isSuccess();
assertThat(users).extracting(u -> u.getName().getOrig())
.containsExactlyInAnyOrder("atestuserX00003");
}

@Test
public void test950AssignmentsWithSpecifiedTargetUsingRefWithTargetFilter() throws SchemaException {
given("query for assignments with target object using new ref() construct");
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF, null, null)
.ref(AssignmentType.F_TARGET_REF)
.item(F_NAME).eq("F0085")
.asc(AssignmentType.F_TARGET_REF, T_OBJECT_REFERENCE, F_NAME)
.build();
Expand All @@ -1414,37 +1457,147 @@ public void test950AssignmentsWithSpecifiedTargetUsingRef() throws SchemaExcepti
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop();
result.recomputeStatus();
queryListener.dumpAndStop(); // order and target filter should share the same join

then("only assignments with specified target object name are returned");
assertThatOperationResult(result).isSuccess();
assertThat(assignments).singleElement()
.matches(a -> a.getTargetRef().getOid().equals("00000000-8888-6666-0000-100000000085"));
}

/*
select
a...
from
RAssignment a
left join a.targetRef.target t
where
a.targetRef is null
// TODO missing condition on t
order by t.name.orig asc
*/
@Test
public void test951AssignmentsWithSpecifiedTargetUsingRefWithWrongTargetFilter() throws SchemaException {
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF)
.item(F_NAME).eq("F0086") // bad name
.asc(AssignmentType.F_TARGET_REF, T_OBJECT_REFERENCE, F_NAME)
.build();

// TODO new .ref() filter with inner filter
OperationResult result = new OperationResult("search");
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop();

/* TODO optionally referencedBy, if necessary
searchObjectTest("Org by Assignment ownedBy user", RoleType.class,
f -> f.referencedBy(AssignmentType.class, AssignmentType.F_TARGET_REF)
.ownedBy(UserType.class)
.id(user3Oid),
roleOid);
assertThatOperationResult(result).isSuccess();
assertThat(assignments).isEmpty();
}

*/
@Test
public void test952AssignmentsWithTargetRefWithValueAndTargetFilter() throws SchemaException {
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF, null, SchemaConstants.ORG_DEFAULT, "00000000-8888-6666-0000-100000000085")
.item(F_NAME).eq("F0085")
.build();

OperationResult result = new OperationResult("search");
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop(); // value and filter should be combined using AND

assertThatOperationResult(result).isSuccess();
assertThat(assignments).singleElement()
.matches(a -> a.getTargetRef().getOid().equals("00000000-8888-6666-0000-100000000085"));
}

@Test
public void test953AssignmentsWithTargetRefWithMultipleValuesAndTargetFilter() throws SchemaException {
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF, null, SchemaConstants.ORG_DEFAULT,
"00000000-8888-6666-0000-100000000085",
"additional-oid-no-problem") // yeah, old repo doesn't care about OID format
.item(F_NAME).eq("F0085")
.build();

OperationResult result = new OperationResult("search");
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop(); // value and filter should be combined using AND

assertThatOperationResult(result).isSuccess();
assertThat(assignments).singleElement()
.matches(a -> a.getTargetRef().getOid().equals("00000000-8888-6666-0000-100000000085"));
}

@Test
public void test954AssignmentsWithTargetRefWrongRelationAndTargetFilter() throws SchemaException {
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF, null, SchemaConstants.ORG_DEPUTY)
.item(F_NAME).eq("F0085")
.build();

OperationResult result = new OperationResult("search");
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop();

assertThatOperationResult(result).isSuccess();
assertThat(assignments).isEmpty(); // no depute ref matches, even though the name is OK
}

@Test
public void test955AssignmentsWithTargetRefWithGoodValueAndWrongTargetFilter() throws SchemaException {
ObjectQuery query = prismContext.queryFor(AssignmentType.class)
.ref(AssignmentType.F_TARGET_REF, null, SchemaConstants.ORG_DEFAULT, "00000000-8888-6666-0000-100000000085")
.item(F_NAME).eq("F0086") // bad name, nothing will be found, even with good OID above
.asc(AssignmentType.F_TARGET_REF, T_OBJECT_REFERENCE, F_NAME)
.build();

OperationResult result = new OperationResult("search");
queryListener.clear().start();
SearchResultList<AssignmentType> assignments =
repositoryService.searchContainers(AssignmentType.class, query, null, result);
queryListener.dumpAndStop();

assertThatOperationResult(result).isSuccess();
assertThat(assignments).isEmpty();
}

@Test(enabled = false, description = "Broken where condition using a wrong joined table")
public void test960MultiValueRefFilterWithTargetFilter() throws SchemaException {
given("query for users with parent org with specified name (target filter)");
ObjectQuery query = prismContext.queryFor(UserType.class)
.ref(UserType.F_PARENT_ORG_REF)
.item(F_NAME).eq("F0085")
.build();
OperationResult result = new OperationResult("search");
display("QUERY: " + query);

when("executing container search");
queryListener.clear().start();
List<PrismObject<UserType>> users = repositoryService.searchObjects(UserType.class, query, null, result);
// WHERE name condition is constructed on m_acc_cert_campaign table (error in HQL->SQL)
queryListener.dumpAndStop();

then("only assignments with specified target object name are returned");
assertThatOperationResult(result).isSuccess();
// two refs match, no DISTINCT use, so it's doubled result
assertThat(users).extracting(u -> u.getName().getOrig())
.containsExactlyInAnyOrder("elaine123", "elaine123");
}

@Test(enabled = false, description = "Broken where condition using a wrong joined table")
public void test961RefFilterWithValueConditionsAndTargetFilterWrongName() throws SchemaException {
given("query for users with parent org with specified relation (ref condition) and name (target filter)");
ObjectQuery query = prismContext.queryFor(UserType.class)
.ref(UserType.F_PARENT_ORG_REF, null, SchemaConstants.ORG_MANAGER)
.item(F_NAME).eq("F0086")
.build();
OperationResult result = new OperationResult("search");
display("QUERY: " + query);

when("executing container search");
queryListener.clear().start();
List<PrismObject<UserType>> users = repositoryService.searchObjects(UserType.class, query, null, result);
queryListener.dumpAndStop();

then("no target matches the wrong name");
assertThatOperationResult(result).isSuccess();
assertThat(users).isEmpty();
}

/**
* See MID-5474 (just a quick attempt to replicate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/
package com.evolveum.midpoint.repo.sql.query.restriction;

import static com.evolveum.midpoint.prism.PrismConstants.T_OBJECT_REFERENCE;

import java.util.*;
import java.util.stream.Collectors;
import javax.xml.namespace.QName;
Expand All @@ -15,11 +17,13 @@

import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismReferenceValue;
import com.evolveum.midpoint.prism.query.ExistsFilter;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.RefFilter;
import com.evolveum.midpoint.repo.sql.data.common.RObjectReference;
import com.evolveum.midpoint.repo.sql.data.common.any.ROExtReference;
import com.evolveum.midpoint.repo.sql.query.InterpretationContext;
import com.evolveum.midpoint.repo.sql.query.QueryInterpreter;
import com.evolveum.midpoint.repo.sql.query.definition.JpaAnyReferenceDefinition;
import com.evolveum.midpoint.repo.sql.query.definition.JpaEntityDefinition;
import com.evolveum.midpoint.repo.sql.query.definition.JpaLinkDefinition;
Expand Down Expand Up @@ -60,15 +64,15 @@ public Condition interpretInternal() throws QueryException {

HibernateQuery hibernateQuery = context.getHibernateQuery();

ObjectFilter targetFilter = filter.getFilter();
List<PrismReferenceValue> values = filter.getValues();
if (CollectionUtils.isEmpty(values)) {
if (targetFilter == null) {
if (filter.getFilter() == null) { // "target filter"
return hibernateQuery.createIsNull(hqlDataInstance.getHqlPath());
} else {
// TODO here or lower after some join?
return targetFilterCondition();
}
}

Set<String> oids = new HashSet<>();
Set<QName> relations = new HashSet<>();
Set<QName> targetTypes = new HashSet<>();
Expand Down Expand Up @@ -102,10 +106,11 @@ public Condition interpretInternal() throws QueryException {
values.forEach(prv -> rootOr
.add(createRefCondition(hibernateQuery,
MiscUtil.singletonOrEmptySet(prv.getOid()), prv.getRelation(), prv.getTargetType())));
return rootOr;
return refCondition(rootOr);
} else {
return createRefCondition(hibernateQuery, oids,
MiscUtil.extractSingleton(relations), MiscUtil.extractSingleton(targetTypes));
return refCondition(
createRefCondition(hibernateQuery, oids,
MiscUtil.extractSingleton(relations), MiscUtil.extractSingleton(targetTypes)));
}
}

Expand Down Expand Up @@ -156,6 +161,7 @@ private Condition createRefCondition(HibernateQuery hibernateQuery,
// If the caller want to interpret it as "any", it has to cater for this itself.
//
// Return: empty list means "nothing to test".

@NotNull
static List<String> getRelationsToTest(QName relation, InterpretationContext context) {
if (QNameUtil.match(relation, PrismConstants.Q_ANY)) {
Expand All @@ -173,4 +179,24 @@ private Condition handleEqInOrNull(HibernateQuery hibernateQuery, String propert
return hibernateQuery.createEq(propertyName, value);
}
}

private Condition targetFilterCondition() throws QueryException {
ObjectFilter targetFilter = Objects.requireNonNull(filter.getFilter());

//noinspection deprecation
ExistsFilter existsFilter = context.getPrismContext().queryFactory().createExists(
filter.getFullPath().append(T_OBJECT_REFERENCE),
context.getType(), // source type (start of the path), not the target type
context.getPrismContext(),
targetFilter);

QueryInterpreter interpreter = context.getInterpreter();
return interpreter.interpretFilter(context, existsFilter, this);
}

private Condition refCondition(Condition condition) throws QueryException {
return filter.getFilter() != null
? context.getHibernateQuery().createAnd(condition, targetFilterCondition())
: condition;
}
}

0 comments on commit 216f1eb

Please sign in to comment.