Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CAM-9676] feat(engine): Implemented OR queries for ProcessInstanceQuery #320

Merged
merged 1 commit into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import javax.ws.rs.core.Response.Status;

import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.impl.ProcessInstanceQueryImpl;
import org.camunda.bpm.engine.rest.dto.AbstractQueryDto;
import org.camunda.bpm.engine.rest.dto.CamundaQueryParam;
import org.camunda.bpm.engine.rest.dto.VariableQueryParameterDto;
Expand Down Expand Up @@ -86,6 +87,8 @@ public class ProcessInstanceQueryDto extends AbstractQueryDto<ProcessInstanceQue

private List<VariableQueryParameterDto> variables;

private List<ProcessInstanceQueryDto> orQueries;

public ProcessInstanceQueryDto() {

}
Expand All @@ -94,6 +97,11 @@ public ProcessInstanceQueryDto(ObjectMapper objectMapper, MultivaluedMap<String,
super(objectMapper, queryParameters);
}

@CamundaQueryParam("orQueries")
public void setOrQueries(List<ProcessInstanceQueryDto> orQueries) {
this.orQueries = orQueries;
}

public Set<String> getProcessInstanceIds() {
return processInstanceIds;
}
Expand Down Expand Up @@ -348,9 +356,20 @@ protected ProcessInstanceQuery createNewQuery(ProcessEngine engine) {
return engine.getRuntimeService().createProcessInstanceQuery();
}

public List<ProcessInstanceQueryDto> getOrQueries() {
return orQueries;
}

