TSK-22_start_working_on_a_task

This commit is contained in:
BerndBreier 2017-12-11 08:45:25 +01:00 committed by holgerhagen
parent 5f326dd9bf
commit d566b54809
18 changed files with 125 additions and 39 deletions

View File

@ -1,6 +1,8 @@
package pro.taskana; package pro.taskana;
import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
@ -19,11 +21,11 @@ public class ExampleBootstrap {
private TaskanaEjb taskanaEjb; private TaskanaEjb taskanaEjb;
@PostConstruct @PostConstruct
public void init(@Observes @Initialized(ApplicationScoped.class) Object init) throws TaskNotFoundException, NotAuthorizedException, WorkbasketNotFoundException, ClassificationNotFoundException { public void init(@Observes @Initialized(ApplicationScoped.class) Object init) throws TaskNotFoundException, NotAuthorizedException, WorkbasketNotFoundException, ClassificationNotFoundException, InvalidStateException, InvalidOwnerException {
System.out.println("---------------------------> Start App"); System.out.println("---------------------------> Start App");
Task task = taskanaEjb.getTaskService().createTask(new Task()); Task task = taskanaEjb.getTaskService().createTask(new Task());
System.out.println("---------------------------> Task started: " + task.getId()); System.out.println("---------------------------> Task started: " + task.getId());
taskanaEjb.getTaskService().claim(task.getId(), "John Doe"); taskanaEjb.getTaskService().claim(task.getId());
System.out.println( System.out.println(
"---------------------------> Task claimed: " "---------------------------> Task claimed: "
+ taskanaEjb.getTaskService().getTaskById(task.getId()).getOwner()); + taskanaEjb.getTaskService().getTaskById(task.getId()).getOwner());

View File

@ -1,6 +1,8 @@
package pro.taskana; package pro.taskana;
import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
@ -18,15 +20,28 @@ import java.util.List;
public interface TaskService { public interface TaskService {
/** /**
* Claim an existing task. * Claim an existing task for the current user.
* @param id * @param id
* task id * task id
* @param userName
* user who claims the task
* @return modified claimed Task * @return modified claimed Task
* @throws TaskNotFoundException TODO * @throws TaskNotFoundException if the task with id was not found
* @throws InvalidStateException if the task state is not ready
* @throws InvalidOwnerException if the task is claimed by another user
*/ */
Task claim(String id, String userName) throws TaskNotFoundException; Task claim(String id) throws TaskNotFoundException, InvalidStateException, InvalidOwnerException;
/**
* Claim an existing task for the current user.
* @param id
* task id
* @param forceClaim
* if true, claim is performed even if the task is already claimed by someone else
* @return modified claimed Task
* @throws TaskNotFoundException if the task with id was not found
* @throws InvalidStateException if the task state is not ready
* @throws InvalidOwnerException if the task is claimed by another user
*/
Task claim(String id, boolean forceClaim) throws TaskNotFoundException, InvalidStateException, InvalidOwnerException;
/** /**
* Set task to completed. * Set task to completed.

View File

@ -4,10 +4,9 @@ package pro.taskana.exceptions;
* Thrown in ConnectionManagementMode AUTOCOMMIT when an attempt to commit fails. * Thrown in ConnectionManagementMode AUTOCOMMIT when an attempt to commit fails.
* *
*/ */
@SuppressWarnings("serial")
public class AutocommitFailedException extends TaskanaRuntimeException { public class AutocommitFailedException extends TaskanaRuntimeException {
public AutocommitFailedException(Throwable cause) { public AutocommitFailedException(Throwable cause) {
super("Autocommit failed", cause); super("Autocommit failed", cause);
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -3,10 +3,11 @@ package pro.taskana.exceptions;
/** /**
* Thrown if a specific task is not in the database. * Thrown if a specific task is not in the database.
*/ */
@SuppressWarnings("serial")
public class ClassificationNotFoundException extends NotFoundException { public class ClassificationNotFoundException extends NotFoundException {
public ClassificationNotFoundException(String id) { public ClassificationNotFoundException(String id) {
super("Classification '" + id + "' not found"); super("Classification '" + id + "' not found");
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -4,11 +4,11 @@ package pro.taskana.exceptions;
* Thrown if ConnectionManagementMode is CONNECTION_MANAGED_EXTERNALLY and an attempt is made to call an API method before the setConnection() method has been called. * Thrown if ConnectionManagementMode is CONNECTION_MANAGED_EXTERNALLY and an attempt is made to call an API method before the setConnection() method has been called.
* *
*/ */
@SuppressWarnings("serial")
public class ConnectionNotSetException extends TaskanaRuntimeException { public class ConnectionNotSetException extends TaskanaRuntimeException {
public ConnectionNotSetException() { public ConnectionNotSetException() {
super("Connection not set"); super("Connection not set");
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -2,13 +2,14 @@ package pro.taskana.exceptions;
/** /**
* This exception is thrown when a method is called with invalid argument. * This exception is thrown when a method is called with invalid argument.
* @author bbr
* *
* @author bbr
*/ */
@SuppressWarnings("serial")
public class InvalidArgumentException extends TaskanaException { public class InvalidArgumentException extends TaskanaException {
public InvalidArgumentException(String msg) { public InvalidArgumentException(String msg) {
super(msg); super(msg);
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -0,0 +1,14 @@
package pro.taskana.exceptions;
/**
* This exception is thrown when the task state doesn't allow the requested operation.
* @author bbr
*
*/
public class InvalidOwnerException extends TaskanaException {
public InvalidOwnerException(String msg) {
super(msg);
}
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,14 @@
package pro.taskana.exceptions;
/**
* This exception is thrown when the task state doesn't allow the requested operation.
* @author bbr
*
*/
public class InvalidStateException extends TaskanaException {
public InvalidStateException(String msg) {
super(msg);
}
private static final long serialVersionUID = 1L;
}

View File

@ -3,10 +3,11 @@ package pro.taskana.exceptions;
/** /**
* This exception is used to communicate a not authorized user. * This exception is used to communicate a not authorized user.
*/ */
@SuppressWarnings("serial")
public class NotAuthorizedException extends TaskanaException { public class NotAuthorizedException extends TaskanaException {
public NotAuthorizedException(String msg) { public NotAuthorizedException(String msg) {
super(msg); super(msg);
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -3,10 +3,11 @@ package pro.taskana.exceptions;
/** /**
* This exception will be thrown if a specific object is not in the database. * This exception will be thrown if a specific object is not in the database.
*/ */
@SuppressWarnings("serial")
public class NotFoundException extends TaskanaException { public class NotFoundException extends TaskanaException {
public NotFoundException(String id) { public NotFoundException(String id) {
super(id); super(id);
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -3,10 +3,11 @@ package pro.taskana.exceptions;
/** /**
* This exception will be thrown if a specific task is not in the database. * This exception will be thrown if a specific task is not in the database.
*/ */
@SuppressWarnings("serial")
public class TaskNotFoundException extends NotFoundException { public class TaskNotFoundException extends NotFoundException {
public TaskNotFoundException(String id) { public TaskNotFoundException(String id) {
super("Task '" + id + "' not found"); super("Task '" + id + "' not found");
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -2,10 +2,9 @@ package pro.taskana.exceptions;
/** /**
* common base class for Taskana's checked exceptions. * common base class for Taskana's checked exceptions.
* @author bbr
* *
* @author bbr
*/ */
@SuppressWarnings("serial")
public class TaskanaException extends Exception { public class TaskanaException extends Exception {
private static final long serialVersionUID = 123234345123412L; private static final long serialVersionUID = 123234345123412L;

View File

@ -2,10 +2,9 @@ package pro.taskana.exceptions;
/** /**
* Common base class for Taskana's runtime exceptions. * Common base class for Taskana's runtime exceptions.
* @author bbr
* *
* @author bbr
*/ */
@SuppressWarnings("serial")
public class TaskanaRuntimeException extends RuntimeException { public class TaskanaRuntimeException extends RuntimeException {
private static final long serialVersionUID = 1511142769801824L; private static final long serialVersionUID = 1511142769801824L;
@ -27,7 +26,7 @@ public class TaskanaRuntimeException extends RuntimeException {
} }
public TaskanaRuntimeException(String message, Throwable cause, boolean enableSuppression, public TaskanaRuntimeException(String message, Throwable cause, boolean enableSuppression,
boolean writableStackTrace) { boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace); super(message, cause, enableSuppression, writableStackTrace);
} }

View File

@ -3,10 +3,11 @@ package pro.taskana.exceptions;
/** /**
* This exception will be thrown if a specific workbasket is not in the database. * This exception will be thrown if a specific workbasket is not in the database.
*/ */
@SuppressWarnings("serial")
public class WorkbasketNotFoundException extends NotFoundException { public class WorkbasketNotFoundException extends NotFoundException {
public WorkbasketNotFoundException(String id) { public WorkbasketNotFoundException(String id) {
super("Workbasket with '" + id + "' not found"); super("Workbasket with '" + id + "' not found");
} }
private static final long serialVersionUID = 1L;
} }

View File

@ -15,7 +15,10 @@ import pro.taskana.Classification;
import pro.taskana.TaskQuery; import pro.taskana.TaskQuery;
import pro.taskana.TaskService; import pro.taskana.TaskService;
import pro.taskana.TaskanaEngine; import pro.taskana.TaskanaEngine;
import pro.taskana.WorkbasketService;
import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
@ -30,6 +33,7 @@ import pro.taskana.model.TaskSummary;
import pro.taskana.model.WorkbasketAuthorization; import pro.taskana.model.WorkbasketAuthorization;
import pro.taskana.model.mappings.ObjectReferenceMapper; import pro.taskana.model.mappings.ObjectReferenceMapper;
import pro.taskana.model.mappings.TaskMapper; import pro.taskana.model.mappings.TaskMapper;
import pro.taskana.security.CurrentUserContext;
/** /**
* This is the implementation of TaskService. * This is the implementation of TaskService.
@ -44,6 +48,7 @@ public class TaskServiceImpl implements TaskService {
private TaskanaEngine taskanaEngine; private TaskanaEngine taskanaEngine;
private TaskanaEngineImpl taskanaEngineImpl; private TaskanaEngineImpl taskanaEngineImpl;
private WorkbasketService workbasketService;
private TaskMapper taskMapper; private TaskMapper taskMapper;
private ObjectReferenceMapper objectReferenceMapper; private ObjectReferenceMapper objectReferenceMapper;
@ -54,27 +59,48 @@ public class TaskServiceImpl implements TaskService {
this.taskanaEngineImpl = (TaskanaEngineImpl) taskanaEngine; this.taskanaEngineImpl = (TaskanaEngineImpl) taskanaEngine;
this.taskMapper = taskMapper; this.taskMapper = taskMapper;
this.objectReferenceMapper = objectReferenceMapper; this.objectReferenceMapper = objectReferenceMapper;
this.workbasketService = taskanaEngineImpl.getWorkbasketService();
} }
@Override @Override
public Task claim(String id, String userName) throws TaskNotFoundException { public Task claim(String id) throws TaskNotFoundException, InvalidStateException, InvalidOwnerException {
return claim(id, false);
}
@Override
public Task claim(String id, boolean forceClaim) throws TaskNotFoundException, InvalidStateException, InvalidOwnerException {
String userName = CurrentUserContext.getUserid();
return claim(id, userName, forceClaim);
}
public Task claim(String id, String userName, boolean forceClaim) throws TaskNotFoundException, InvalidStateException, InvalidOwnerException {
LOGGER.debug("entry to claim(id = {}, userName = {})", id, userName); LOGGER.debug("entry to claim(id = {}, userName = {})", id, userName);
Task task = null; Task task = null;
try { try {
taskanaEngineImpl.openConnection(); taskanaEngineImpl.openConnection();
task = taskMapper.findById(id); task = taskMapper.findById(id);
if (task != null) { if (task == null) {
Timestamp now = new Timestamp(System.currentTimeMillis());
task.setOwner(userName);
task.setModified(now);
task.setClaimed(now);
task.setState(TaskState.CLAIMED);
taskMapper.update(task);
LOGGER.debug("Method claim() claimed task '{}' for user '{}'.", id, userName);
} else {
LOGGER.warn("Method claim() didn't find task with id {}. Throwing TaskNotFoundException", id); LOGGER.warn("Method claim() didn't find task with id {}. Throwing TaskNotFoundException", id);
throw new TaskNotFoundException(id); throw new TaskNotFoundException(id);
} }
TaskState state = task.getState();
if (state == TaskState.COMPLETED) {
LOGGER.warn("Method claim() found that task {} is already completed. Throwing InvalidStateException", id);
throw new InvalidStateException("Task is already completed");
}
if (state == TaskState.CLAIMED && !forceClaim) {
LOGGER.warn("Method claim() found that task {} is claimed by {} and forceClaim is false. Throwing InvalidOwnerException", id, task.getOwner());
throw new InvalidOwnerException("Task is already claimed by user " + task.getOwner());
}
Timestamp now = new Timestamp(System.currentTimeMillis());
task.setOwner(userName);
task.setModified(now);
task.setClaimed(now);
task.setRead(true);
task.setState(TaskState.CLAIMED);
taskMapper.update(task);
LOGGER.debug("Method claim() claimed task '{}' for user '{}'.", id, userName);
} finally { } finally {
taskanaEngineImpl.returnConnection(); taskanaEngineImpl.returnConnection();
LOGGER.debug("exit from claim()"); LOGGER.debug("exit from claim()");
@ -369,4 +395,6 @@ public class TaskServiceImpl implements TaskService {
} }
return taskSummaries; return taskSummaries;
} }
} }

View File

@ -331,7 +331,7 @@ public class TaskServiceImplTest {
Thread.sleep(SLEEP_TIME); // to have different timestamps Thread.sleep(SLEEP_TIME); // to have different timestamps
String expectedOwner = "John Does"; String expectedOwner = "John Does";
Task acturalTask = cut.claim(expectedTask.getId(), expectedOwner); Task acturalTask = cut.claim(expectedTask.getId(), expectedOwner, true);
verify(taskanaEngineImpl, times(1)).openConnection(); verify(taskanaEngineImpl, times(1)).openConnection();
verify(taskMapperMock, times(1)).findById(expectedTask.getId()); verify(taskMapperMock, times(1)).findById(expectedTask.getId());
@ -347,12 +347,12 @@ public class TaskServiceImplTest {
} }
@Test(expected = TaskNotFoundException.class) @Test(expected = TaskNotFoundException.class)
public void testClaimThrowinTaskNotFoundException() throws TaskNotFoundException { public void testClaimThrowinTaskNotFoundException() throws Exception {
try { try {
Task expectedTask = null; Task expectedTask = null;
Mockito.doReturn(expectedTask).when(taskMapperMock).findById(any()); Mockito.doReturn(expectedTask).when(taskMapperMock).findById(any());
cut.claim("1", "OWNER"); cut.claim("1", "OWNER", true);
} catch (Exception e) { } catch (Exception e) {
verify(taskanaEngineImpl, times(1)).openConnection(); verify(taskanaEngineImpl, times(1)).openConnection();
verify(taskMapperMock, times(1)).findById(any()); verify(taskMapperMock, times(1)).findById(any());

View File

@ -4,6 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import pro.taskana.exceptions.ClassificationNotFoundException; import pro.taskana.exceptions.ClassificationNotFoundException;
import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
@ -19,14 +21,14 @@ public class ExampleBootstrap {
private TaskService taskService; private TaskService taskService;
@PostConstruct @PostConstruct
public void test() throws TaskNotFoundException, NotAuthorizedException, WorkbasketNotFoundException, ClassificationNotFoundException { public void test() throws TaskNotFoundException, NotAuthorizedException, WorkbasketNotFoundException, ClassificationNotFoundException, InvalidStateException, InvalidOwnerException {
System.out.println("---------------------------> Start App"); System.out.println("---------------------------> Start App");
Task task = new Task(); Task task = new Task();
task.setName("Spring example task"); task.setName("Spring example task");
task.setWorkbasketId("1"); task.setWorkbasketId("1");
task = taskService.createTask(task); task = taskService.createTask(task);
System.out.println("---------------------------> Task started: " + task.getId()); System.out.println("---------------------------> Task started: " + task.getId());
taskService.claim(task.getId(), "John Doe"); taskService.claim(task.getId());
System.out.println( System.out.println(
"---------------------------> Task claimed: " + taskService.getTaskById(task.getId()).getOwner()); "---------------------------> Task claimed: " + taskService.getTaskById(task.getId()).getOwner());
// taskService.complete(task.getId()); // taskService.complete(task.getId());

View File

@ -20,6 +20,8 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import pro.taskana.TaskService; import pro.taskana.TaskService;
import pro.taskana.exceptions.InvalidOwnerException;
import pro.taskana.exceptions.InvalidStateException;
import pro.taskana.exceptions.NotAuthorizedException; import pro.taskana.exceptions.NotAuthorizedException;
import pro.taskana.exceptions.TaskNotFoundException; import pro.taskana.exceptions.TaskNotFoundException;
import pro.taskana.exceptions.WorkbasketNotFoundException; import pro.taskana.exceptions.WorkbasketNotFoundException;
@ -85,13 +87,19 @@ public class TaskController {
public ResponseEntity<Task> claimTask(@PathVariable String taskId, @RequestBody String userName) { public ResponseEntity<Task> claimTask(@PathVariable String taskId, @RequestBody String userName) {
// TODO verify user // TODO verify user
try { try {
taskService.claim(taskId, userName); taskService.claim(taskId);
Task updatedTask = taskService.getTaskById(taskId); Task updatedTask = taskService.getTaskById(taskId);
return ResponseEntity.status(HttpStatus.OK).body(updatedTask); return ResponseEntity.status(HttpStatus.OK).body(updatedTask);
} catch (TaskNotFoundException e) { } catch (TaskNotFoundException e) {
logger.error("The given Task coundn´t be found/claimd or does not Exist.", e); logger.error("The given Task coundn´t be found/claimd or does not Exist.", e);
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} } catch (InvalidStateException e) {
logger.error("The given Task could not be claimed. Reason: {}", e);
return ResponseEntity.status(HttpStatus.CONFLICT).build();
} catch (InvalidOwnerException e) {
logger.error("The given Task could not be claimed. Reason: {}", e);
return ResponseEntity.status(HttpStatus.CONFLICT).build();
}
} }
@RequestMapping(method = RequestMethod.POST, value = "/{taskId}/complete") @RequestMapping(method = RequestMethod.POST, value = "/{taskId}/complete")