ActionFilter.java

/*
 * Copyright 2020 Global Crop Diversity Trust
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gringlobal.service.filter;

import java.time.Instant;
import java.util.List;
import java.util.Set;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.collections.CollectionUtils;
import org.genesys.blocks.model.filters.TemporalFilter;
import org.gringlobal.api.exception.InvalidApiUsageException;
import org.gringlobal.model.AbstractAction;
import org.gringlobal.model.AbstractAction.ActionState;
import org.gringlobal.model.QAbstractAction;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.EntityPathBase;

/**
 * @author Maxym Borodenko
 */
@Getter
@Setter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true)
public abstract class ActionFilter<T extends ActionFilter<T, R>, R extends AbstractAction<R>> extends CooperatorOwnedModelFilter<T, R> {

	private static final long serialVersionUID = 3870340079764137534L;
	/** The action name code. */
	public Set<String> actionNameCode;
	/** Action "state" */
	public Set<ActionState> states;

	/** The action assignee. */
	public AclSidFilter assignee;

	/** Date filter on startedDate */
	public TemporalFilter<Instant> startedDate;
	/** Date filter on completedDate */
	public TemporalFilter<Instant> completedDate;
	/** Date filter on notBeforeDate */
	public TemporalFilter<Instant> notBeforeDate;

	protected List<Predicate> collectPredicates(final EntityPathBase<R> instance, final QAbstractAction action) {
		List<Predicate> predicates = super.collectPredicates(instance, action._super);

		if (CollectionUtils.isNotEmpty(actionNameCode)) {
			predicates.add(action.actionNameCode.in(actionNameCode));
		}

		if (CollectionUtils.isNotEmpty(states)) {
			if (states.size() < ActionState.values().length) {
				var now = Instant.now();
				BooleanBuilder statePredicates = new BooleanBuilder(); 
				states.forEach((state) -> {
					switch (state) {
					case COMPLETED:
						// startedDate is not null AND completedDate is not null
						statePredicates.or(action.startedDate.isNotNull().and(action.completedDate.isNotNull()));
						break;
					case PENDING:
						// startedDate is null AND (notBeforeDate is null or notBeforeDate <= now)
						statePredicates.or(action.startedDate.isNull().and(action.notBeforeDate.isNull().or(action.notBeforeDate.loe(now))));
						break;
					case INPROGRESS:
						// startedDate is not null AND completedDate is null
						statePredicates.or(action.startedDate.isNotNull().and(action.completedDate.isNull()));
						break;
					case SCHEDULED:
						// startedDate is null AND notBeforeDate > now
						statePredicates.or(action.startedDate.isNull().and(action.notBeforeDate.gt(now)));
						break;
					default:
						throw new InvalidApiUsageException("Unsupported action state " + state);
					}
				});
				predicates.add(statePredicates);
			}
		}

		if (startedDate != null) {
			predicates.add(startedDate.buildQuery(action.startedDate));
		}
		if (completedDate != null) {
			predicates.add(completedDate.buildQuery(action.completedDate));
		}
		if (notBeforeDate != null) {
			predicates.add(notBeforeDate.buildQuery(action.notBeforeDate));
		}

		if (assignee != null) {
			predicates.addAll(assignee.collectPredicates(action.assignee()));
		}

		return predicates;
	}
}