ee.telekom.workflow.web.rest.RestController Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of workflow-web Show documentation
Show all versions of workflow-web Show documentation
Telekom-workflow-engine web console, REST services and JMX interface for monitoring and interacting with the running engine.
package ee.telekom.workflow.web.rest;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import com.google.gson.JsonObject;
import ee.telekom.workflow.core.common.UnexpectedStatusException;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceStatus;
import ee.telekom.workflow.facade.WorkflowEngineFacade;
import ee.telekom.workflow.facade.model.CreateWorkflowInstance;
import ee.telekom.workflow.facade.model.WorkItemState;
import ee.telekom.workflow.facade.model.WorkflowInstanceState;
import ee.telekom.workflow.graph.WorkItemStatus;
import ee.telekom.workflow.util.JsonUtil;
import ee.telekom.workflow.web.rest.form.UpdateInstanceStatusForm;
import ee.telekom.workflow.web.rest.model.HumanTaskModel;
import ee.telekom.workflow.web.rest.model.WorkflowInstanceRestModel;
import ee.telekom.workflow.web.util.JsonParserUtil;
/**
* Controller that implements a REST API.
*
* For details on individual API methods, please refer to the RequestMapping methods.
*
* Some method expect/support an argument/arguments/result field in the request body. Regardless of the deserializer (JSON: e.g. Jackson or GSON, XML:...), the values of these fields
* are forwarded to lower level engine methods such that maps are implemented using LinkedHashMap's and lists or arrays are implemented using ArrayList's. Furthermore, do not mix up
* plain JSON with the JSON base format that the engine internally uses to store, e.g., workflow instance attributes.
*
* {"arg1":{"refNum":1, "date":"22.09.2014"}, "arg2":[true, 1, "text"]} is forwarded by the following map:
* Map map = new LinkedHashMap<>();
* Map arg1 = new LinkedHashMap<>();
* arg1.put("refNum",1);
* arg1.put("date","22.09.2014");
* List> arg2 = new ArrayList<>();
* arg2.add(true);
* arg2.add(1);
* arg2.add("text");
* map.put("arg1",arg1);
* map.put("arg2",arg2);
*
*/
@Controller
@RequestMapping("/api")
public class RestController{
private static final Logger log = LoggerFactory.getLogger( RestController.class );
@Autowired
private WorkflowEngineFacade facade;
@Autowired
private RequestMappingHandlerAdapter adapter;
@PostConstruct
public void init(){
for (HttpMessageConverter converter : adapter.getMessageConverters()) {
if (converter instanceof GsonHttpMessageConverter) {
Gson gson = new GsonBuilder().serializeNulls().create();
((GsonHttpMessageConverter)converter).setGson(gson);
}
}
}
@ExceptionHandler()
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public String errorHandler( Exception e ){
log.error( "Rest exception occured", e );
return e.getMessage();
}
/**
* Creates a new workflow instance.
*
*
* Request: POST /workflowInstance {workflowName: "credit.step1", workflowVersion: null, arguments: {"arg1":{"refNum":1, "date":"22.09.2014"}, "arg2":[true, 1, "text"]}, label1: "one", label2: null }
* Response: OK, {refNum: 1, workflowName: "credit.step1", workflowVersion: null, label1: "one", label2: null, status: NEW}
*
*/
@RequestMapping(method = RequestMethod.POST, value = "/workflowInstance", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity create( @RequestBody String body ){
JsonObject root = JsonParserUtil.parseJson( body );
CreateWorkflowInstance request = new CreateWorkflowInstance();
request.setWorkflowName( JsonParserUtil.getAsNullSafeString( root, "workflowName" ) );
request.setWorkflowVersion( JsonParserUtil.getAsNullSafeInteger( root, "workflowVersion" ) );
request.setLabel1( JsonParserUtil.getAsNullSafeString( root, "label1" ) );
request.setLabel2( JsonParserUtil.getAsNullSafeString( root, "label2" ) );
request.setArguments( JsonUtil.deserializeHashMap( JsonParserUtil.toNullSafeJsonString( root, "arguments" ), String.class, Object.class ) );
facade.createWorkflowInstance( request );
WorkflowInstanceState woin = facade.findWorkflowInstance( request.getRefNum(), true );
return new ResponseEntity<>( createInstanceModel( woin ), HttpStatus.OK );
}
/**
* Finds a workflow instance.
*
*
* Request: GET /workflowInstance/1
* Response: OK {refNum: 1, workflowName: "credit.step1", workflowVersion: null, label1: "one", label2: null, status: NEW}
* Response: NOT_FOUND, if no such workflow instance exists
*
*/
@RequestMapping(method = RequestMethod.GET, value = "/workflowInstance/{woinRefNum}",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity find( @PathVariable long woinRefNum ){
WorkflowInstanceState woin = facade.findWorkflowInstance( woinRefNum, null );
if( woin == null ){
return new ResponseEntity<>( HttpStatus.NOT_FOUND );
}
else{
return new ResponseEntity<>( createInstanceModel( woin ), HttpStatus.OK );
}
}
/**
* Aborts, suspends or resumes a workflow instance.
*
*
* Request: POST /workflowInstance/{woinRefNum} {status: "ABORT"}
* Response: OK, {refNum: 1, workflowName: "credit.step1", workflowVersion: null, label1: "one", label2: null, status: ABORT}
* Response: NOT_FOUND, if no such workflow instance exists
* Response: CONFLICT, if the workflow's current status does not allow the requested status transition
* Response: BAD_REQUEST, if the new status is not allowed.
*
*/
@RequestMapping(method = RequestMethod.POST, value = "/workflowInstance/{woinRefNum}",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity updateIntance( @PathVariable long woinRefNum, @RequestBody UpdateInstanceStatusForm form ){
WorkflowInstanceState woin = facade.findWorkflowInstance( woinRefNum, true );
if( woin == null ){
return new ResponseEntity<>( HttpStatus.NOT_FOUND );
}
try{
if( WorkflowInstanceStatus.ABORT.name().equals( form.getStatus() ) ){
facade.abortWorkflowInstance( woinRefNum );
}
else if( WorkflowInstanceStatus.SUSPENDED.name().equals( form.getStatus() ) ){
facade.suspendWorkflowInstance( woinRefNum );
}
else if( WorkflowInstanceStatus.EXECUTING.name().equals( form.getStatus() ) ){
facade.resumeWorkflowInstance( woinRefNum );
}
else{
return new ResponseEntity<>( HttpStatus.BAD_REQUEST );
}
}
catch( UnexpectedStatusException e ){
return new ResponseEntity<>( HttpStatus.CONFLICT );
}
woin = facade.findWorkflowInstance( woinRefNum, true );
return new ResponseEntity<>( createInstanceModel( woin ), HttpStatus.OK );
}
/**
* Notifies all waiting signal work items of the given workflow instance and the given signal name.
*
* Technically, sets the work item's result value and updates the status to EXECUTED.
*
*
* Request: POST /workflowInstance/1/signal/invoice {argument: {refNum:3, invoiceAmount: "10 Euro"}}
* Response: NO_CONTENT
*
*/
@RequestMapping(method = RequestMethod.POST, value = "/workflowInstance/{woinRefNum}/signal/{signal}",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity sendSignal( @PathVariable long woinRefNum, @PathVariable String signal, @RequestBody String argument ){
facade.sendSignalToWorkflowInstance( woinRefNum, signal, JsonUtil.deserialize( argument ) );
return new ResponseEntity<>( HttpStatus.NO_CONTENT );
}
/**
* (Un)assigns a human task to a user or submits its result.
*
* In order to unassign a human task from a user, set an empty user name.
*
* Technically, assigning means updating the user field while submitting means to update the status field to EXECUTED and setting the result value.
*
*
* Request: POST /workflowInstance/1/humanTask/2 {result: {resolution: "completed"}}
* Response: OK, {refNum:3, woinRefNum:1, tokenId:2, status:EXECUTED, role:"auditor", user:"hans", arguments:{task:"audit customer 500"}, result: {resolution: "completed"}}
* Response: NOT_FOUND, if no such human task exists
* Response: CONFLICT, if the human task's status does not allow the requested status transition
*
*/
@RequestMapping(method = RequestMethod.POST, value = "/workflowInstance/{woinRefNum}/humanTask/{tokenId}",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity updateHumanTask( @PathVariable long woinRefNum, @PathVariable int tokenId, @RequestBody String body ){
WorkItemState woit = facade.findActiveWorkItemByTokenId( woinRefNum, tokenId );
if( woit == null ){
return new ResponseEntity<>( HttpStatus.NOT_FOUND );
}
try{
JsonObject root = JsonParserUtil.parseJson( body );
String user = JsonParserUtil.getAsNullSafeString( root, "user" );
if( user != null ){
String userName = user.trim().isEmpty() ? null : user.trim();
facade.assignHumanTask( woit.getRefNum(), userName );
}
else{
facade.submitHumanTask( woit.getRefNum(), JsonUtil.deserialize( JsonParserUtil.toNullSafeJsonString( root, "result" ) ) );
}
}
catch( UnexpectedStatusException e ){
return new ResponseEntity<>( HttpStatus.CONFLICT );
}
woit = facade.findWorkItem( woit.getRefNum(), true );
return new ResponseEntity<>( createHumanTaskModel( woit ), HttpStatus.OK );
}
/**
* Searches workflow instance's that match the given criteria.
*
*
* Request: GET /workflowInstance/search?label1=aClientId
* Response: OK, [{refNum: 1, workflowName: "credit.step1", workflowVersion: null, label1: "aClientId", label2: null, status: NEW}, {refNum: 2, workflowName: "credit.client.step1", workflowVersion: null, label1: "aClientId", label2: "anotherIdentificator", status: NEW}]
*
*/
@RequestMapping(method = RequestMethod.GET, value = "/workflowInstance/search",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity> findInstances( @RequestParam(required = false) String label1,
@RequestParam(required = false) String label2,
@RequestParam(required = false) Boolean activeOnly ){
if( activeOnly == null ){
activeOnly = true;
}
label1 = StringUtils.trimToNull( label1 );
label2 = StringUtils.trimToNull( label2 );
List woins = facade.findWorkflowInstancesByLabels( label1, label2, activeOnly );
return new ResponseEntity<>( createInstancesModel( woins ), HttpStatus.OK );
}
/**
* Searches active human tasks by role and/or user.
*
*
* Request: GET /humanTask/search?role=auditor&user=hans
* Response: OK, [{refNum:3, woinRefNum:1, tokenId:2, status:EXECUTED, role:"auditor", user:"hans", arguments:{task:"audit customer 500"}}]
*
*/
@RequestMapping(method = RequestMethod.GET, value = "/humanTask/search",
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_XML_VALUE})
public ResponseEntity> findActiveHumanTasks( @RequestParam(required = false) String role,
@RequestParam(required = false) String user ){
if( StringUtils.isBlank( role ) && StringUtils.isBlank( user ) ){
return new ResponseEntity<>( HttpStatus.BAD_REQUEST );
}
List woits = facade.findActiveHumanTasksByRoleAndUser( role, user );
return new ResponseEntity<>( createHumanTasksModel( woits ), HttpStatus.OK );
}
///// PRIVATE METHODS /////
private WorkflowInstanceRestModel createInstanceModel( WorkflowInstanceState woin ){
return new WorkflowInstanceRestModel( woin.getRefNum(),
woin.getWorkflowName(),
woin.getWorkflowVersion(),
woin.getLabel1(),
woin.getLabel2(),
WorkflowInstanceStatus.valueOf( woin.getStatus() ) );
}
private List createInstancesModel( List woins ){
List result = new ArrayList<>();
for( WorkflowInstanceState woin : woins ){
result.add( createInstanceModel( woin ) );
}
return result;
}
private List createHumanTasksModel( List woits ){
List result = new ArrayList<>();
for( WorkItemState woit : woits ){
result.add( createHumanTaskModel( woit ) );
}
return result;
}
private HumanTaskModel createHumanTaskModel( WorkItemState woit ){
return new HumanTaskModel( woit.getRefNum(),
woit.getWoinRefNum(),
woit.getTokenId(),
WorkItemStatus.valueOf( woit.getStatus() ),
woit.getRole(),
woit.getUserName(),
woit.getArguments(),
woit.getResult() );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy