Skip to content

Commit

Permalink
Support UUID value type in flowable-rest API (#3856)
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky authored Aug 27, 2024
1 parent f344a93 commit 3c0bf9c
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -1726,6 +1727,73 @@ public void testQueryLocalDateTimeVariable() throws Exception {
Assertions.assertThat(caseInstance).isNull();
}

@Test
public void testQueryUUIDVariable() throws Exception {
Map<String, Object> vars = new HashMap<>();
UUID someUUID = UUID.randomUUID();
vars.put("uuidVar", someUUID);

CaseInstance caseInstance1 = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("oneTaskCase")
.variables(vars)
.start();

UUID someUUID2 = UUID.randomUUID();
vars = new HashMap<>();
vars.put("uuidVar", someUUID);
vars.put("uuidVar2", someUUID2);
CaseInstance caseInstance2 = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("oneTaskCase")
.variables(vars)
.start();

UUID someUUID3 = UUID.randomUUID();
vars = new HashMap<>();
vars.put("uuidVar", someUUID3);
CaseInstance caseInstance3 = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("oneTaskCase")
.variables(vars)
.start();

// Query on single uuid variable, should result in 2 matches
CaseInstanceQuery query = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", someUUID);
List<CaseInstance> caseInstances = query.list();
Assertions.assertThat(caseInstances).hasSize(2);

// Query on two uuid variables, should result in single value
query = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", someUUID)
.variableValueEquals("uuidVar2", someUUID2);
CaseInstance caseInstance = query.singleResult();
Assertions.assertThat(caseInstance).isNotNull();
Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance2.getId());

UUID unexistingUUID = UUID.randomUUID();
// Query with unexisting variable value
caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals("uuidVar", unexistingUUID).singleResult();
Assertions.assertThat(caseInstance).isNull();

// Test NOT_EQUALS
caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueNotEquals("uuidVar", someUUID).singleResult();
Assertions.assertThat(caseInstance).isNotNull();
Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance3.getId());

// Test value-only matching
caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(someUUID3).singleResult();
Assertions.assertThat(caseInstance).isNotNull();
Assertions.assertThat(caseInstance.getId()).isEqualTo(caseInstance3.getId());

caseInstances = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(someUUID).list();
Assertions.assertThat(caseInstances)
.extracting(CaseInstance::getId)
.containsExactlyInAnyOrder(
caseInstance1.getId(),
caseInstance2.getId()
);

caseInstance = cmmnRuntimeService.createCaseInstanceQuery().variableValueEquals(unexistingUUID).singleResult();
Assertions.assertThat(caseInstance).isNull();
}

@Test
public void testLocalization() {
CaseInstance createdCase = cmmnRuntimeService.createCaseInstanceBuilder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.flowable.common.rest.variable.RestVariableConverter;
import org.flowable.common.rest.variable.ShortRestVariableConverter;
import org.flowable.common.rest.variable.StringRestVariableConverter;
import org.flowable.common.rest.variable.UUIDRestVariableConverter;
import org.flowable.dmn.api.DmnDecision;
import org.flowable.eventsubscription.api.EventSubscription;
import org.flowable.form.api.FormDefinition;
Expand Down Expand Up @@ -1093,6 +1094,7 @@ protected void initializeVariableConverters() {
variableConverters.add(new InstantRestVariableConverter());
variableConverters.add(new LocalDateRestVariableConverter());
variableConverters.add(new LocalDateTimeRestVariableConverter());
variableConverters.add(new UUIDRestVariableConverter());
variableConverters.add(new JsonObjectRestVariableConverter(objectMapper));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
Expand Down Expand Up @@ -162,6 +163,30 @@ public void testGetCaseInstanceLocalDateTimeVariable() throws Exception {
+ "}");
}

@CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/repository/oneHumanTaskCase.cmmn" })
public void testGetCaseInstanceUUIDVariable() throws Exception {

CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").start();
UUID someUUID = UUID.fromString("26da81fb-18a6-4c19-b7b4-6877a568bfe1");
runtimeService.setVariable(caseInstance.getId(), "variable", someUUID);

CloseableHttpResponse response = executeRequest(
new HttpGet(SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_VARIABLE,
caseInstance.getId(), "variable")), HttpStatus.SC_OK);

JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());

closeResponse(response);
assertThat(responseNode).isNotNull();
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS)
.isEqualTo("{"
+ " name: 'variable',"
+ " type: 'uuid',"
+ " value: '" + someUUID + "'"
+ "}");
}

/**
* Test getting a case instance variable data. GET cmmn-runtime/case-instances/{caseInstanceId}/variables/{variableName}
*/
Expand Down Expand Up @@ -427,4 +452,37 @@ public void testUpdateBinaryCaseVariable() throws Exception {
assertThat(variableValue).isInstanceOf(byte[].class);
assertThat(new String((byte[]) variableValue)).isEqualTo("This is binary content");
}

