Skip to content

Commit 0069d6a

Browse files
ProcessInstanceCollectionResource and CaseInstanceCollectionResource… (#3956)
* ProcessInstanceCollectionResource and CaseInstanceCollectionResource now support sending transientVariables,variables and startFormVariables in one request. * added missing license header * testcase cleanup --------- Co-authored-by: Christopher Welsch <christopher.welsch@flowable.com>
1 parent d05f8f1 commit 0069d6a

8 files changed

Lines changed: 331 additions & 41 deletions

File tree

modules/flowable-cmmn-rest/src/main/java/org/flowable/cmmn/rest/service/api/runtime/caze/CaseInstanceCollectionResource.java

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ public DataResponse<CaseInstanceResponse> getCaseInstances(@ApiParam(hidden = tr
278278

279279
@ApiOperation(value = "Start a case instance", tags = { "Case Instances" },
280280
notes = "Note that also a *transientVariables* property is accepted as part of this json, that follows the same structure as the *variables* property.\n\n"
281-
+ "Only one of *caseDefinitionId* or *caseDefinitionKey* an be used in the request body. \n\n"
282-
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n "
283-
+ "If tenantId is omitted, the default tenant will be used. More information about the variable format can be found in the REST variables section.\n\n "
284-
+ "Note that the variable-scope that is supplied is ignored, process-variables are always local.\n\n",
281+
+ "Only one of *caseDefinitionId* or *caseDefinitionKey* an be used in the request body.\n\n"
282+
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n"
283+
+ "If tenantId is omitted, the default tenant will be used.\n\n "
284+
+ "It is possible to send variables, transientVariables and startFormVariables in one request.\n\n"
285+
+ "More information about the variable format can be found in the REST variables section.\n\n "
286+
+ "Note that the variable-scope that is supplied is ignored, case-variables are always local.\n\n",
285287
code = 201)
286288
@ApiResponses(value = {
287289
@ApiResponse(code = 201, message = "Indicates the case instance was created."),
@@ -319,26 +321,26 @@ public CaseInstanceResponse createCaseInstance(@RequestBody CaseInstanceCreateRe
319321
}
320322
startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
321323
}
322-
323-
} else {
324-
if (request.getVariables() != null) {
325-
startVariables = new HashMap<>();
326-
for (RestVariable variable : request.getVariables()) {
327-
if (variable.getName() == null) {
328-
throw new FlowableIllegalArgumentException("Variable name is required.");
329-
}
330-
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
324+
325+
}
326+
327+
if (request.getVariables() != null) {
328+
startVariables = new HashMap<>();
329+
for (RestVariable variable : request.getVariables()) {
330+
if (variable.getName() == null) {
331+
throw new FlowableIllegalArgumentException("Variable name is required.");
331332
}
333+
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
332334
}
333-
334-
if (request.getTransientVariables() != null) {
335-
transientVariables = new HashMap<>();
336-
for (RestVariable variable : request.getTransientVariables()) {
337-
if (variable.getName() == null) {
338-
throw new FlowableIllegalArgumentException("Variable name is required.");
339-
}
340-
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
335+
}
336+
337+
if (request.getTransientVariables() != null) {
338+
transientVariables = new HashMap<>();
339+
for (RestVariable variable : request.getTransientVariables()) {
340+
if (variable.getName() == null) {
341+
throw new FlowableIllegalArgumentException("Variable name is required.");
341342
}
343+
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
342344
}
343345
}
344346

modules/flowable-cmmn-rest/src/test/java/org/flowable/cmmn/rest/service/api/runtime/CaseInstanceCollectionResourceTest.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
1717
import static org.assertj.core.api.Assertions.assertThat;
1818
import static org.assertj.core.api.Assertions.entry;
19+
import static org.assertj.core.api.Assertions.tuple;
1920
import static org.mockito.Mockito.when;
2021

2122
import java.io.IOException;
@@ -951,4 +952,88 @@ public void testQueryByParentScopeId() throws IOException {
951952

952953
}
953954

955+
@CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/runtime/caseWithStartFormAndServiceTask.cmmn" })
956+
public void testAllVariablesAreApplied() {
957+
CaseDefinition caseDefinition = repositoryService.createCaseDefinitionQuery().caseDefinitionKey("caseWithStartFormAndServiceTask").singleResult();
958+
959+
runUsingMocks(() -> {
960+
Map engineConfigurations = cmmnEngineConfiguration.getEngineConfigurations();
961+
engineConfigurations.put(EngineConfigurationConstants.KEY_FORM_ENGINE_CONFIG, formEngineConfiguration);
962+
963+
FormInfo formInfo = new FormInfo();
964+
formInfo.setId("formDefId");
965+
formInfo.setKey("formDefKey");
966+
formInfo.setName("Form Definition Name");
967+
968+
when(formEngineConfiguration.getFormRepositoryService()).thenReturn(formRepositoryService);
969+
when(formRepositoryService.getFormModelByKeyAndParentDeploymentId("form1", caseDefinition.getDeploymentId(), caseDefinition.getTenantId(),
970+
cmmnEngineConfiguration.isFallbackToDefaultTenant()))
971+
.thenReturn(formInfo);
972+
973+
String url = CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_DEFINITION_START_FORM, caseDefinition.getId());
974+
CloseableHttpResponse response = executeRequest(new HttpGet(SERVER_URL_PREFIX + url), HttpStatus.SC_OK);
975+
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
976+
closeResponse(response);
977+
assertThatJson(responseNode)
978+
.when(Option.IGNORING_EXTRA_FIELDS)
979+
.isEqualTo("{"
980+
+ " id: 'formDefId',"
981+
+ " key: 'formDefKey',"
982+
+ " name: 'Form Definition Name',"
983+
+ " type: 'startForm',"
984+
+ " definitionKey: 'caseWithStartFormAndServiceTask'"
985+
+ "}");
986+
987+
ArrayNode formVariablesNode = objectMapper.createArrayNode();
988+
989+
// String variable
990+
ObjectNode stringVarNode = formVariablesNode.addObject();
991+
stringVarNode.put("name", "user");
992+
stringVarNode.put("value", "simple string value");
993+
stringVarNode.put("type", "string");
994+
995+
ObjectNode integerVarNode = formVariablesNode.addObject();
996+
integerVarNode.put("name", "number");
997+
integerVarNode.put("value", 1234);
998+
integerVarNode.put("type", "integer");
999+
1000+
ArrayNode variablesNode = objectMapper.createArrayNode();
1001+
stringVarNode = variablesNode.addObject();
1002+
stringVarNode.put("name", "userVariable");
1003+
stringVarNode.put("value", "simple string value");
1004+
stringVarNode.put("type", "string");
1005+
1006+
ArrayNode transientVariablesNode = objectMapper.createArrayNode();
1007+
stringVarNode = transientVariablesNode.addObject();
1008+
stringVarNode.put("name", "userTransient");
1009+
stringVarNode.put("value", "simple transient value");
1010+
stringVarNode.put("type", "string");
1011+
1012+
ObjectNode requestNode = objectMapper.createObjectNode();
1013+
requestNode.set("startFormVariables", formVariablesNode);
1014+
requestNode.set("variables", variablesNode);
1015+
requestNode.set("transientVariables", transientVariablesNode);
1016+
requestNode.put("caseDefinitionKey", "caseWithStartFormAndServiceTask");
1017+
1018+
when(formRepositoryService.getFormModelByKeyAndParentDeploymentId("form1", caseDefinition.getDeploymentId()))
1019+
.thenReturn(formInfo);
1020+
when(formEngineConfiguration.getFormService()).thenReturn(formEngineFormService);
1021+
when(formEngineFormService.getVariablesFromFormSubmission(null, "planModel", null, caseDefinition.getId(), ScopeTypes.CMMN, formInfo,
1022+
Map.of("user", "simple string value", "number", 1234), null))
1023+
.thenReturn(Map.of("user", "simple string value return", "number", 1234L));
1024+
1025+
HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_COLLECTION));
1026+
httpPost.setEntity(new StringEntity(requestNode.toString()));
1027+
response = executeRequest(httpPost, HttpStatus.SC_CREATED);
1028+
responseNode = objectMapper.readTree(response.getEntity().getContent());
1029+
1030+
String instanceId = responseNode.get("id").asText();
1031+
assertThat(runtimeService.getVariables(instanceId).entrySet()).extracting(Map.Entry::getKey, Map.Entry::getValue).containsExactlyInAnyOrder(
1032+
tuple("user", "simple string value return"),
1033+
tuple("number", 1234L),
1034+
tuple("userVariable", "simple string value"),
1035+
tuple("userTransient", "simple transient value")
1036+
);
1037+
});
1038+
}
9541039
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.rest.service.api.runtime;
14+
15+
import org.flowable.cmmn.api.delegate.DelegatePlanItemInstance;
16+
import org.flowable.cmmn.api.delegate.PlanItemJavaDelegate;
17+
18+
/**
19+
* Processes the transient variable and puts the relevant bits in real variables
20+
*/
21+
public class TransientVariableServiceTask implements PlanItemJavaDelegate {
22+
@Override
23+
public void execute(DelegatePlanItemInstance planItemInstance) {
24+
planItemInstance.getTransientVariables().forEach((s, o) -> planItemInstance.setVariable(s, o.toString()));
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/cmmn" xmlns:cmmndi="http://www.omg.org/spec/CMMN/20151109/CMMNDI" xmlns:dc="http://www.omg.org/spec/CMMN/20151109/DC" xmlns:di="http://www.omg.org/spec/CMMN/20151109/DI" xmlns:design="http://flowable.org/design" targetNamespace="http://flowable.org/cmmn" design:palette="flowable-work-case-palette">
3+
4+
<case id="caseWithStartFormAndServiceTask" name="Case With Start Form">
5+
<documentation>A human task case</documentation>
6+
<casePlanModel id="myPlanModel" name="My CasePlanModel" flowable:formKey="testFormKey">
7+
<planItem id="planItemcmmnTask_1" name="Human task" definitionRef="cmmnTask_1">
8+
<entryCriterion id="cmmnEntrySentry_1" sentryRef="sentrycmmnEntrySentry_1"></entryCriterion>
9+
</planItem>
10+
<planItem id="planItemcmmnTask_2" name="Service task" definitionRef="cmmnTask_2"></planItem>
11+
<sentry id="sentrycmmnEntrySentry_1">
12+
<extensionElements>
13+
<design:stencilid><![CDATA[EntryCriterion]]></design:stencilid>
14+
</extensionElements>
15+
<planItemOnPart id="sentryOnPartcmmnEntrySentry_1" sourceRef="planItemcmmnTask_2">
16+
<standardEvent>complete</standardEvent>
17+
</planItemOnPart>
18+
</sentry>
19+
<humanTask id="cmmnTask_1" name="Human task" flowable:assignee="${initiator}" flowable:formFieldValidation="false">
20+
<extensionElements>
21+
<flowable:task-candidates-type><![CDATA[all]]></flowable:task-candidates-type>
22+
<design:stencilid><![CDATA[HumanTask]]></design:stencilid>
23+
<design:stencilsuperid><![CDATA[Task]]></design:stencilsuperid>
24+
</extensionElements>
25+
</humanTask>
26+
<task id="cmmnTask_2" name="Service task" flowable:type="java" flowable:class="org.flowable.cmmn.rest.service.api.runtime.TransientVariableServiceTask">
27+
<extensionElements>
28+
<design:stencilid><![CDATA[ServiceTask]]></design:stencilid>
29+
<design:stencilsuperid><![CDATA[Task]]></design:stencilsuperid>
30+
</extensionElements>
31+
</task>
32+
</casePlanModel>
33+
</case>
34+
35+
</definitions>

modules/flowable-rest/src/main/java/org/flowable/rest/service/api/runtime/process/ProcessInstanceCollectionResource.java

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,10 @@ public DataResponse<ProcessInstanceResponse> getProcessInstances(@ApiParam(hidde
287287
@ApiOperation(value = "Start a process instance", tags = { "Process Instances" },
288288
notes = "Note that also a *transientVariables* property is accepted as part of this json, that follows the same structure as the *variables* property.\n\n"
289289
+ "Only one of *processDefinitionId*, *processDefinitionKey* or *message* can be used in the request body. \n\n"
290-
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n "
291-
+ "If tenantId is omitted, the default tenant will be used. More information about the variable format can be found in the REST variables section.\n\n "
290+
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n"
291+
+ "If tenantId is omitted, the default tenant will be used.\n\n "
292+
+ "It is possible to send variables, transientVariables and startFormVariables in one request.\n\n"
293+
+ "More information about the variable format can be found in the REST variables section.\n\n "
292294
+ "Note that the variable-scope that is supplied is ignored, process-variables are always local.\n\n",
293295
code = 201)
294296
@ApiResponses(value = {
@@ -327,27 +329,25 @@ public ProcessInstanceResponse createProcessInstance(@RequestBody ProcessInstanc
327329
}
328330
startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
329331
}
330-
331-
} else {
332-
333-
if (request.getVariables() != null && !request.getVariables().isEmpty()) {
334-
startVariables = new HashMap<>();
335-
for (RestVariable variable : request.getVariables()) {
336-
if (variable.getName() == null) {
337-
throw new FlowableIllegalArgumentException("Variable name is required.");
338-
}
339-
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
332+
}
333+
334+
if (request.getVariables() != null && !request.getVariables().isEmpty()) {
335+
startVariables = new HashMap<>();
336+
for (RestVariable variable : request.getVariables()) {
337+
if (variable.getName() == null) {
338+
throw new FlowableIllegalArgumentException("Variable name is required.");
340339
}
340+
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
341341
}
342-
343-
if (request.getTransientVariables() != null && !request.getTransientVariables().isEmpty()) {
344-
transientVariables = new HashMap<>();
345-
for (RestVariable variable : request.getTransientVariables()) {
346-
if (variable.getName() == null) {
347-
throw new FlowableIllegalArgumentException("Variable name is required.");
348-
}
349-
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
342+
}
343+
344+
if (request.getTransientVariables() != null && !request.getTransientVariables().isEmpty()) {
345+
transientVariables = new HashMap<>();
346+
for (RestVariable variable : request.getTransientVariables()) {
347+
if (variable.getName() == null) {
348+
throw new FlowableIllegalArgumentException("Variable name is required.");
350349
}
350+
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
351351
}
352352
}
353353

0 commit comments

Comments
 (0)