Skip to content

Commit

Permalink
ProcessInstanceCollectionResource and CaseInstanceCollectionResource… (
Browse files Browse the repository at this point in the history
…#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>
  • Loading branch information
WelschChristopher and WelschChristopher authored Oct 3, 2024
1 parent d05f8f1 commit 0069d6a
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,12 @@ public DataResponse<CaseInstanceResponse> getCaseInstances(@ApiParam(hidden = tr

@ApiOperation(value = "Start a case instance", tags = { "Case Instances" },
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"
+ "Only one of *caseDefinitionId* or *caseDefinitionKey* an be used in the request body. \n\n"
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n "
+ "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 "
+ "Note that the variable-scope that is supplied is ignored, process-variables are always local.\n\n",
+ "Only one of *caseDefinitionId* or *caseDefinitionKey* an be used in the request body.\n\n"
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n"
+ "If tenantId is omitted, the default tenant will be used.\n\n "
+ "It is possible to send variables, transientVariables and startFormVariables in one request.\n\n"
+ "More information about the variable format can be found in the REST variables section.\n\n "
+ "Note that the variable-scope that is supplied is ignored, case-variables are always local.\n\n",
code = 201)
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Indicates the case instance was created."),
Expand Down Expand Up @@ -319,26 +321,26 @@ public CaseInstanceResponse createCaseInstance(@RequestBody CaseInstanceCreateRe
}
startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

} else {
if (request.getVariables() != null) {
startVariables = new HashMap<>();
for (RestVariable variable : request.getVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));

}

if (request.getVariables() != null) {
startVariables = new HashMap<>();
for (RestVariable variable : request.getVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

if (request.getTransientVariables() != null) {
transientVariables = new HashMap<>();
for (RestVariable variable : request.getTransientVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

if (request.getTransientVariables() != null) {
transientVariables = new HashMap<>();
for (RestVariable variable : request.getTransientVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import static org.assertj.core.api.Assertions.tuple;
import static org.mockito.Mockito.when;

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

}

@CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/runtime/caseWithStartFormAndServiceTask.cmmn" })
public void testAllVariablesAreApplied() {
CaseDefinition caseDefinition = repositoryService.createCaseDefinitionQuery().caseDefinitionKey("caseWithStartFormAndServiceTask").singleResult();

runUsingMocks(() -> {
Map engineConfigurations = cmmnEngineConfiguration.getEngineConfigurations();
engineConfigurations.put(EngineConfigurationConstants.KEY_FORM_ENGINE_CONFIG, formEngineConfiguration);

FormInfo formInfo = new FormInfo();
formInfo.setId("formDefId");
formInfo.setKey("formDefKey");
formInfo.setName("Form Definition Name");

when(formEngineConfiguration.getFormRepositoryService()).thenReturn(formRepositoryService);
when(formRepositoryService.getFormModelByKeyAndParentDeploymentId("form1", caseDefinition.getDeploymentId(), caseDefinition.getTenantId(),
cmmnEngineConfiguration.isFallbackToDefaultTenant()))
.thenReturn(formInfo);

String url = CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_DEFINITION_START_FORM, caseDefinition.getId());
CloseableHttpResponse response = executeRequest(new HttpGet(SERVER_URL_PREFIX + url), HttpStatus.SC_OK);
JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS)
.isEqualTo("{"
+ " id: 'formDefId',"
+ " key: 'formDefKey',"
+ " name: 'Form Definition Name',"
+ " type: 'startForm',"
+ " definitionKey: 'caseWithStartFormAndServiceTask'"
+ "}");

ArrayNode formVariablesNode = objectMapper.createArrayNode();

// String variable
ObjectNode stringVarNode = formVariablesNode.addObject();
stringVarNode.put("name", "user");
stringVarNode.put("value", "simple string value");
stringVarNode.put("type", "string");

ObjectNode integerVarNode = formVariablesNode.addObject();
integerVarNode.put("name", "number");
integerVarNode.put("value", 1234);
integerVarNode.put("type", "integer");

ArrayNode variablesNode = objectMapper.createArrayNode();
stringVarNode = variablesNode.addObject();
stringVarNode.put("name", "userVariable");
stringVarNode.put("value", "simple string value");
stringVarNode.put("type", "string");

ArrayNode transientVariablesNode = objectMapper.createArrayNode();
stringVarNode = transientVariablesNode.addObject();
stringVarNode.put("name", "userTransient");
stringVarNode.put("value", "simple transient value");
stringVarNode.put("type", "string");

ObjectNode requestNode = objectMapper.createObjectNode();
requestNode.set("startFormVariables", formVariablesNode);
requestNode.set("variables", variablesNode);
requestNode.set("transientVariables", transientVariablesNode);
requestNode.put("caseDefinitionKey", "caseWithStartFormAndServiceTask");

when(formRepositoryService.getFormModelByKeyAndParentDeploymentId("form1", caseDefinition.getDeploymentId()))
.thenReturn(formInfo);
when(formEngineConfiguration.getFormService()).thenReturn(formEngineFormService);
when(formEngineFormService.getVariablesFromFormSubmission(null, "planModel", null, caseDefinition.getId(), ScopeTypes.CMMN, formInfo,
Map.of("user", "simple string value", "number", 1234), null))
.thenReturn(Map.of("user", "simple string value return", "number", 1234L));

HttpPost httpPost = new HttpPost(SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_COLLECTION));
httpPost.setEntity(new StringEntity(requestNode.toString()));
response = executeRequest(httpPost, HttpStatus.SC_CREATED);
responseNode = objectMapper.readTree(response.getEntity().getContent());

String instanceId = responseNode.get("id").asText();
assertThat(runtimeService.getVariables(instanceId).entrySet()).extracting(Map.Entry::getKey, Map.Entry::getValue).containsExactlyInAnyOrder(
tuple("user", "simple string value return"),
tuple("number", 1234L),
tuple("userVariable", "simple string value"),
tuple("userTransient", "simple transient value")
);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* 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.flowable.cmmn.rest.service.api.runtime;

import org.flowable.cmmn.api.delegate.DelegatePlanItemInstance;
import org.flowable.cmmn.api.delegate.PlanItemJavaDelegate;

/**
* Processes the transient variable and puts the relevant bits in real variables
*/
public class TransientVariableServiceTask implements PlanItemJavaDelegate {
@Override
public void execute(DelegatePlanItemInstance planItemInstance) {
planItemInstance.getTransientVariables().forEach((s, o) -> planItemInstance.setVariable(s, o.toString()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">

<case id="caseWithStartFormAndServiceTask" name="Case With Start Form">
<documentation>A human task case</documentation>
<casePlanModel id="myPlanModel" name="My CasePlanModel" flowable:formKey="testFormKey">
<planItem id="planItemcmmnTask_1" name="Human task" definitionRef="cmmnTask_1">
<entryCriterion id="cmmnEntrySentry_1" sentryRef="sentrycmmnEntrySentry_1"></entryCriterion>
</planItem>
<planItem id="planItemcmmnTask_2" name="Service task" definitionRef="cmmnTask_2"></planItem>
<sentry id="sentrycmmnEntrySentry_1">
<extensionElements>
<design:stencilid><![CDATA[EntryCriterion]]></design:stencilid>
</extensionElements>
<planItemOnPart id="sentryOnPartcmmnEntrySentry_1" sourceRef="planItemcmmnTask_2">
<standardEvent>complete</standardEvent>
</planItemOnPart>
</sentry>
<humanTask id="cmmnTask_1" name="Human task" flowable:assignee="${initiator}" flowable:formFieldValidation="false">
<extensionElements>
<flowable:task-candidates-type><![CDATA[all]]></flowable:task-candidates-type>
<design:stencilid><![CDATA[HumanTask]]></design:stencilid>
<design:stencilsuperid><![CDATA[Task]]></design:stencilsuperid>
</extensionElements>
</humanTask>
<task id="cmmnTask_2" name="Service task" flowable:type="java" flowable:class="org.flowable.cmmn.rest.service.api.runtime.TransientVariableServiceTask">
<extensionElements>
<design:stencilid><![CDATA[ServiceTask]]></design:stencilid>
<design:stencilsuperid><![CDATA[Task]]></design:stencilsuperid>
</extensionElements>
</task>
</casePlanModel>
</case>

</definitions>
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,10 @@ public DataResponse<ProcessInstanceResponse> getProcessInstances(@ApiParam(hidde
@ApiOperation(value = "Start a process instance", tags = { "Process Instances" },
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"
+ "Only one of *processDefinitionId*, *processDefinitionKey* or *message* can be used in the request body. \n\n"
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n "
+ "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 "
+ "Parameters *businessKey*, *variables* and *tenantId* are optional.\n\n"
+ "If tenantId is omitted, the default tenant will be used.\n\n "
+ "It is possible to send variables, transientVariables and startFormVariables in one request.\n\n"
+ "More information about the variable format can be found in the REST variables section.\n\n "
+ "Note that the variable-scope that is supplied is ignored, process-variables are always local.\n\n",
code = 201)
@ApiResponses(value = {
Expand Down Expand Up @@ -327,27 +329,25 @@ public ProcessInstanceResponse createProcessInstance(@RequestBody ProcessInstanc
}
startFormVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

} else {

if (request.getVariables() != null && !request.getVariables().isEmpty()) {
startVariables = new HashMap<>();
for (RestVariable variable : request.getVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

if (request.getVariables() != null && !request.getVariables().isEmpty()) {
startVariables = new HashMap<>();
for (RestVariable variable : request.getVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
startVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

if (request.getTransientVariables() != null && !request.getTransientVariables().isEmpty()) {
transientVariables = new HashMap<>();
for (RestVariable variable : request.getTransientVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}

if (request.getTransientVariables() != null && !request.getTransientVariables().isEmpty()) {
transientVariables = new HashMap<>();
for (RestVariable variable : request.getTransientVariables()) {
if (variable.getName() == null) {
throw new FlowableIllegalArgumentException("Variable name is required.");
}
transientVariables.put(variable.getName(), restResponseFactory.getVariableValue(variable));
}
}

Expand Down
Loading

0 comments on commit 0069d6a

Please sign in to comment.