Skip to content

Commit

Permalink
Merge branch 'main' of github.com:flowable/flowable-engine
Browse files Browse the repository at this point in the history
  • Loading branch information
tijsrademakers committed Oct 3, 2024
2 parents 024ba64 + d1a8c91 commit c47582b
Show file tree
Hide file tree
Showing 34 changed files with 588 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,10 @@ protected void writeConditionalDefinition(Event parentEvent, ConditionalEventDef

if (StringUtils.isNotEmpty(conditionalDefinition.getConditionExpression())) {
xtw.writeStartElement(ELEMENT_CONDITION);
if (conditionalDefinition.getConditionLanguage() != null) {
xtw.writeAttribute(XSI_PREFIX, XSI_NAMESPACE, "type", "tFormalExpression");
BpmnXMLUtil.writeDefaultAttribute(BpmnXMLConstants.ATTRIBUTE_SCRIPT_LANGUAGE, conditionalDefinition.getConditionLanguage(), xtw);
}
xtw.writeCharacters(conditionalDefinition.getConditionExpression());
xtw.writeEndElement();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.util.BpmnXMLUtil;
import org.flowable.bpmn.model.BaseElement;
import org.flowable.bpmn.model.BpmnModel;
Expand Down Expand Up @@ -66,6 +67,9 @@ protected void writeAdditionalChildElements(BaseElement element, BpmnModel model
if (StringUtils.isNotEmpty(sequenceFlow.getConditionExpression())) {
xtw.writeStartElement(ELEMENT_FLOW_CONDITION);
xtw.writeAttribute(XSI_PREFIX, XSI_NAMESPACE, "type", "tFormalExpression");
if (sequenceFlow.getConditionLanguage() != null) {
BpmnXMLUtil.writeDefaultAttribute(BpmnXMLConstants.ATTRIBUTE_SCRIPT_LANGUAGE, sequenceFlow.getConditionLanguage(), xtw);
}
xtw.writeCData(sequenceFlow.getConditionExpression());
xtw.writeEndElement();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ public String getElementName() {

@Override
public void parseChildElement(XMLStreamReader xtr, BaseElement parentElement, BpmnModel model) throws Exception {
if (!(parentElement instanceof SequenceFlow)) {
if (!(parentElement instanceof SequenceFlow sequenceFlow)) {
return;
}

((SequenceFlow) parentElement).setConditionExpression(xtr.getElementText().trim());
sequenceFlow.setConditionLanguage(xtr.getAttributeValue(null, ATTRIBUTE_SCRIPT_LANGUAGE));
sequenceFlow.setConditionExpression(xtr.getElementText().trim());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ public String getElementName() {

@Override
public void parseChildElement(XMLStreamReader xtr, BaseElement parentElement, BpmnModel model) throws Exception {
if (!(parentElement instanceof ConditionalEventDefinition)) {
if (!(parentElement instanceof ConditionalEventDefinition conditionalEventDefinition)) {
return;
}

((ConditionalEventDefinition) parentElement).setConditionExpression(xtr.getElementText().trim());
conditionalEventDefinition.setConditionLanguage(xtr.getAttributeValue(null, ATTRIBUTE_SCRIPT_LANGUAGE));
conditionalEventDefinition.setConditionExpression(xtr.getElementText().trim());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
public class ConditionalEventDefinition extends EventDefinition {

protected String conditionExpression;
protected String conditionLanguage;

public String getConditionExpression() {
return conditionExpression;
Expand All @@ -27,6 +28,14 @@ public void setConditionExpression(String conditionExpression) {
this.conditionExpression = conditionExpression;
}

public String getConditionLanguage() {
return conditionLanguage;
}

public void setConditionLanguage(String conditionLanguage) {
this.conditionLanguage = conditionLanguage;
}

@Override
public ConditionalEventDefinition clone() {
ConditionalEventDefinition clone = new ConditionalEventDefinition();
Expand All @@ -37,5 +46,6 @@ public ConditionalEventDefinition clone() {
public void setValues(ConditionalEventDefinition otherDefinition) {
super.setValues(otherDefinition);
setConditionExpression(otherDefinition.getConditionExpression());
setConditionLanguage(otherDefinition.getConditionLanguage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
public class SequenceFlow extends FlowElement {

protected String conditionExpression;
protected String conditionLanguage;
protected String sourceRef;
protected String targetRef;
protected String skipExpression;
Expand Down Expand Up @@ -60,6 +61,14 @@ public void setConditionExpression(String conditionExpression) {
this.conditionExpression = conditionExpression;
}

public String getConditionLanguage() {
return conditionLanguage;
}

public void setConditionLanguage(String conditionLanguage) {
this.conditionLanguage = conditionLanguage;
}

public String getSourceRef() {
return sourceRef;
}
Expand Down Expand Up @@ -123,6 +132,7 @@ public SequenceFlow clone() {
public void setValues(SequenceFlow otherFlow) {
super.setValues(otherFlow);
setConditionExpression(otherFlow.getConditionExpression());
setConditionLanguage(otherFlow.getConditionLanguage());
setSourceRef(otherFlow.getSourceRef());
setTargetRef(otherFlow.getTargetRef());
setSkipExpression(otherFlow.getSkipExpression());
Expand Down
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 @@ -24,4 +24,8 @@ public interface FlowableConditionalEvent extends FlowableActivityEvent {
*/
String getConditionExpression();

/**
* @return the scripting language of the condition expression.
*/
String getConditionLanguage();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
public class FlowableConditionalEventImpl extends FlowableActivityEventImpl implements FlowableConditionalEvent {

protected String conditionExpression;
protected String conditionLanguage;

public FlowableConditionalEventImpl(FlowableEngineEventType type) {
super(type);
Expand All @@ -34,4 +35,12 @@ public String getConditionExpression() {
public void setConditionExpression(String conditionExpression) {
this.conditionExpression = conditionExpression;
}

public String getConditionLanguage() {
return conditionLanguage;
}

public void setConditionLanguage(String conditionLanguage) {
this.conditionLanguage = conditionLanguage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ public static FlowableMessageEvent createMessageEvent(FlowableEngineEventType ty
return newEvent;
}

public static FlowableConditionalEvent createConditionalEvent(FlowableEngineEventType type, String activityId, String conditionExpression,
public static FlowableConditionalEvent createConditionalEvent(FlowableEngineEventType type, String activityId, String conditionExpression, String conditionLanguage,
String executionId, String processInstanceId, String processDefinitionId) {

FlowableConditionalEventImpl newEvent = new FlowableConditionalEventImpl(type);
Expand All @@ -377,6 +377,7 @@ public static FlowableConditionalEvent createConditionalEvent(FlowableEngineEven
newEvent.setProcessDefinitionId(processDefinitionId);
newEvent.setProcessInstanceId(processInstanceId);
newEvent.setConditionExpression(conditionExpression);
newEvent.setConditionLanguage(conditionLanguage);
return newEvent;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
*/
public interface Condition extends Serializable {

boolean evaluate(String sequenceFlowId, DelegateExecution execution);
boolean evaluate(String elementId, DelegateExecution execution);
}
Loading

0 comments on commit c47582b

Please sign in to comment.