Skip to content

Commit 399552f

Browse files
committed
Expand planItemInstances keyword to support filtering based on a predicate and add information about lambda expressions in release notes
1 parent eb868ad commit 399552f

File tree

4 files changed

+45
-35
lines changed

4 files changed

+45
-35
lines changed

distro/src/readme.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ <h1>Flowable Release Notes</h1>
3131
<h3>Release Notes - Flowable - 8.0.0</h3>
3232

3333
<ul>
34+
<li>
35+
The method `stream()` can be used to obtain a <code>java.util.stream.Stream</code> from a collection.
36+
</li>
37+
<li>
38+
Lambda expressions can be used in expressions. e.g. if you have a transient list of customers you can do something like:
39+
<code>${customers.stream().filter(customer -> customer.type eq 'premium').toList()}</code> to get a list of premium customers
40+
or <code>${customers.stream().map(customer -> customer.name).toList()</code> to get the names of all the customers.
41+
</li>
42+
<li>
43+
The <code>planItemInstances</code> now expose a <code>filter(Predicate&lt;DelegatePlanItemInstance&gt; predicate)</code> method that can be used to provide custom filter options.
44+
</li>
3445
<li>
3546
Date variables will now include the milliseconds when they are returned over REST.
3647
E.g., up to now a date variable with the value of <code>2020-05-04T09:25:45.583Z</code> would have been returned as <code>2020-05-04T09:25:45Z</code>.
@@ -45,7 +56,7 @@ <h3>Release Notes - Flowable - 8.0.0</h3>
4556
There is still support for Jackson 2 variables.
4657
With Spring Boot this can be enabled by setting <code>flowable.variable-json-mapper</code> to <code>jackson2</code>.
4758
By default, Jackson 3 is being used for variables.
48-
When configuring Flowable without Spring Boot then the <code>variableJsonMapper</code> on the process, cmmn and app engine configurations
59+
When configuring Flowable without Spring Boot then the <code>variableJsonMapper</code> on the process, cmmn, and app engine configurations
4960
should be set to be <code>org.flowable.common.engine.impl.json.jackson2.Jackson2VariableJsonMapper</code>
5061
and the <code>org.flowable.common.rest.variable.Jackson2JsonObjectRestVariableConverter</code> should be added to the appropriate rest response factories.
5162
</li>

docs/docusaurus/docs/cmmn/ch03-API.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ To filter based on the name:
234234

235235
- names('name1', 'name2', 'name3')
236236

237+
To filter using a lambda expression:
238+
239+
- filter(planItemInstance -> planItemInstance.name.startsWith('Service')
240+
237241
For some use cases, the plan item instances that should be filtered should only be part of the current stage. The 'current stage' is either the parent stage of a plan item or the case instance when there’s no parent stage.
238242

239243
- currentStage()

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/el/PlanItemInstancesWrapper.java

Lines changed: 17 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import java.util.Arrays;
1717
import java.util.List;
1818
import java.util.Objects;
19+
import java.util.function.Predicate;
1920
import java.util.stream.Collectors;
2021

22+
import org.flowable.cmmn.api.delegate.DelegatePlanItemInstance;
2123
import org.flowable.cmmn.api.runtime.CaseInstance;
2224
import org.flowable.cmmn.api.runtime.PlanItemInstance;
2325
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
@@ -78,16 +80,10 @@ public PlanItemInstancesWrapper definitionId(String...ids) {
7880
}
7981

8082
public PlanItemInstancesWrapper definitionIds(String...ids) {
81-
ensurePlanItemInstanceInitialized();
82-
8383
List<String> list = Arrays.asList(ids);
84-
List<PlanItemInstanceEntity> filteredPlanItemInstances = planItemInstances.stream()
85-
.filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
84+
return filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
8685
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() != null
87-
&& list.contains(planItemInstanceEntity.getPlanItem().getPlanItemDefinition().getId()))
88-
.collect(Collectors.toList());
89-
90-
return new PlanItemInstancesWrapper(variableContainer, caseInstanceEntity, filteredPlanItemInstances);
86+
&& list.contains(planItemInstanceEntity.getPlanItem().getPlanItemDefinition().getId()));
9187
}
9288

