Skip to content

Commit

Permalink
repo-sqale: implemented isAnySubordinate/Descendant/Ancestor + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
virgo47 committed Jul 23, 2021
1 parent 948c27b commit 9fef133
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -898,28 +898,71 @@ public <T extends Containerable> SearchResultList<T> searchContainers(
}
}

// This operation does not use parent OperationResult, so the exception handling is simpler.
@Override
public boolean isAnySubordinate(String upperOrgOid, Collection<String> lowerObjectOids)
throws SchemaException {
// TODO
// TODO is SqaleQueryContext.beforeQuery call included if necessary?
throw new UnsupportedOperationException();
public boolean isAnySubordinate(String ancestorOrgOid, Collection<String> descendantOrgOids) {
Validate.notNull(ancestorOrgOid, "upperOrgOid must not be null.");
Validate.notNull(descendantOrgOids, "lowerObjectOids must not be null.");

LOGGER.trace("Querying for subordination upper {}, lower {}",
ancestorOrgOid, descendantOrgOids);

if (descendantOrgOids.isEmpty()) {
// trivial case
return false;
}

long opHandle = registerOperationStart(OP_IS_ANY_SUBORDINATE, OrgType.class);
try {
return isAnySubordinateAttempt(UUID.fromString(ancestorOrgOid),
descendantOrgOids.stream()
.map(s -> UUID.fromString(s))
.collect(Collectors.toList()));
} catch (Exception e) {
throw new SystemException(
"isAnySubordinateAttempt failed somehow, this really should not happen.", e);
} finally {
registerOperationFinish(opHandle, 1); // TODO attempt
}
}

private boolean isAnySubordinateAttempt(UUID ancestorOrgOid, Collection<UUID> lowerObjectOids) {
try (JdbcSession jdbcSession = repositoryContext.newJdbcSession().startTransaction()) {
jdbcSession.executeStatement("CALL m_refresh_org_closure()");

QOrgClosure oc = new QOrgClosure();
long count = jdbcSession.newQuery()
.from(oc)
.where(oc.ancestorOid.eq(ancestorOrgOid)
.and(oc.descendantOid.in(lowerObjectOids)))
.fetchCount();

return count != 0L;
}
}

@Override
public <O extends ObjectType> boolean isDescendant(PrismObject<O> object, String orgOid)
public <O extends ObjectType> boolean isDescendant(
PrismObject<O> descendant, String ancestorOrgOid)
throws SchemaException {
// TODO
// TODO is SqaleQueryContext.beforeQuery call included if necessary?
throw new UnsupportedOperationException();
List<ObjectReferenceType> objParentOrgRefs = descendant.asObjectable().getParentOrgRef();
List<String> objParentOrgOids = new ArrayList<>(objParentOrgRefs.size());
for (ObjectReferenceType objParentOrgRef : objParentOrgRefs) {
objParentOrgOids.add(objParentOrgRef.getOid());
}
return isAnySubordinate(ancestorOrgOid, objParentOrgOids);
}

@Override
public <O extends ObjectType> boolean isAncestor(PrismObject<O> object, String oid)
public <O extends ObjectType> boolean isAncestor(
PrismObject<O> ancestorOrg, String descendantOid)
throws SchemaException {
// TODO
// TODO is SqaleQueryContext.beforeQuery call included if necessary?
throw new UnsupportedOperationException();
if (ancestorOrg.getOid() == null) {
return false;
}
Collection<String> oidList = new ArrayList<>(1);
oidList.add(descendantOid);
return isAnySubordinate(ancestorOrg.getOid(), oidList);
}

@Override
Expand Down Expand Up @@ -1052,7 +1095,7 @@ private long advanceSequence(SequenceType sequence, UUID oid) {
@Override
public void returnUnusedValuesToSequence(
String oid, Collection<Long> unusedValues, OperationResult parentResult)
throws ObjectNotFoundException, SchemaException {
throws ObjectNotFoundException {
UUID oidUuid = checkOid(oid);
Validate.notNull(parentResult, "Operation result must not be null.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,22 @@

import com.querydsl.sql.ColumnMetadata;

import com.evolveum.midpoint.repo.sqale.SqaleQueryContext;
import com.evolveum.midpoint.repo.sqale.SqaleRepositoryService;
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;
import com.evolveum.midpoint.repo.sqlbase.querydsl.UuidPath;

/**
* Querydsl query type for org closure table.
* Can also be used for common table expression (CTE) representing org hierarchy on the fly.
* This does not have to be under {@link FlexibleRelationalPathBase}, but is for convenience.
*
* [IMPORTANT]
* *Be aware that the materialized view is refreshed only on demand!*
* This is executed when {@link com.evolveum.midpoint.prism.query.OrgFilter} is used in
* {@link SqaleQueryContext#beforeQuery()} or when executing
* {@link SqaleRepositoryService#isAnySubordinate(java.lang.String, java.util.Collection)}.
* If any access via other paths is done, use statement `CALL m_refresh_org_closure()` before.
*/
@SuppressWarnings("unused")
public class QOrgClosure extends FlexibleRelationalPathBase<MOrgClosure> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

import static com.evolveum.midpoint.prism.PrismConstants.T_OBJECT_REFERENCE;
import static com.evolveum.midpoint.prism.PrismConstants.T_PARENT;
Expand All @@ -19,6 +21,7 @@
import java.math.BigInteger;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.function.Function;
import javax.xml.namespace.QName;
Expand Down Expand Up @@ -1674,14 +1677,97 @@ public void test950SearchOperationUpdatesPerformanceMonitor() throws SchemaExcep
assertSingleOperationRecorded(pm, RepositoryService.OP_SEARCH_OBJECTS);
}

@Test(enabled = false)
@Test
public void test960SearchByAxiomQueryLanguage() throws SchemaException {
OperationResult operationResult = createOperationResult();
SearchResultList<FocusType> focusTypes = searchObjects(FocusType.class,
". type UserType and employeeNumber startsWith \"5\"",

given("query for not-indexed extension");
SearchResultList<FocusType> result = searchObjects(FocusType.class,
". type UserType and costCenter startsWith \"5\"",
operationResult);
System.out.println("focusTypes = " + focusTypes);

expect("searchObjects throws exception because of not-indexed item");
assertThat(result)
.extracting(f -> f.getOid())
.containsExactlyInAnyOrder(user3Oid, user4Oid);
}

@Test
public void test970IsAncestor() throws Exception {
OperationResult operationResult = createOperationResult();

expect("isAncestor returns true for parent-child orgs");
PrismObject<OrgType> rootOrg =
repositoryService.getObject(OrgType.class, org1Oid, null, operationResult);
assertTrue(repositoryService.isAncestor(rootOrg, org11Oid));

expect("isAncestor returns true for parent-descendant (deep child) orgs");
assertTrue(repositoryService.isAncestor(rootOrg, org111Oid));

expect("isAncestor returns true for the same org");
assertTrue(repositoryService.isAncestor(rootOrg, org1Oid));

expect("isAncestor returns false for unrelated orgs");
assertFalse(repositoryService.isAncestor(rootOrg, org21Oid));

expect("isAncestor returns false for reverse relationship");
assertFalse(repositoryService.isAncestor(
repositoryService.getObject(OrgType.class, org11Oid, null, operationResult),
org1Oid));
}

@Test
public void test971IsDescendant() throws Exception {
OperationResult operationResult = createOperationResult();

expect("isDescendant returns true for child-parent orgs");
PrismObject<OrgType> org11 =
repositoryService.getObject(OrgType.class, org11Oid, null, operationResult);
assertTrue(repositoryService.isDescendant(org11, org1Oid));

expect("isDescendant returns true for org-grandparent orgs");
assertTrue(repositoryService.isDescendant(
repositoryService.getObject(OrgType.class, org112Oid, null, operationResult),
org1Oid));

// TODO this one is strange, as it is not symmetric with isAncestor.
// This works fine for non-orgs, but not for org itself - which by one look is ancestor
// of itself, but then by another one is NOT descendant of itself.
expect("isDescendant returns false for the same org");
assertFalse(repositoryService.isDescendant(org11, org11Oid));

expect("isDescendant returns false for unrelated orgs");
assertFalse(repositoryService.isDescendant(org11, org21Oid));

expect("isDescendant returns false for reverse relationship");
assertFalse(repositoryService.isDescendant(org11, org112Oid));
}

@Test
public void test972IsAnySubordinate() {
expect("isAnySubordinate returns true for parent-child orgs");
assertTrue(repositoryService.isAnySubordinate(org1Oid, List.of(org11Oid)));

expect("isAnySubordinate returns true for parent-descendant (deep child) orgs");
assertTrue(repositoryService.isAnySubordinate(org1Oid, List.of(org111Oid)));

expect("isAnySubordinate returns true for the same org");
assertTrue(repositoryService.isAnySubordinate(org1Oid, List.of(org1Oid)));

expect("isAnySubordinate returns true when the list contains only descendants");
assertTrue(repositoryService.isAnySubordinate(org1Oid,
List.of(org1Oid, org111Oid, org112Oid)));

expect("isAnySubordinate returns true when the list mixes descendants and non-descendants");
assertTrue(repositoryService.isAnySubordinate(org1Oid, List.of(org111Oid, org21Oid)));

expect("isAnySubordinate returns false when list contains only unrelated orgs");
assertFalse(repositoryService.isAnySubordinate(org1Oid, List.of(org21Oid)));

expect("isAnySubordinate returns false when list contains only ancestors");
assertFalse(repositoryService.isAnySubordinate(org11Oid, List.of(org1Oid)));
}

// endregion

// support methods
Expand Down

0 comments on commit 9fef133

Please sign in to comment.