Skip to content

Commit

Permalink
feat(engine): HistoricProcessInstanceQuery processInstanceIdNotIn filter
Browse files Browse the repository at this point in the history
related to: #4896
  • Loading branch information
venetrius committed Jan 16, 2025
1 parent 93319af commit d3ade5a
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
"desc": "Filter by process instance ids. ${listTypeDescription}."
},

"processInstanceIdNotIn": {
"type": "array",
"itemType": "string",
"desc": "Exclude instances by process instance ids. ${listTypeDescription}."
},

"processDefinitionId": {
"type": "string",
"desc": "Filter by the process definition the instances run on."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public class HistoricProcessInstanceQueryDto extends AbstractQueryDto<HistoricPr

private String processInstanceId;
private Set<String> processInstanceIds;
private List<String> processInstanceIdNotIn;
private String processDefinitionId;
private String processDefinitionKey;
private List<String> processDefinitionKeys;
Expand Down Expand Up @@ -145,6 +146,11 @@ public void setProcessInstanceIds(Set<String> processInstanceIds) {
this.processInstanceIds = processInstanceIds;
}

@CamundaQueryParam(value = "processInstanceIdNotIn", converter = StringSetConverter.class)
public void setProcessInstanceIdNotIn(List<String> processInstanceIdNotIn) {
this.processInstanceIdNotIn = processInstanceIdNotIn;
}

public String getProcessDefinitionId() {
return processDefinitionId;
}
Expand Down Expand Up @@ -413,6 +419,9 @@ protected void applyFilters(HistoricProcessInstanceQuery query) {
if (processInstanceIds != null) {
query.processInstanceIds(processInstanceIds);
}
if (processInstanceIdNotIn != null && !processInstanceIdNotIn.isEmpty()) {
query.processInstanceIdNotIn(processInstanceIdNotIn.toArray(new String[0]));
}
if (processDefinitionId != null) {
query.processDefinitionId(processDefinitionId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,43 @@ private void verifyProcessInstanceIdSetInvocation() {
verify(mockedQuery).list();
}

@Test
public void testQueryByProcessInstanceIdNotIn() {
given()
.queryParam("processInstanceIdNotIn", "firstProcessInstanceId,secondProcessInstanceId")
.then()
.expect()
.statusCode(Status.OK.getStatusCode())
.when()
.get(HISTORIC_PROCESS_INSTANCE_RESOURCE_URL);

verify(mockedQuery).processInstanceIdNotIn("firstProcessInstanceId", "secondProcessInstanceId");
verify(mockedQuery).list();
}

@Test
public void testQueryByProcessInstanceIdNotInAsPost() {
Map<String, Set<String>> parameters = new HashMap<String, Set<String>>();

Set<String> processInstanceIds = new HashSet<String>();
processInstanceIds.add("firstProcessInstanceId");
processInstanceIds.add("secondProcessInstanceId");

parameters.put("processInstanceIdNotIn", processInstanceIds);

given()
.contentType(POST_JSON_CONTENT_TYPE)
.body(parameters)
.then()
.expect()
.statusCode(Status.OK.getStatusCode())
.when()
.post(HISTORIC_PROCESS_INSTANCE_RESOURCE_URL);

verify(mockedQuery).processInstanceIdNotIn("firstProcessInstanceId", "secondProcessInstanceId");
verify(mockedQuery).list();
}

@Test
public void testQueryByProcessDefinitionKeyNotIn() {
given()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ public interface HistoricProcessInstanceQuery extends Query<HistoricProcessInsta
*/
HistoricProcessInstanceQuery processInstanceIds(Set<String> processInstanceIds);

/**
* Only select historic process instances whose id is not in the given set of ids.
* {@link ProcessInstance) ids and {@link HistoricProcessInstance} ids match.
*/
HistoricProcessInstanceQuery processInstanceIdNotIn(String... processInstanceIdNotIn);

/**
* Only select historic process instances for the given process definition
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class HistoricProcessInstanceQueryImpl extends AbstractVariableQueryImpl<
protected String processDefinitionKey;
protected String[] processDefinitionKeys;
protected Set<String> processInstanceIds;
protected String[] processInstanceIdNotIn;
protected String[] tenantIds;
protected boolean isTenantIdSet;
protected String[] executedActivityIds;
Expand Down Expand Up @@ -120,6 +121,12 @@ public HistoricProcessInstanceQuery processInstanceIds(Set<String> processInstan
return this;
}

public HistoricProcessInstanceQuery processInstanceIdNotIn(String... processDefinitionIdNotIn){
ensureNotNull("processDefinitionIdNotIn", (Object[]) processDefinitionIdNotIn);
this.processInstanceIdNotIn = processDefinitionIdNotIn;
return this;
}

public HistoricProcessInstanceQueryImpl processDefinitionId(String processDefinitionId) {
this.processDefinitionId = processDefinitionId;
return this;
Expand Down Expand Up @@ -310,7 +317,9 @@ protected boolean hasExcludingConditions() {
|| CompareUtil.areNotInAscendingOrder(startedAfter, startedBefore)
|| CompareUtil.areNotInAscendingOrder(finishedAfter, finishedBefore)
|| CompareUtil.elementIsContainedInList(processDefinitionKey, processKeyNotIn)
|| CompareUtil.elementIsNotContainedInList(processInstanceId, processInstanceIds);
|| CompareUtil.elementIsNotContainedInList(processInstanceId, processInstanceIds)
|| CompareUtil.elementIsContainedInArray(processInstanceId, processInstanceIdNotIn)
|| CompareUtil.elementsAreContainedInArray(processInstanceIds, processInstanceIdNotIn);
}

public HistoricProcessInstanceQuery orderByProcessInstanceBusinessKey() {
Expand Down Expand Up @@ -559,6 +568,10 @@ public Set<String> getProcessInstanceIds() {
return processInstanceIds;
}

public String[] getProcessInstanceIdNotIn() {
return processInstanceIdNotIn;
}

public String getStartedBy() {
return startedBy;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

/**
Expand Down Expand Up @@ -158,4 +159,12 @@ public static <T extends Comparable<T>> T min(T obj1, T obj2) {
public static <T extends Comparable<T>> T max(T obj1, T obj2) {
return obj1.compareTo(obj2) >= 0 ? obj1 : obj2;
}

public static <T> boolean elementsAreContainedInArray(Collection<T> subset, T[] superset) {
if (subset != null && !subset.isEmpty() && superset != null && superset.length > 0 && superset.length >= subset.size()) {
return new HashSet<>(Arrays.asList(superset)).containsAll(subset);
}
return false;
}
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,13 @@
<bind name="fieldName" value="'SELF.PROC_INST_ID_'" />
<include refid="org.camunda.bpm.engine.impl.persistence.entity.Commons.applyInForPaginatedCollection" />
</if>
<if test="query.processInstanceIdNotIn != null and query.processInstanceIdNotIn.length > 0">
${queryType} SELF.PROC_INST_ID_ NOT IN
<foreach item="processInstanceId" index="index" collection="query.processInstanceIdNotIn"
open="(" separator="," close=")">
#{processInstanceId}
</foreach>
</if>
<if test="query.caseInstanceId != null">
${queryType} SELF.CASE_INST_ID_ = #{query.caseInstanceId}
</if>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -2595,6 +2596,101 @@ public void shouldQueryByVariableValue_12() {
.containsExactlyInAnyOrder(processInstanceIdOne, processInstanceIdTwo);
}

@Test
@Deployment(resources = {"org/camunda/bpm/engine/test/history/oneTaskProcess.bpmn20.xml"})
public void shouldExcludeByProcessInstanceIdNotIn() {
// GIVEN
String processInstanceIdOne = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();
String processInstanceIdTwo = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();

// WHEN
List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery().list();
List<HistoricProcessInstance> excludedFirst = historyService.createHistoricProcessInstanceQuery()
.processInstanceIdNotIn(processInstanceIdOne).list();
List<HistoricProcessInstance> excludedAll = historyService.createHistoricProcessInstanceQuery()
.processInstanceIdNotIn(processInstanceIdOne, processInstanceIdTwo).list();

// THEN
assertThat(processInstances)
.extracting("processInstanceId")
.containsExactlyInAnyOrder(processInstanceIdOne, processInstanceIdTwo);
assertThat(excludedFirst)
.extracting("processInstanceId")
.containsExactly(processInstanceIdTwo);
assertThat(excludedAll)
.extracting("processInstanceId")
.isEmpty();
}

@Test
@Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml")
public void testWithNonExistentProcessInstanceIdNotIn() {
// GIVEN
String processInstanceIdOne = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();
String processInstanceIdTwo = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();

String nonExistentProcessInstanceId = "ThisIsAFake";

// WHEN
List<HistoricProcessInstance> processInstances = historyService.createHistoricProcessInstanceQuery().list();
List<HistoricProcessInstance> excludedNonExistent = historyService.createHistoricProcessInstanceQuery()
.processInstanceIdNotIn(nonExistentProcessInstanceId).list();

// THEN
assertThat(processInstances)
.extracting("processInstanceId")
.containsExactlyInAnyOrder(processInstanceIdOne, processInstanceIdTwo);
assertThat(excludedNonExistent)
.extracting("processInstanceId")
.containsExactly(processInstanceIdOne, processInstanceIdTwo);
}

@Test
@Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml")
public void testQueryByOneInvalidProcessInstanceIdNotIn() {
try {
// when
historyService.createHistoricProcessInstanceQuery()
.processInstanceIdNotIn((String) null);
fail();
} catch(ProcessEngineException expected) {
// then Exception is expected
}
}

@Test
@Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml")
public void testExcludingProcessInstanceAndProcessInstanceIdNotIn() {
// GIVEN
String processInstanceIdOne = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();
runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();

// WHEN
long count = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstanceIdOne)
.processInstanceIdNotIn(processInstanceIdOne).count();

// THEN making a query that has contradicting conditions should succeed
assertThat(count).isEqualTo(0L);
}

@Test
@Deployment(resources = "org/camunda/bpm/engine/test/api/oneTaskProcess.bpmn20.xml")
public void testExcludingProcessInstanceIdsAndProcessInstanceIdNotIn() {
// GIVEN
String processInstanceIdOne = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();
String processInstanceIdTwo = runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();
runtimeService.startProcessInstanceByKey("oneTaskProcess").getProcessInstanceId();

// WHEN
long count = historyService.createHistoricProcessInstanceQuery()
.processInstanceIds(new HashSet<>(Arrays.asList(processInstanceIdOne, processInstanceIdTwo)))
.processInstanceIdNotIn(processInstanceIdOne, processInstanceIdTwo).count();

// THEN making a query that has contradicting conditions should succeed
assertThat(count).isEqualTo(0L);
}

protected void deployment(String... resources) {
testHelper.deploy(resources);
}
Expand Down

0 comments on commit d3ade5a

Please sign in to comment.