9389
public List<String> getDefinitionId() {
@@ -104,16 +100,11 @@ public PlanItemInstancesWrapper name(String...names) {
104100
}
105101

106102
public PlanItemInstancesWrapper names(String...names) {
107-
ensurePlanItemInstanceInitialized();
108103

109104
List<String> list = Arrays.asList(names);
110-
List<PlanItemInstanceEntity> filteredPlanItemInstances = planItemInstances.stream()
111-
.filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
105+
return filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
112106
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() != null
113-
&& list.contains(planItemInstanceEntity.getPlanItem().getPlanItemDefinition().getName()))
114-
.collect(Collectors.toList());
115-
116-
return new PlanItemInstancesWrapper(variableContainer, caseInstanceEntity, filteredPlanItemInstances);
107+
&& list.contains(planItemInstanceEntity.getPlanItem().getPlanItemDefinition().getName()));
117108
}
118109

119110
public List<String> getDefinitionName() {
@@ -193,25 +184,23 @@ public PlanItemInstancesWrapper asyncActiveLeave() {
193184
}
194185

195186
public PlanItemInstancesWrapper onlyTerminal() {
196-
ensurePlanItemInstanceInitialized();
197-
198-
List<PlanItemInstanceEntity> filteredPlanItemInstances = planItemInstances.stream()
199-
.filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
187+
return filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
200188
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() != null
201-
&& PlanItemInstanceState.isInTerminalState(planItemInstanceEntity))
202-
.collect(Collectors.toList());
203-
204-
return new PlanItemInstancesWrapper(variableContainer, caseInstanceEntity, filteredPlanItemInstances);
189+
&& PlanItemInstanceState.isInTerminalState(planItemInstanceEntity));
205190
}
206191

207192
public PlanItemInstancesWrapper onlyNonTerminal() {
193+
return filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
194+
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() != null
195+
&& !PlanItemInstanceState.isInTerminalState(planItemInstanceEntity));
196+
}
197+
198+
public PlanItemInstancesWrapper filter(Predicate<DelegatePlanItemInstance> filter) {
208199
ensurePlanItemInstanceInitialized();
209200

210201
List<PlanItemInstanceEntity> filteredPlanItemInstances = planItemInstances.stream()
211-
.filter(planItemInstanceEntity -> planItemInstanceEntity.getPlanItem() != null
212-
&& planItemInstanceEntity.getPlanItem().getPlanItemDefinition() != null
213-
&& !PlanItemInstanceState.isInTerminalState(planItemInstanceEntity))
214-
.collect(Collectors.toList());
202+
.filter(filter)
203+
.collect(Collectors.toList());
215204

216205
return new PlanItemInstancesWrapper(variableContainer, caseInstanceEntity, filteredPlanItemInstances);
217206
}
@@ -224,13 +213,7 @@ public List<PlanItemInstanceEntity> getList() {
224213
// Helper methods
225214

226215
protected PlanItemInstancesWrapper getPlanItemInstancesWithState(String state) {
227-
ensurePlanItemInstanceInitialized();
228-
229-
List<PlanItemInstanceEntity> filteredPlanItemInstances = planItemInstances.stream()
230-
.filter(planItemInstanceEntity -> Objects.equals(state, planItemInstanceEntity.getState()))
231-
.collect(Collectors.toList());
232-
233-
return new PlanItemInstancesWrapper(variableContainer, caseInstanceEntity, filteredPlanItemInstances);
216+
return filter(planItemInstanceEntity -> Objects.equals(state, planItemInstanceEntity.getState()));
234217
}
235218

236219
protected List<PlanItemInstanceEntity> collectAllChildPlanItemInstances(PlanItemInstanceContainer planItemInstanceContainer) {

modules/flowable-cmmn-engine/src/test/java/org/flowable/cmmn/test/el/PlanItemInstancesKeyWordInExpressionsTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,18 @@ public void testWithName() {
144144
assertThat(evaluateExpression(caseInstance.getId(), "${planItemInstances.name('invalid', 'A').count()}")).isEqualTo(1);
145145
}
146146

147+
@Test
148+
@CmmnDeployment(resources = "org/flowable/cmmn/test/el/PlanItemInstancesKeyWordInExpressionsTest.testWithName.cmmn")
149+
public void testWithFilterUsingLambda() {
150+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
151+
.caseDefinitionKey("testPlanItemInstancesKeyWord").start();
152+
153+
assertThat(evaluateExpression(caseInstance.getId(), "${planItemInstances.count()}")).isEqualTo(8);
154+
assertThat(evaluateExpression(caseInstance.getId(), "${planItemInstances.filter(inst -> inst.name eq 'A' or inst.name eq 'B').count()}")).isEqualTo(2);
155+
156+
assertThat(evaluateExpression(caseInstance.getId(), "${planItemInstances.filter(inst -> inst.name eq 'invalid').count()}")).isEqualTo(0);
157+
}
158+
147159
@Test
148160
@CmmnDeployment
149161
public void testWithStateAndId() {

0 commit comments

Comments
 (0)