提交 01c1ebc0 编写于 作者: F Frederik Heremans

Added task-variable delete and update (both simple and binary) to REST

上级 2c26cc21
......@@ -13,15 +13,29 @@
package org.activiti.rest.api.task;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.api.SecuredResource;
import org.activiti.rest.api.engine.variable.RestVariable;
import org.activiti.rest.api.engine.variable.RestVariable.RestVariableScope;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.restlet.data.Status;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.Representation;
import org.restlet.resource.ResourceException;
/**
......@@ -114,4 +128,120 @@ public class BaseTaskVariableResource extends SecuredResource {
}
return task;
}
protected RestVariable setBinaryVariable(Representation representation, Task task, boolean isNew) {
try {
RestletFileUpload upload = new RestletFileUpload(new DiskFileItemFactory());
List<FileItem> items = upload.parseRepresentation(representation);
String variableScope = null;
String variableName = null;
String variableType = null;
FileItem uploadItem = null;
for (FileItem fileItem : items) {
if(fileItem.isFormField()) {
if("scope".equals(fileItem.getFieldName())) {
variableScope = fileItem.getString("UTF-8");
} else if("name".equals(fileItem.getFieldName())) {
variableName = fileItem.getString("UTF-8");
} else if("type".equals(fileItem.getFieldName())) {
variableType = fileItem.getString("UTF-8");
}
} else if(fileItem.getName() != null) {
uploadItem = fileItem;
}
}
// Validate input and set defaults
if(uploadItem == null) {
throw new ActivitiIllegalArgumentException("No file content was found in request body.");
}
if(variableName == null) {
throw new ActivitiIllegalArgumentException("No variable name was found in request body.");
}
if(variableType != null) {
if(!RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE.equals(variableType) && !RestResponseFactory.SERIALIZABLE_VARIABLE_TYPE.equals(variableType)) {
throw new ActivitiIllegalArgumentException("Only 'binary' and 'serializable' are supported as variable type.");
}
} else {
variableType = RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE;
}
RestVariableScope scope = RestVariableScope.LOCAL;
if(variableScope != null) {
scope = RestVariable.getScopeFromString(variableScope);
}
if(variableType.equals(RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE)) {
// Use raw bytes as variable value
ByteArrayOutputStream variableOutput = new ByteArrayOutputStream(((Long)uploadItem.getSize()).intValue());
IOUtils.copy(uploadItem.getInputStream(), variableOutput);
setVariable(task, variableName, variableOutput.toByteArray(), scope, isNew);
} else {
// Try deserializing the object
ObjectInputStream stream = new ObjectInputStream(uploadItem.getInputStream());
Object value = stream.readObject();
setVariable(task, variableName, value, scope, isNew);
stream.close();
}
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createBinaryRestVariable(this, variableName, scope, variableType, task.getId(), null);
} catch(FileUploadException fue) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, fue);
} catch (IOException ioe) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, ioe);
} catch (ClassNotFoundException ioe) {
throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE.getCode(), "The provided body contains a serialized object for which the class is nog found: " + ioe.getMessage(), null, null);
}
}
protected RestVariable setSimpleVariable(RestVariable restVariable, Task task, boolean isNew) {
if(restVariable.getName() == null) {
throw new ActivitiIllegalArgumentException("Variable name is required");
}
// Figure out scope, revert to local is omitted
RestVariableScope scope = restVariable.getVariableScope();
if(scope == null) {
scope = RestVariableScope.LOCAL;
}
Object actualVariableValue = getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.getVariableValue(restVariable);
setVariable(task, restVariable.getName(), actualVariableValue, scope, isNew);
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createRestVariable(this, restVariable.getName(), actualVariableValue, scope, task.getId(), null, false);
}
protected void setVariable(Task task, String name, Object value, RestVariableScope scope, boolean isNew) {
// Create can only be done on new variables. Existing variables should be updated using PUT
boolean hasVariable = hasVariableOnScope(task, name, scope);
if(isNew && hasVariable) {
throw new ResourceException(new Status(Status.CLIENT_ERROR_CONFLICT.getCode(), "Variable '" + name + "' is already present on task '" + task.getId() + "'.", null, null));
}
if(!isNew && !hasVariable) {
throw new ResourceException(new Status(Status.CLIENT_ERROR_NOT_FOUND.getCode(), "Task '" + task.getId() + "' doesn't have a variable with name: '"+ name + "'.", null, null));
}
if(scope == RestVariableScope.LOCAL) {
ActivitiUtil.getTaskService().setVariableLocal(task.getId(), name, value);
} else {
if(task.getExecutionId() != null) {
// Explicitly set on execution, setting non-local variable on task will override local-variable if exists
ActivitiUtil.getRuntimeService().setVariable(task.getExecutionId(), name, value);
} else {
// Standalone task, no global variables possible
throw new ActivitiIllegalArgumentException("Cannot set global variable '" + name + "' on task '" + task.getId() +"', task is not part of process.");
}
}
}
}
......@@ -13,9 +13,7 @@
package org.activiti.rest.api.task;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
......@@ -24,17 +22,11 @@ import java.util.Map;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.RestResponseFactory;
import org.activiti.rest.api.engine.variable.RestVariable;
import org.activiti.rest.api.engine.variable.RestVariable.RestVariableScope;
import org.activiti.rest.application.ActivitiRestServicesApplication;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.io.IOUtils;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.Representation;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
......@@ -81,7 +73,7 @@ public class TaskVariableCollectionResource extends BaseTaskVariableResource {
Task task = getTaskFromRequest();
Object result = null;
if(MediaType.MULTIPART_FORM_DATA.isCompatible(representation.getMediaType())) {
result = createBinaryVariable(representation, task);
result = setBinaryVariable(representation, task, true);
} else {
// Since we accept both an array of RestVariables and a single RestVariable, we need to inspect the
// body before passing on to the converterService
......@@ -94,7 +86,7 @@ public class TaskVariableCollectionResource extends BaseTaskVariableResource {
throw new ActivitiIllegalArgumentException("Request didn't cantain a list of variables to create.");
}
for(RestVariable var : restVariables) {
variables.add(createSimpleVariable(var, task));
variables.add(setSimpleVariable(var, task, true));
}
} catch (IOException ioe) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, ioe);
......@@ -120,116 +112,6 @@ public class TaskVariableCollectionResource extends BaseTaskVariableResource {
}
}
protected RestVariable createBinaryVariable(Representation representation, Task task) {
try {
RestletFileUpload upload = new RestletFileUpload(new DiskFileItemFactory());
List<FileItem> items = upload.parseRepresentation(representation);
String variableScope = null;
String variableName = null;
String variableType = null;
FileItem uploadItem = null;
for (FileItem fileItem : items) {
if(fileItem.isFormField()) {
if("scope".equals(fileItem.getFieldName())) {
variableScope = fileItem.getString("UTF-8");
} else if("name".equals(fileItem.getFieldName())) {
variableName = fileItem.getString("UTF-8");
} else if("type".equals(fileItem.getFieldName())) {
variableType = fileItem.getString("UTF-8");
}
} else if(fileItem.getName() != null) {
uploadItem = fileItem;
}
}
// Validate input and set defaults
if(uploadItem == null) {
throw new ActivitiIllegalArgumentException("No file content was found in request body.");
}
if(variableName == null) {
throw new ActivitiIllegalArgumentException("No variable name was found in request body.");
}
if(variableType != null) {
if(!RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE.equals(variableType) && !RestResponseFactory.SERIALIZABLE_VARIABLE_TYPE.equals(variableType)) {
throw new ActivitiIllegalArgumentException("Only 'binary' and 'serializable' are supported as variable type.");
}
} else {
variableType = RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE;
}
RestVariableScope scope = RestVariableScope.LOCAL;
if(variableScope != null) {
scope = RestVariable.getScopeFromString(variableScope);
}
if(variableType.equals(RestResponseFactory.BYTE_ARRAY_VARIABLE_TYPE)) {
// Use raw bytes as variable value
ByteArrayOutputStream variableOutput = new ByteArrayOutputStream(((Long)uploadItem.getSize()).intValue());
IOUtils.copy(uploadItem.getInputStream(), variableOutput);
setVariable(task, variableName, variableOutput.toByteArray(), scope);
} else {
// Try deserializing the object
ObjectInputStream stream = new ObjectInputStream(uploadItem.getInputStream());
Object value = stream.readObject();
setVariable(task, variableName, value, scope);
stream.close();
}
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createBinaryRestVariable(this, variableName, scope, variableType, task.getId(), null);
} catch(FileUploadException fue) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, fue);
} catch (IOException ioe) {
throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, ioe);
} catch (ClassNotFoundException ioe) {
throw new ResourceException(Status.CLIENT_ERROR_UNSUPPORTED_MEDIA_TYPE.getCode(), "The provided body contains a serialized object for which the class is nog found: " + ioe.getMessage(), null, null);
}
}
protected RestVariable createSimpleVariable(RestVariable restVariable, Task task) {
if(restVariable.getName() == null) {
throw new ActivitiIllegalArgumentException("Variable name is required");
}
// Figure out scope, revert to local is omitted
RestVariableScope scope = restVariable.getVariableScope();
if(scope == null) {
scope = RestVariableScope.LOCAL;
}
Object actualVariableValue = getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.getVariableValue(restVariable);
setVariable(task, restVariable.getName(), actualVariableValue, scope);
return getApplication(ActivitiRestServicesApplication.class).getRestResponseFactory()
.createRestVariable(this, restVariable.getName(), actualVariableValue, scope, task.getId(), null, false);
}
protected void setVariable(Task task, String name, Object value, RestVariableScope scope) {
// Create can only be done on new variables. Existing variables should be updated using PUT
if(hasVariableOnScope(task, name, scope)) {
throw new ResourceException(new Status(Status.CLIENT_ERROR_CONFLICT.getCode(), "Variable '" + name + "' is already present on task '" + task.getId() + "'.", null, null));
}
if(scope == RestVariableScope.LOCAL) {
ActivitiUtil.getTaskService().setVariableLocal(task.getId(), name, value);
} else {
if(task.getExecutionId() != null) {
// Explicitly set on execution, setting non-local variable on task will override local-variable if exists
ActivitiUtil.getRuntimeService().setVariable(task.getExecutionId(), name, value);
} else {
// Standalone task, no global variables possible
throw new ActivitiIllegalArgumentException("Cannot set global variable '" + name + "' on task '" + task.getId() +"', task is not part of process.");
}
}
}
protected void addLocalVariables(Task task, Map<String, RestVariable> variableMap) {
Map<String, Object> rawVariables = ActivitiUtil.getTaskService().getVariablesLocal(task.getId());
......
......@@ -13,8 +13,22 @@
package org.activiti.rest.api.task;
import java.io.IOException;
import org.activiti.engine.ActivitiIllegalArgumentException;
import org.activiti.engine.ActivitiObjectNotFoundException;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntity;
import org.activiti.engine.task.Task;
import org.activiti.rest.api.ActivitiUtil;
import org.activiti.rest.api.engine.variable.RestVariable;
import org.activiti.rest.api.engine.variable.RestVariable.RestVariableScope;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Put;
import org.restlet.resource.ResourceException;
/**
......@@ -29,4 +43,70 @@ public class TaskVariableResource extends BaseTaskVariableResource {
return getVariableFromRequest(false);
}
@Put
public RestVariable updateVariable(Representation representation) {
if (authenticate() == false)
return null;
String variableName = getAttribute("variableName");
if (variableName == null) {
throw new ActivitiIllegalArgumentException("The variableName cannot be null");
}
Task task = getTaskFromRequest();
RestVariable result = null;
if(MediaType.MULTIPART_FORM_DATA.isCompatible(representation.getMediaType())) {
result = setBinaryVariable(representation, task, false);
if(!result.getName().equals(variableName)) {
throw new ActivitiIllegalArgumentException("Variable name in the body should be equal to the name used in the requested URL.");
}
} else {
try {
RestVariable restVariable = getConverterService().toObject(representation, RestVariable.class, this);
if(!restVariable.getName().equals(variableName)) {
throw new ActivitiIllegalArgumentException("Variable name in the body should be equal to the name used in the requested URL.");
}
result = setSimpleVariable(restVariable, task, false);
} catch(IOException ioe) {
throw new ResourceException(Status.SERVER_ERROR_INTERNAL, ioe);
}
}
return result;
}
@Delete
public void deleteVariable() {
if (authenticate() == false)
return;
Task task = getTaskFromRequest();
String variableName = getAttribute("variableName");
if (variableName == null) {
throw new ActivitiIllegalArgumentException("The variableName cannot be null");
}
// Determine scope
String scopeString = getQueryParameter("scope", getQuery());
RestVariableScope scope = RestVariableScope.LOCAL;
if(scopeString != null) {
scope = RestVariable.getScopeFromString(scopeString);
}
if(!hasVariableOnScope(task, variableName, scope)) {
throw new ActivitiObjectNotFoundException("Task '" + task.getId() + "' doesn't have a variable '" + variableName + "' in scope " + scope.name().toLowerCase(), VariableInstanceEntity.class);
}
if(scope == RestVariableScope.LOCAL) {
ActivitiUtil.getTaskService().removeVariableLocal(task.getId(), variableName);
} else {
// Safe to use executionId, as the hasVariableOnScope whould have stopped a global-var update on standalone task
ActivitiUtil.getRuntimeService().removeVariable(task.getExecutionId(), variableName);
}
setStatus(Status.SUCCESS_NO_CONTENT);
}
}
......@@ -13,16 +13,22 @@
package org.activiti.rest.api.runtime;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.Deployment;
import org.activiti.rest.BaseRestTestCase;
import org.activiti.rest.HttpMultipartRepresentation;
import org.activiti.rest.api.RestUrls;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ObjectNode;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
......@@ -248,6 +254,195 @@ public class TaskVariableResourceTest extends BaseRestTestCase {
}
}
/**
* Test deleting a single task variable in all scopes, including "not found" check.
*
* DELETE runtime/tasks/{taskId}/variables/{variableName}
*/
@Deployment
public void testDeleteTaskVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess",
Collections.singletonMap("overlappingVariable", (Object) "processValue"));
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.setVariableLocal(task.getId(), "overlappingVariable", "taskValue");
taskService.setVariableLocal(task.getId(), "anotherTaskVariable", "taskValue");
// Delete variable without scope, local should be presumed -> local removed and global should be retained
ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "overlappingVariable"));
Representation response = client.delete();
assertEquals(Status.SUCCESS_NO_CONTENT, client.getResponse().getStatus());
assertEquals(0L, response.getSize());
assertFalse(taskService.hasVariableLocal(task.getId(), "overlappingVariable"));
assertTrue(taskService.hasVariable(task.getId(), "overlappingVariable"));
// Delete local scope variable
client.release();
client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "anotherTaskVariable") + "?scope=local");
response = client.delete();
assertEquals(Status.SUCCESS_NO_CONTENT, client.getResponse().getStatus());
assertEquals(0L, response.getSize());
assertFalse(taskService.hasVariableLocal(task.getId(), "anotherTaskVariable"));
// Delete global scope variable
assertTrue(taskService.hasVariable(task.getId(), "overlappingVariable"));
client.release();
client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "overlappingVariable") + "?scope=global");
response = client.delete();
assertEquals(Status.SUCCESS_NO_CONTENT, client.getResponse().getStatus());
assertEquals(0L, response.getSize());
assertFalse(taskService.hasVariable(task.getId(), "overlappingVariable"));
// Run the same delete again, variable is not there so 404 should be returned
client.release();
try {
client.delete();
fail("Exception expected");
} catch(ResourceException expected) {
assertEquals(Status.CLIENT_ERROR_NOT_FOUND, expected.getStatus());
assertEquals("Task '" + task.getId() + "' doesn't have a variable 'overlappingVariable' in scope global", expected.getStatus().getDescription());
}
}
/**
* Test updating a single task variable in all scopes, including "not found" check.
*
* PUT runtime/tasks/{taskId}/variables/{variableName}
*/
@Deployment
public void testUpdateTaskVariable() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess",
Collections.singletonMap("overlappingVariable", (Object) "processValue"));
Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.setVariableLocal(task.getId(), "overlappingVariable", "taskValue");
// Update variable without scope, local should be presumed -> local updated and global should be retained
ObjectNode requestNode = objectMapper.createObjectNode();
requestNode.put("name", "overlappingVariable");
requestNode.put("value", "updatedLocalValue");
requestNode.put("type", "string");
ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "overlappingVariable"));
Representation response = client.put(requestNode);
assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
JsonNode responseNode = objectMapper.readTree(response.getStream());
assertNotNull(responseNode);
assertEquals("updatedLocalValue", responseNode.get("value").asText());
assertEquals("local", responseNode.get("scope").asText());
// Check local value is changed in engine and global one remains unchanged
assertEquals("updatedLocalValue", taskService.getVariableLocal(task.getId(), "overlappingVariable"));
assertEquals("processValue", runtimeService.getVariable(task.getExecutionId(), "overlappingVariable"));
// Update variable in local scope
response.release();
requestNode = objectMapper.createObjectNode();
requestNode.put("name", "overlappingVariable");
requestNode.put("value", "updatedLocalValueOnceAgain");
requestNode.put("type", "string");
requestNode.put("scope", "local");
response = client.put(requestNode);
assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
responseNode = objectMapper.readTree(response.getStream());
assertNotNull(responseNode);
assertEquals("updatedLocalValueOnceAgain", responseNode.get("value").asText());
assertEquals("local", responseNode.get("scope").asText());
// Check local value is changed in engine and global one remains unchanged
assertEquals("updatedLocalValueOnceAgain", taskService.getVariableLocal(task.getId(), "overlappingVariable"));
assertEquals("processValue", runtimeService.getVariable(task.getExecutionId(), "overlappingVariable"));
// Update variable in global scope
response.release();
requestNode = objectMapper.createObjectNode();
requestNode.put("name", "overlappingVariable");
requestNode.put("value", "updatedInGlobalScope");
requestNode.put("type", "string");
requestNode.put("scope", "global");
response = client.put(requestNode);
assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
responseNode = objectMapper.readTree(response.getStream());
assertNotNull(responseNode);
assertEquals("updatedInGlobalScope", responseNode.get("value").asText());
assertEquals("global", responseNode.get("scope").asText());
// Check global value is changed in engine and local one remains unchanged
assertEquals("updatedLocalValueOnceAgain", taskService.getVariableLocal(task.getId(), "overlappingVariable"));
assertEquals("updatedInGlobalScope", runtimeService.getVariable(task.getExecutionId(), "overlappingVariable"));
// Try updating with mismatch between URL and body variableName unexisting property
try {
requestNode.put("name", "unexistingVariable");
client.put(requestNode);
fail("Exception expected");
} catch(ResourceException expected) {
assertEquals(Status.CLIENT_ERROR_BAD_REQUEST, expected.getStatus());
assertEquals("Variable name in the body should be equal to the name used in the requested URL.", expected.getStatus().getDescription());
}
// Try updating unexisting property
try {
client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "unexistingVariable"));
requestNode.put("name", "unexistingVariable");
client.put(requestNode);
fail("Exception expected");
} catch(ResourceException expected) {
assertEquals(Status.CLIENT_ERROR_NOT_FOUND, expected.getStatus());
assertEquals("Task '" + task.getId() + "' doesn't have a variable with name: 'unexistingVariable'.", expected.getStatus().getDescription());
}
}
/**
* Test updating a single task variable using a binary stream.
* PUT runtime/tasks/{taskId}/variables/{variableName}
*/
public void testUpdateBinaryTaskVariable() throws Exception {
try {
Task task = taskService.newTask();
taskService.saveTask(task);
taskService.setVariable(task.getId(), "binaryVariable", "Original value".getBytes());
InputStream binaryContent = new ByteArrayInputStream("This is binary content".getBytes());
// Add name, type and scope
Map<String, String> additionalFields = new HashMap<String, String>();
additionalFields.put("name", "binaryVariable");
additionalFields.put("type", "binary");
additionalFields.put("scope", "local");
// Upload a valid BPMN-file using multipart-data
Representation uploadRepresentation = new HttpMultipartRepresentation("value",
binaryContent, additionalFields);
ClientResource client = getAuthenticatedClient(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE, task.getId(), "binaryVariable"));
Representation response = client.put(uploadRepresentation);
assertEquals(Status.SUCCESS_OK, client.getResponse().getStatus());
JsonNode responseNode = objectMapper.readTree(response.getStream());
assertNotNull(responseNode);
assertEquals("binaryVariable", responseNode.get("name").asText());
assertTrue(responseNode.get("value").isNull());
assertEquals("local", responseNode.get("scope").asText());
assertEquals("binary", responseNode.get("type").asText());
assertNotNull(responseNode.get("valueUrl").isNull());
assertTrue(responseNode.get("valueUrl").asText().endsWith(RestUrls.createRelativeResourceUrl(RestUrls.URL_TASK_VARIABLE_DATA, task.getId(), "binaryVariable")));
// Check actual value of variable in engine
Object variableValue = taskService.getVariableLocal(task.getId(), "binaryVariable");
assertNotNull(variableValue);
assertTrue(variableValue instanceof byte[]);
assertEquals("This is binary content", new String((byte[])variableValue));
} finally {
// Clean adhoc-tasks even if test fails
List<Task> tasks = taskService.createTaskQuery().list();
for (Task task : tasks) {
taskService.deleteTask(task.getId(), true);
}
}
}
protected String getMediaType(ClientResource client) {
Form headers = (Form) client.getResponseAttributes().get(HeaderConstants.ATTRIBUTE_HEADERS);
return headers.getFirstValue(HeaderConstants.HEADER_CONTENT_TYPE);
......
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="OneTaskCategory">
<process id="oneTaskProcess" name="The One Task Process">
<documentation>One task process description</documentation>
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="processTask" />
<userTask id="processTask" name="Process task" activiti:candidateUsers="kermit" activiti:candidateGroups="sales">
<documentation>Process task description</documentation>
</userTask>
<sequenceFlow id="flow2" sourceRef="processTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
</definitions>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="OneTaskCategory">
<process id="oneTaskProcess" name="The One Task Process">
<documentation>One task process description</documentation>
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="processTask" />
<userTask id="processTask" name="Process task" activiti:candidateUsers="kermit" activiti:candidateGroups="sales">
<documentation>Process task description</documentation>
</userTask>
<sequenceFlow id="flow2" sourceRef="processTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
</definitions>
\ No newline at end of file
......@@ -2316,6 +2316,255 @@ The request body should be an array containing one or more JSON-objects represen
</table>
</para>
</section>
<section>
<title>Update an existing variable on a task</title>
<para>
<programlisting>PUT runtime/tasks/{taskId}/variables/{variableName}</programlisting>
</para>
<para>
<table>
<title>URL parameters</title>
<tgroup cols='3'>
<thead>
<row>
<entry>Parameter</entry>
<entry>Required</entry>
<entry>Value</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>taskId</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The id of the task to update the variable for.</entry>
</row>
<row>
<entry>variableName</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The name of the variable to update.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
<emphasis role="bold">Request body for updating simple (non-binary) variables:</emphasis>
<programlisting>
{
"name" : "myTaskVariable",
"scope" : "local",
"type" : "string",
"value" : "Hello my friend"
}</programlisting>
<itemizedlist>
<listitem>
<para><literal>name</literal>: Required name of the variable</para>
</listitem>
<listitem>
<para><literal>scope</literal>: Scope of variable that is updated. If omitted, <literal>local</literal> is assumed.</para>
</listitem>
<listitem>
<para><literal>type</literal>: Type of variable that is updated. If omitted, reverts to raw JSON-value type (string, boolean, integer or double).</para>
</listitem>
<listitem>
<para><literal>value</literal>: Variable value.</para>
</listitem>
</itemizedlist>
More information about the variable format can be found in <link linkend="restVariables">the REST variables section</link>.
</para>
<para>
<emphasis role="bold">Success response body:</emphasis>
<programlisting>
{
"name" : "myTaskVariable",
"scope" : "local",
"type" : "string",
"value" : "Hello my friend"
}</programlisting>
<table>
<title>Response codes</title>
<tgroup cols='2'>
<thead>
<row>
<entry>Response code</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>200</entry>
<entry>Indicates the variables was updated and the result is returned.</entry>
</row>
<row>
<entry>400</entry>
<entry>Indicates the name of a variable to update was missing or that an attempt is done to update a variable on a standalone task (without a process associated) with scope <literal>global</literal>. Status message provides additional information.</entry>
</row>
<row>
<entry>404</entry>
<entry>Indicates the requested task was not found or the task doesn't have a variable with the given name in the given scope. Status message contains additional information about the error.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title>Updating a binary variable on a task</title>
<para>
<programlisting>PUT runtime/tasks/{taskId}/variables/{variableName}</programlisting>
</para>
<para>
<table>
<title>URL parameters</title>
<tgroup cols='3'>
<thead>
<row>
<entry>Parameter</entry>
<entry>Required</entry>
<entry>Value</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>taskId</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The id of the task to update the variable for.</entry>
</row>
<row>
<entry>variableName</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The name of the variable to update.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
<emphasis role="bold">Request body:</emphasis>
The request should be of type <literal>multipart/form-data</literal>. There should be a single file-part included with the binary value of the variable. On top of that, the folowing additionl form-fields can be present:
<itemizedlist>
<listitem>
<para><literal>name</literal>: Required name of the variable.</para>
</listitem>
<listitem>
<para><literal>scope</literal>: Scope of variable that is updated. If omitted, <literal>local</literal> is assumed.</para>
</listitem>
<listitem>
<para><literal>type</literal>: Type of variable that is updated. If omitted, <literal>binary</literal> is assumed and the binary data in the request will be stored as an array of bytes.</para>
</listitem>
</itemizedlist>
</para>
<para>
<emphasis role="bold">Success response body:</emphasis>
<programlisting>
{
"name" : "binaryVariable",
"scope" : "local",
"type" : "binary",
"value" : null,
"valueUrl" : "http://.../runtime/tasks/123/variables/binaryVariable/data"
}</programlisting>
<table>
<title>Response codes</title>
<tgroup cols='2'>
<thead>
<row>
<entry>Response code</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>200</entry>
<entry>Indicates the variable was updated and the result is returned.</entry>
</row>
<row>
<entry>400</entry>
<entry>Indicates the name of the variable to update was missing or that an attempt is done to update a variable on a standalone task (without a process associated) with scope <literal>global</literal>. Status message provides additional information.</entry>
</row>
<row>
<entry>404</entry>
<entry>Indicates the requested task was not found or the variable to update doesn't exist for the given task in the given scope.</entry>
</row>
<row>
<entry>415</entry>
<entry>Indicates the serializable data contains an object for which no class is present in the JVM running the Activiti engine and therefor cannot be deserialized.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title>Delete a variable on a task</title>
<para>
<programlisting>DELETE runtime/tasks/{taskId}/variables/{variableName}?scope={scope}</programlisting>
</para>
<para>
<table>
<title>URL parameters</title>
<tgroup cols='3'>
<thead>
<row>
<entry>Parameter</entry>
<entry>Required</entry>
<entry>Value</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>taskId</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The id of the task the variable to delete belongs to.</entry>
</row>
<row>
<entry>variableName</entry>
<entry>Yes</entry>
<entry>String</entry>
<entry>The name of the variable to delete.</entry>
</row>
<row>
<entry>scope</entry>
<entry>No</entry>
<entry>String</entry>
<entry>Scope of variable to delete in. Can be either <literal>local</literal> or <literal>global</literal>. If omitted, <literal>local</literal> is assumed.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
<para>
<table>
<title>Response codes</title>
<tgroup cols='2'>
<thead>
<row>
<entry>Response code</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>204</entry>
<entry>Indicates the task variable was found and has been deleted. Response-body is intentionally empty.</entry>
</row>
<row>
<entry>404</entry>
<entry>Indicates the requested task was not found or the task doesn't have a variable with the given name. Status message contains additional information about the error.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
</section>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册