@Override
protected void applyFilters(ProcessInstanceQuery query) {

if (orQueries != null) {
for (ProcessInstanceQueryDto orQueryDto: orQueries) {
ProcessInstanceQueryImpl orQuery = new ProcessInstanceQueryImpl();
orQuery.setOrQueryActive();
orQueryDto.applyFilters(orQuery);
((ProcessInstanceQueryImpl) query).addOrQuery(orQuery);
}
}
if (processInstanceIds != null) {
query.processInstanceIds(processInstanceIds);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -45,7 +46,10 @@
import javax.ws.rs.core.Response.Status;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.ProcessInstanceQueryImpl;
import org.camunda.bpm.engine.impl.calendar.DateTimeUtil;
import org.camunda.bpm.engine.rest.dto.runtime.ProcessInstanceDto;
import org.camunda.bpm.engine.rest.dto.runtime.ProcessInstanceQueryDto;
import org.camunda.bpm.engine.rest.exception.InvalidRequestException;
import org.camunda.bpm.engine.rest.helper.MockProvider;
import org.camunda.bpm.engine.rest.helper.variable.EqualsPrimitiveValue;
Expand All @@ -57,6 +61,7 @@
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;

Expand All @@ -79,7 +84,7 @@ public void setUpRuntimeData() {
}

private ProcessInstanceQuery setUpMockInstanceQuery(List<ProcessInstance> mockedInstances) {
ProcessInstanceQuery sampleInstanceQuery = mock(ProcessInstanceQuery.class);
ProcessInstanceQuery sampleInstanceQuery = mock(ProcessInstanceQueryImpl.class);
when(sampleInstanceQuery.list()).thenReturn(mockedInstances);
when(sampleInstanceQuery.count()).thenReturn((long) mockedInstances.size());
when(processEngine.getRuntimeService().createProcessInstanceQuery()).thenReturn(sampleInstanceQuery);
Expand Down Expand Up @@ -1063,4 +1068,34 @@ public void testProcessDefinitionKeyNotInPostParameter() {
verify(mockedQuery).processDefinitionKeyNotIn(MockProvider.EXAMPLE_PROCESS_DEFINITION_KEY, MockProvider.ANOTHER_EXAMPLE_PROCESS_DEFINITION_KEY);
verify(mockedQuery).list();
}

@Test
public void testOrQuery() {
// given
ProcessInstanceQueryDto processInstanceQueryDto = new ProcessInstanceQueryDto();

ProcessInstanceQueryDto orQuery = new ProcessInstanceQueryDto();
orQuery.setProcessDefinitionId(MockProvider.EXAMPLE_PROCESS_DEFINITION_ID);
orQuery.setBusinessKey(MockProvider.EXAMPLE_PROCESS_INSTANCE_BUSINESS_KEY);

processInstanceQueryDto.setOrQueries(Collections.singletonList(orQuery));

// when
given()
.contentType(POST_JSON_CONTENT_TYPE)
.header(ACCEPT_JSON_HEADER)
.body(processInstanceQueryDto)
.then().expect()
.statusCode(Status.OK.getStatusCode())
.when()
.post(PROCESS_INSTANCE_QUERY_URL);

ArgumentCaptor<ProcessInstanceQueryImpl> argument = ArgumentCaptor.forClass(ProcessInstanceQueryImpl.class);
verify(((ProcessInstanceQueryImpl) mockedQuery)).addOrQuery(argument.capture());

// then
assertThat(argument.getValue().getProcessDefinitionId()).isEqualTo(MockProvider.EXAMPLE_PROCESS_DEFINITION_ID);
assertThat(argument.getValue().getBusinessKey()).isEqualTo(MockProvider.EXAMPLE_PROCESS_INSTANCE_BUSINESS_KEY);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@
import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.persistence.entity.SuspensionState;
import org.camunda.bpm.engine.impl.variable.serializer.VariableSerializers;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.runtime.ProcessInstanceQuery;

Expand Down Expand Up @@ -69,6 +73,10 @@ public class ProcessInstanceQueryImpl extends AbstractVariableQueryImpl<ProcessI
protected String[] tenantIds;
protected boolean isProcessDefinitionWithoutTenantId = false;

// or query /////////////////////////////
protected List<ProcessInstanceQueryImpl> queries = new ArrayList<>(Arrays.asList(this));
protected boolean isOrQueryActive = false;

public ProcessInstanceQueryImpl() {
}

Expand Down Expand Up @@ -168,28 +176,48 @@ public ProcessInstanceQuery subCaseInstanceId(String subCaseInstanceId) {
}

public ProcessInstanceQuery orderByProcessInstanceId() {
if (isOrQueryActive) {
throw new ProcessEngineException("Invalid query usage: cannot set orderByProcessInstanceId() within 'or' query");
}

orderBy(ProcessInstanceQueryProperty.PROCESS_INSTANCE_ID);
return this;
}

public ProcessInstanceQuery orderByProcessDefinitionId() {
if (isOrQueryActive) {
throw new ProcessEngineException("Invalid query usage: cannot set orderByProcessDefinitionId() within 'or' query");
}

orderBy(new QueryOrderingProperty(QueryOrderingProperty.RELATION_PROCESS_DEFINITION,
ProcessInstanceQueryProperty.PROCESS_DEFINITION_ID));
return this;
}

public ProcessInstanceQuery orderByProcessDefinitionKey() {
if (isOrQueryActive) {
throw new ProcessEngineException("Invalid query usage: cannot set orderByProcessDefinitionKey() within 'or' query");
}

orderBy(new QueryOrderingProperty(QueryOrderingProperty.RELATION_PROCESS_DEFINITION,
ProcessInstanceQueryProperty.PROCESS_DEFINITION_KEY));
return this;
}

public ProcessInstanceQuery orderByTenantId() {
if (isOrQueryActive) {
throw new ProcessEngineException("Invalid query usage: cannot set orderByTenantId() within 'or' query");
}

orderBy(ProcessInstanceQueryProperty.TENANT_ID);
return this;
}

public ProcessInstanceQuery orderByBusinessKey() {
if (isOrQueryActive) {
throw new ProcessEngineException("Invalid query usage: cannot set orderByBusinessKey() within 'or' query");
}

orderBy(ProcessInstanceQueryProperty.BUSINESS_KEY);
return this;
}
Expand Down Expand Up @@ -275,10 +303,17 @@ public ProcessInstanceQuery processDefinitionWithoutTenantId() {

//results /////////////////////////////////////////////////////////////////

@Override
protected void checkQueryOk() {
ensureVariablesInitialized();

super.checkQueryOk();
}

@Override
public long executeCount(CommandContext commandContext) {
checkQueryOk();
ensureVariablesInitialized();

return commandContext
.getExecutionManager()
.findProcessInstanceCountByQueryCriteria(this);
Expand All @@ -287,20 +322,36 @@ public long executeCount(CommandContext commandContext) {
@Override
public List<ProcessInstance> executeList(CommandContext commandContext, Page page) {
checkQueryOk();
ensureVariablesInitialized();

return commandContext
.getExecutionManager()
.findProcessInstancesByQueryCriteria(this, page);
}

public List<String> executeIdsList(CommandContext commandContext) {
checkQueryOk();
ensureVariablesInitialized();

return commandContext
.getExecutionManager()
.findProcessInstancesIdsByQueryCriteria(this);
}

@Override
protected void ensureVariablesInitialized() {
super.ensureVariablesInitialized();

if (!queries.isEmpty()) {
VariableSerializers variableSerializers = Context.getProcessEngineConfiguration()
.getVariableSerializers();

for (ProcessInstanceQueryImpl orQuery: queries) {
for (QueryVariableValue var : orQuery.queryVariableValues) {
var.initialize(variableSerializers);
}
}
}
}

//getters /////////////////////////////////////////////////////////////////

public String getProcessInstanceId() {
Expand All @@ -311,6 +362,27 @@ public Set<String> getProcessInstanceIds() {
return processInstanceIds;
}

public List<ProcessInstanceQueryImpl> getQueries() {
return queries;
}

public void addOrQuery(ProcessInstanceQueryImpl orQuery) {
orQuery.isOrQueryActive = true;
this.queries.add(orQuery);
}

public void setOrQueryActive() {
isOrQueryActive = true;
}

public boolean isOrQueryActive() {
return isOrQueryActive;
}

public String[] getActivityIds() {
return activityIds;
}

public String getBusinessKey() {
return businessKey;
}
Expand Down Expand Up @@ -387,11 +459,41 @@ public String getSubCaseInstanceId() {
return subCaseInstanceId;
}

public boolean isTenantIdSet() {
return isTenantIdSet;
}

public boolean isRootProcessInstances() {
return isRootProcessInstances;
}

public boolean isProcessDefinitionWithoutTenantId() {
return isProcessDefinitionWithoutTenantId;
}

public boolean isLeafProcessInstances() {
return isLeafProcessInstances;
}

@Override
public ProcessInstanceQuery or() {
if (this != queries.get(0)) {
throw new ProcessEngineException("Invalid query usage: cannot set or() within 'or' query");
}

ProcessInstanceQueryImpl orQuery = new ProcessInstanceQueryImpl();
orQuery.isOrQueryActive = true;
orQuery.queries = queries;
queries.add(orQuery);
return orQuery;
}

@Override
public ProcessInstanceQuery endOr() {
if (!queries.isEmpty() && this != queries.get(queries.size()-1)) {
throw new ProcessEngineException("Invalid query usage: cannot set endOr() before or()");
}

return queries.get(0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Set;

import org.camunda.bpm.engine.ProcessEngineConfiguration;
import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.query.Query;

/**
Expand Down Expand Up @@ -262,4 +263,31 @@ public interface ProcessInstanceQuery extends Query<ProcessInstanceQuery, Proces

/** Order by the business key (needs to be followed by {@link #asc()} or {@link #desc()}). */
ProcessInstanceQuery orderByBusinessKey();

/**
* <p>After calling or(), a chain of several filter criteria could follow. Each filter criterion that follows or()
* will be linked together with an OR expression until the OR query is terminated. To terminate the OR query right
* after the last filter criterion was applied, {@link #endOr()} must be invoked.</p>
*
* @return an object of the type {@link ProcessInstanceQuery} on which an arbitrary amount of filter criteria could be applied.
* The several filter criteria will be linked together by an OR expression.
*
* @throws ProcessEngineException when or() has been invoked directly after or() or after or() and trailing filter
* criteria. To prevent throwing this exception, {@link #endOr()} must be invoked after a chain of filter criteria to
* mark the end of the OR query.
* */
ProcessInstanceQuery or();

/**
* <p>endOr() terminates an OR query on which an arbitrary amount of filter criteria were applied. To terminate the
* OR query which has been started by invoking {@link #or()}, endOr() must be invoked. Filter criteria which are
* applied after calling endOr() are linked together by an AND expression.</p>
*
* @return an object of the type {@link ProcessInstanceQuery} on which an arbitrary amount of filter criteria could be applied.
* The filter criteria will be linked together by an AND expression.
*
* @throws ProcessEngineException when endOr() has been invoked before {@link #or()} was invoked. To prevent throwing
* this exception, {@link #or()} must be invoked first.
* */
ProcessInstanceQuery endOr();
}