Skip to content

Commit

Permalink
RepoAssignmentDataProvider: Handle added and deleted values
Browse files Browse the repository at this point in the history
Added values are not in repository - so repository search can not detect them,
we prepend them to search result list (see {@link #internalIterator(long, long)}.

Deleted values needs to be excluded from repository search - so correct counts
are returned for filters (see {@link #getQuery()}.
  • Loading branch information
tonydamage committed Jun 27, 2022
1 parent 5c448d3 commit 74bb28d
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,16 @@ protected List<PrismContainerValueWrapper<AssignmentType>> customPostSearch(List
}
if (QNameUtil.match(assignment.getTargetRef().getType(), OrgType.COMPLEX_TYPE)) {
PrismObject<OrgType> org = WebModelServiceUtils.loadObject(assignment.getTargetRef(), getPageBase(), task, task.getResult());
if (org != null) {
if (FocusTypeUtil.determineSubTypes(org).contains("access")) {
resultList.add(ass);
}
if (org != null && FocusTypeUtil.determineSubTypes(org).contains("access")) {
resultList.add(ass);
}
}

}

return resultList;
}

@Override
protected ObjectFilter getSubtypeFilter(){
return getPageBase().getPrismContext().queryFor(OrgType.class)
.block()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ private ConstructionAssociationPanel getConstructionAssociationPanel(String idPa
return constructionDetailsPanel;
}

@Override
protected List<ObjectTypes> getObjectTypesList() {
return Collections.singletonList(ObjectTypes.RESOURCE);
}
Expand All @@ -185,12 +186,6 @@ protected boolean isEntitlementAssignment() {
return true;
}


// FIXME: This is post-process query, which can not by run currently by any repository
// Probably we should do some post-filtering?
// assignment:
// (construction/association not exists and status = ADDED)
// OR construction/association/ref exists
@Override
protected List<PrismContainerValueWrapper<AssignmentType>> customPostSearch(List<PrismContainerValueWrapper<AssignmentType>> assignments) {
List<PrismContainerValueWrapper<AssignmentType>> filteredAssignments = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ public ContainerListDataProvider(Component component, @NotNull IModel<Search<C>>
public Iterator<? extends PrismContainerValueWrapper<C>> internalIterator(long first, long count) {
LOGGER.trace("begin::iterator() from {} count {}.", first, count);
getAvailableData().clear();
return doRepositoryIteration(first, count);
}

protected Iterator<? extends PrismContainerValueWrapper<C>> doRepositoryIteration(long first, long count) {
Task task = getPageBase().createSimpleTask(OPERATION_SEARCH_CONTAINERS);
OperationResult result = task.getResult();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,48 @@
*/
package com.evolveum.midpoint.web.component.util;

import com.evolveum.midpoint.gui.api.factory.wrapper.PrismContainerWrapperFactory;
import com.evolveum.midpoint.gui.api.factory.wrapper.WrapperContext;
import com.evolveum.midpoint.gui.api.prism.wrapper.PrismContainerValueWrapper;
import com.evolveum.midpoint.gui.api.util.WebComponentUtil;
import com.evolveum.midpoint.gui.api.util.WebModelServiceUtils;
import com.evolveum.midpoint.prism.Objectable;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.impl.query.builder.QueryBuilder;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectPaging;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.query.OwnedByFilter;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.web.component.prism.ValueStatus;
import com.evolveum.midpoint.web.page.error.PageError;
import com.evolveum.midpoint.gui.impl.component.search.Search;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentHolderType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.AssignmentType;

import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectReferenceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;

import org.apache.wicket.Component;
import org.apache.wicket.RestartResponseException;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.model.IModel;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class RepoAssignmentListProvider extends ContainerListDataProvider<AssignmentType> {

private static final long serialVersionUID = 1L;

private static final Trace LOGGER = TraceManager.getTrace(ContainerListDataProvider.class);
private static final String DOT_CLASS = ContainerListDataProvider.class.getName() + ".";
private static final String OPERATION_SEARCH_CONTAINERS = DOT_CLASS + "searchContainers";
private static final String OPERATION_COUNT_CONTAINERS = DOT_CLASS + "countContainers";


private final IModel<List<PrismContainerValueWrapper<AssignmentType>>> model;
private final String oid;
private final Class<? extends Objectable> objectType;
private final ItemPath path;

public RepoAssignmentListProvider(Component component, @NotNull IModel<Search<AssignmentType>> search, IModel<List<PrismContainerValueWrapper<AssignmentType>>> model,

private transient List<PrismContainerValueWrapper<AssignmentType>> newData;

public RepoAssignmentListProvider(Component component, @NotNull IModel<Search<AssignmentType>> search, IModel<List<PrismContainerValueWrapper<AssignmentType>>> model,
Class<? extends Objectable> objectType, String oid, ItemPath path) {
super(component, search);
this.model = model;
Expand All @@ -77,22 +66,100 @@ protected List<PrismContainerValueWrapper<AssignmentType>> postFilter(List<Prism
return assignmentList;
}

protected ObjectFilter postFilterIds() {
List<PrismContainerValueWrapper<AssignmentType>> data = model.getObject();
List<PrismContainerValueWrapper<AssignmentType>> filtered = postFilter(data);

if (data == filtered) {
// No filtering were done
return null;
}
var builder = QueryBuilder.queryFor(AssignmentType.class, getPrismContext());
if (filtered.isEmpty()) {
return builder.none().buildFilter();
}
List<Long> idList = filtered.stream()
.filter(i -> i.getRealValue() != null)
.map(i -> i.getRealValue().getId())
.filter(i -> i != null)
.collect(Collectors.toList());

long[] ids = new long[idList.size()];
int i = 0;
for (Long item : idList) {
ids[i] = item;
i++;
}
return builder.id(ids).buildFilter();
}


@Override
public Iterator<? extends PrismContainerValueWrapper<AssignmentType>> internalIterator(long first, long count) {
getAvailableData().clear();
initChangeLists();
// FIXME: Sort new data

var newData = this.newData;
// If current page is inside new data, we add them to result list
if (first < newData.size()) {
for (var wrapper : newData.subList((int) first, (int) Math.min(newData.size(), first + count))) {
getAvailableData().add(wrapper);
}
}
// If there are still less data, then
if (getAvailableData().size() < count) {
// We get offset for repo search
long repoFirst = Math.max(0, first - newData.size());
// We search and populate availableData from repository
// removed data are handled in #getQuery call.
doRepositoryIteration(repoFirst, count - getAvailableData().size());
}
return getAvailableData().iterator();
}


/**
* Added and deleted values needs to be treated specially.
* This method performs walk of model list and updates internal state,
* to have newData list and set of deleted ids.
*
* Added values are not in repository - so repository search can not detect them,
* we prepend them to search result list (see {@link #internalIterator(long, long)}.
*
*/
private void initChangeLists() {
newData = new ArrayList<>();
for (PrismContainerValueWrapper<AssignmentType> wrapper : model.getObject()) {
if (ValueStatus.ADDED.equals(wrapper.getStatus())) {
newData.add(wrapper);
}
}
}

@Override
protected int internalSize() {
initChangeLists();
return newData.size() + super.internalSize();
}

@Override
protected PrismContainerValueWrapper<AssignmentType> createWrapper(AssignmentType object, Task task,
OperationResult result) throws SchemaException {
for (PrismContainerValueWrapper<AssignmentType> item : model.getObject()) {
if (Objects.equals(item.getRealValue().getId(),object.getId())) {
if (ValueStatus.DELETED == item.getStatus()) {
return null;
}
postProcessWrapper(item);
return item;
}
}
// FIXME: Should not happen? Could possibly, if user was modified in other session
// or role has assignments and inducements and ownedBy filter is not supported
// Could possibly, if user was modified in other session.
// Lets silently skip the item for now (by returning null)
return null;
}

protected void postProcessWrapper(PrismContainerValueWrapper<AssignmentType> valueWrapper) {
AssignmentType assignmentType = valueWrapper.getRealValue();
if (assignmentType == null) {
Expand All @@ -106,15 +173,38 @@ protected void postProcessWrapper(PrismContainerValueWrapper<AssignmentType> val
PrismObject<? extends ObjectType> object = WebModelServiceUtils.loadObject(targetRef, getPageBase());
targetRef.asReferenceValue().setObject(object);
}

// We need to add oid selector to the query


/**
* Returns query for Data Provider
*
* This implementation rewrites query a bit:
* - Adds ownedBy filter for parent object
* - Optionally adds id filter if AssignmentPanel has postFilter implemented
*
*/
@Override
public ObjectQuery getQuery() {
var idFilter = postFilterIds();
var orig = super.getQuery();
ObjectFilter filter = orig != null ? orig.getFilter() : null;
if (orig != null) {
filter = orig.getFilter();
// We have user entered filter
if (idFilter != null) {
// PostFilter filtered data, so we need to search only in these data
filter = QueryBuilder.queryFor(getType(), getPrismContext())
.filter(orig.getFilter())
.and().filter(idFilter)
.buildFilter();
} else {
// postFilter did not filter data, so use only provided top filter
filter = orig.getFilter();
}
} else {
//
filter = idFilter;
}

if (filter != null) {
return QueryBuilder.queryFor(AssignmentType.class, getPrismContext())
.filter(filter)
Expand All @@ -124,7 +214,7 @@ public ObjectQuery getQuery() {
.build();
}
return QueryBuilder.queryFor(AssignmentType.class, getPrismContext())
.ownedBy(objectType, path)
.ownedBy(objectType, path)
.id(oid)
.build();
}
Expand Down

0 comments on commit 74bb28d

Please sign in to comment.