@CmmnDeployment(resources = { "org/flowable/cmmn/rest/service/api/repository/oneHumanTaskCase.cmmn" })
public void testUpdateUUIDCaseVariable() throws Exception {

UUID someUUID = UUID.fromString("87b859b2-d0c7-4845-93c2-e96ef69115b5");
UUID someUUID2 = UUID.fromString("b2233abf-f84f-426f-b978-0d249b90cc45");
CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase")
.variables(Collections.singletonMap("overlappingVariable", (Object) "caseValue")).start();
runtimeService.setVariable(caseInstance.getId(), "uuidVariable", someUUID);

// Update variable
ObjectNode requestNode = objectMapper.createObjectNode();
requestNode.put("name", "uuidVariable");
requestNode.put("value", someUUID2.toString());
requestNode.put("type", "uuid");

HttpPut httpPut = new HttpPut(
SERVER_URL_PREFIX + CmmnRestUrls.createRelativeResourceUrl(CmmnRestUrls.URL_CASE_INSTANCE_VARIABLE, caseInstance.getId(), "uuidVariable"));
httpPut.setEntity(new StringEntity(requestNode.toString()));
CloseableHttpResponse response = executeRequest(httpPut, HttpStatus.SC_OK);

assertThat(runtimeService.getVariable(caseInstance.getId(), "uuidVariable")).isEqualTo(someUUID2);

JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertThat(responseNode).isNotNull();
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS)
.isEqualTo("{"
+ " scope: 'global',"
+ " value: 'b2233abf-f84f-426f-b978-0d249b90cc45'"
+ "}");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
Expand Down Expand Up @@ -71,6 +72,7 @@ public void testGetTaskVariables() throws Exception {
caseVariables.put("dateProcVar", cal.getTime());
caseVariables.put("byteArrayProcVar", "Some raw bytes".getBytes());
caseVariables.put("overlappingVariable", "case-value");
caseVariables.put("uuidVar", UUID.fromString("a053505c-43c9-479f-ae01-5352ce559786"));
CaseInstance caseInstance = runtimeService.createCaseInstanceBuilder().caseDefinitionKey("oneHumanTaskCase").variables(caseVariables).start();

// Set local task variables, including one that has the same name as one
Expand All @@ -86,6 +88,7 @@ public void testGetTaskVariables() throws Exception {
taskVariables.put("dateTaskVar", cal.getTime());
taskVariables.put("byteArrayTaskVar", "Some raw bytes".getBytes());
taskVariables.put("overlappingVariable", "task-value");
taskVariables.put("uuidVar", UUID.fromString("a053505c-43c9-479f-ae01-5352ce559786"));
taskService.setVariablesLocal(task.getId(), taskVariables);

// Request all variables (no scope provides) which include global an local
Expand All @@ -97,7 +100,7 @@ public void testGetTaskVariables() throws Exception {
closeResponse(response);
assertThat(responseNode).isNotNull();
assertThat(responseNode.isArray()).isTrue();
assertThat(responseNode).hasSize(17);
assertThat(responseNode).hasSize(18);

// Overlapping variable should contain task-value AND be defined as "local"
assertThatJson(responseNode)
Expand Down Expand Up @@ -148,6 +151,9 @@ public void testGetTaskVariables() throws Exception {
+ " },"
+ " {"
+ " scope: 'local'"
+ " },"
+ " {"
+ " scope: 'local'"
+ " }"
+ "]");

Expand All @@ -160,7 +166,7 @@ public void testGetTaskVariables() throws Exception {
closeResponse(response);
assertThat(responseNode).isNotNull();
assertThat(responseNode.isArray()).isTrue();
assertThat(responseNode).hasSize(9);
assertThat(responseNode).hasSize(10);
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS, Option.IGNORING_ARRAY_ORDER, Option.IGNORING_EXTRA_ARRAY_ITEMS)
.isEqualTo("["
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* 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.common.rest.variable;

import java.util.UUID;

import org.flowable.common.engine.api.FlowableIllegalArgumentException;

public class UUIDRestVariableConverter implements RestVariableConverter {

@Override
public String getRestTypeName() {
return "uuid";
}

@Override
public Class<?> getVariableType() {
return UUID.class;
}

@Override
public Object getVariableValue(EngineRestVariable result) {
if (result.getValue() != null) {
if (!(result.getValue() instanceof String)) {
throw new FlowableIllegalArgumentException("Converter can only convert Strings");
}
return UUID.fromString((String) result.getValue());
}
return null;
}

@Override
public void convertVariableValue(Object variableValue, EngineRestVariable result) {
if (variableValue != null) {
if (!(variableValue instanceof UUID)) {
throw new FlowableIllegalArgumentException("Converter can only convert UUIDs");
}
result.setValue(((UUID)variableValue).toString());
} else {
result.setValue(null);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.flowable.common.rest.variable.RestVariableConverter;
import org.flowable.common.rest.variable.ShortRestVariableConverter;
import org.flowable.common.rest.variable.StringRestVariableConverter;
import org.flowable.common.rest.variable.UUIDRestVariableConverter;
import org.flowable.dmn.api.DecisionExecutionAuditContainer;
import org.flowable.dmn.api.DecisionServiceExecutionAuditContainer;
import org.flowable.dmn.api.DmnDecision;
Expand Down Expand Up @@ -302,6 +303,7 @@ protected void initializeVariableConverters() {
variableConverters.add(new InstantRestVariableConverter());
variableConverters.add(new LocalDateRestVariableConverter());
variableConverters.add(new LocalDateTimeRestVariableConverter());
variableConverters.add(new UUIDRestVariableConverter());
}

protected DmnRestUrlBuilder createUrlBuilder() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.flowable.common.rest.variable.RestVariableConverter;
import org.flowable.common.rest.variable.ShortRestVariableConverter;
import org.flowable.common.rest.variable.StringRestVariableConverter;
import org.flowable.common.rest.variable.UUIDRestVariableConverter;
import org.flowable.dmn.api.DmnDecision;
import org.flowable.engine.form.FormData;
import org.flowable.engine.form.FormProperty;
Expand Down Expand Up @@ -1528,6 +1529,7 @@ protected void initializeVariableConverters() {
variableConverters.add(new LocalDateRestVariableConverter());
variableConverters.add(new LocalDateTimeRestVariableConverter());
variableConverters.add(new JsonObjectRestVariableConverter(objectMapper));
variableConverters.add(new UUIDRestVariableConverter());
}

protected String formatUrl(String serverRootUrl, String[] fragments, Object... arguments) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
Expand Down Expand Up @@ -178,6 +179,31 @@ public void testGetProcessInstanceLocalDateTimeVariable() throws Exception {
+ "}");
}

@Test
@Deployment(resources = { "org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.testProcess.bpmn20.xml" })
public void testGetProcessInstanceUUIDVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
UUID someUUID = UUID.fromString("239969dd-3310-4068-b558-e4cbce5650ea");
runtimeService.setVariable(processInstance.getId(), "variable", someUUID);

CloseableHttpResponse response = executeRequest(
new HttpGet(
SERVER_URL_PREFIX + RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE, processInstance.getId(), "variable")),
HttpStatus.SC_OK);

JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());

closeResponse(response);
assertThat(responseNode).isNotNull();
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS)
.isEqualTo("{"
+ " name: 'variable',"
+ " type: 'uuid',"
+ " value: '" + someUUID + "'"
+ "}");
}

/**
* Test getting a process instance variable data. GET runtime/process-instances/{processInstanceId}/variables/{variableName}
*/
Expand Down Expand Up @@ -415,6 +441,40 @@ public void testUpdateLocalDateTimeProcessVariable() throws Exception {
+ "}");
}

@Test
@Deployment(resources = { "org/flowable/rest/service/api/runtime/ProcessInstanceVariableResourceTest.testProcess.bpmn20.xml" })
public void testUpdateUUIDProcessVariable() throws Exception {
UUID someUUID = UUID.fromString("239969dd-3310-4068-b558-e4cbce5650ea");
UUID someUUID2 = UUID.fromString("c5b16e77-0c15-4d7b-ac12-15352af76355");
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("oneTaskProcess", Collections.singletonMap("overlappingVariable", (Object) "processValue"));
runtimeService.setVariable(processInstance.getId(), "uuidVariable", someUUID);

// Update variable
ObjectNode requestNode = objectMapper.createObjectNode();
requestNode.put("name", "uuidVariable");
requestNode.put("value", someUUID2.toString());
requestNode.put("type", "uuid");

HttpPut httpPut = new HttpPut(
SERVER_URL_PREFIX + RestUrls.createRelativeResourceUrl(RestUrls.URL_PROCESS_INSTANCE_VARIABLE, processInstance.getId(), "uuidVariable"));
httpPut.setEntity(new StringEntity(requestNode.toString()));
CloseableHttpResponse response = executeRequest(httpPut, HttpStatus.SC_OK);

assertThat(runtimeService.getVariable(processInstance.getId(), "uuidVariable"))
.isEqualTo(someUUID2);

JsonNode responseNode = objectMapper.readTree(response.getEntity().getContent());
closeResponse(response);
assertThat(responseNode).isNotNull();
assertThatJson(responseNode)
.when(Option.IGNORING_EXTRA_FIELDS)
.isEqualTo("{"
+ " scope: null,"
+ " value: 'c5b16e77-0c15-4d7b-ac12-15352af76355'"
+ "}");
}

/**
* Test updating a single process variable using a binary stream. PUT runtime/process-instances/{processInstanceId}/variables/{variableName}
*/
Expand Down

0 comments on commit 3c0bf9c

Please sign in to comment.