diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java
index 6e357fdf2..463c3a583 100644
--- a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java
+++ b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskService.java
@@ -169,9 +169,9 @@ public interface TaskService {
* @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} to be claimed
* @return the claimed {@linkplain Task}
* @throws TaskNotFoundException if the {@linkplain Task} with taskId was not found
- * @throws InvalidStateException if the {@linkplain Task#getState() state} of the {@linkplain
- * Task} with taskId isn't {@linkplain TaskState#READY}
- * @throws InvalidOwnerException if the {@linkplain Task} with taskId is claimed by someone else
+ * @throws InvalidStateException if the state of Task with taskId is in {@linkplain
+ * TaskState#END_STATES}
+ * @throws InvalidOwnerException cannot be thrown
* @throws NotAuthorizedException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the {@linkplain Workbasket} the {@linkplain Task} is in
*/
@@ -216,8 +216,7 @@ public interface TaskService {
* @throws TaskNotFoundException if the {@linkplain Task} with taskId was not found
* @throws InvalidStateException if the {@linkplain Task} is already in one of the {@linkplain
* TaskState#END_STATES}
- * @throws InvalidOwnerException if forceCancel is false and the {@linkplain Task} is claimed by
- * another user
+ * @throws InvalidOwnerException cannot be thrown
* @throws NotAuthorizedException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the {@linkplain Workbasket} the {@linkplain Task} is in
*/
@@ -326,7 +325,9 @@ public interface TaskService {
* @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} which should be
* completed
* @return the updated {@linkplain Task} after completion
- * @throws InvalidStateException if the {@linkplain Task} with taskId wasn't claimed before
+ * @throws InvalidStateException if the {@linkplain Task#getState() state} of the {@linkplain
+ * Task} with taskId is with taskId is {@linkplain TaskState#TERMINATED} or {@linkplain
+ * TaskState#CANCELLED}
* @throws TaskNotFoundException if the {@linkplain Task} with taskId wasn't found
* @throws InvalidOwnerException if current user isn't the {@linkplain Task#getOwner() owner} of
* the {@linkplain Task} or {@linkplain TaskanaRole#ADMIN}
@@ -389,13 +390,13 @@ public interface TaskService {
*
*
This is typically done by administration to correct any technical issue.
*
- * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} to cancel
+ * @param taskId the {@linkplain Task#getId() id} of the {@linkplain Task} to terminate
* @return the updated {@linkplain Task}
* @throws TaskNotFoundException if the {@linkplain Task} with taskId wasn't found
* @throws InvalidStateException if the {@linkplain Task} isn't in {@linkplain TaskState#READY} or
* {@linkplain TaskState#CLAIMED}
* @throws NotAuthorizedException if the current user isn't member of {@linkplain
- * TaskanaRole#ADMIN} or {@linkplain TaskanaRole#BUSINESS_ADMIN}
+ * TaskanaRole#ADMIN} or {@linkplain TaskanaRole#TASK_ADMIN}
*/
Task terminateTask(String taskId)
throws TaskNotFoundException, InvalidStateException, NotAuthorizedException;
@@ -710,8 +711,9 @@ public interface TaskService {
* @throws TaskNotFoundException if the given {@linkplain Task#getId() id} doesn't refer to an
* existing {@linkplain Task}
* @throws InvalidStateException if the {@linkplain Task#getState() state} of the referenced
- * {@linkplain Task} isn't one of the {@linkplain TaskState#END_STATES} and forceDelete is
- * false
+ * {@linkplain Task} isn't {@linkplain TaskState#TERMINATED} or {@linkplain
+ * TaskState#CANCELLED} and the Callback State of the Task is {@linkplain
+ * CallbackState#CALLBACK_PROCESSING_REQUIRED}
* @throws NotAuthorizedException if the current user isn't member of {@linkplain
* TaskanaRole#ADMIN}
*/
diff --git a/rest/taskana-rest-spring/src/docs/asciidoc/rest-api.adoc b/rest/taskana-rest-spring/src/docs/asciidoc/rest-api.adoc
index 50bacd5af..7b66589e9 100644
--- a/rest/taskana-rest-spring/src/docs/asciidoc/rest-api.adoc
+++ b/rest/taskana-rest-spring/src/docs/asciidoc/rest-api.adoc
@@ -8,8 +8,7 @@ This is the REST documentation for http://taskana.pro)[TASKANA] - the world's fi
Whenever a parameter is an array type, several values can be passed by declaring that parameter multiple times.
Whenever a parameter is a complex type, the attributes of the value-object can be passed as a json.
-For example, a complex parameter with the name "complex-query-param" and attributes "attribute1" and "attribute2"
-would be specified in the following way: +
+For example, a complex parameter with the name "complex-query-param" and attributes "attribute1" and "attribute2" would be specified in the following way: +
complex-query-param={"attribute1":"value1","attribute2":"value2"}
=== Hypermedia Support
@@ -101,6 +100,7 @@ include::{snippets}/TaskControllerRestDocTest/getSpecificTaskDocTest/auto-sectio
include::{snippets}/TaskControllerRestDocTest/getAllTasksDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/updateTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/claimTaskDocTest/auto-section.adoc[]
+include::{snippets}/TaskControllerRestDocTest/forceClaimTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/selectAndClaimTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/cancelClaimTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/forceCancelClaimTaskDocTest/auto-section.adoc[]
@@ -109,9 +109,13 @@ include::{snippets}/TaskControllerRestDocTest/forceRequestReviewTaskDocTest/auto
include::{snippets}/TaskControllerRestDocTest/requestChangesTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/forceRequestChangesTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/completeTaskDocTest/auto-section.adoc[]
+include::{snippets}/TaskControllerRestDocTest/forceCompleteTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/cancelTaskDocTest/auto-section.adoc[]
+include::{snippets}/TaskControllerRestDocTest/terminateTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/transferTaskDocTest/auto-section.adoc[]
+include::{snippets}/TaskControllerRestDocTest/setTaskReadDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/deleteTaskDocTest/auto-section.adoc[]
+include::{snippets}/TaskControllerRestDocTest/forceDeleteTaskDocTest/auto-section.adoc[]
include::{snippets}/TaskControllerRestDocTest/deleteTasksDocTest/auto-section.adoc[]
== Task Comment Resource
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java
index 1245cd418..5b7f13135 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/RestEndpoints.java
@@ -43,6 +43,7 @@ public final class RestEndpoints {
// task endpoints
public static final String URL_TASKS = API_V1 + "tasks";
public static final String URL_TASKS_ID = API_V1 + "tasks/{taskId}";
+ public static final String URL_TASKS_ID_FORCE = API_V1 + "tasks/{taskId}/force";
public static final String URL_TASKS_ID_CLAIM = API_V1 + "tasks/{taskId}/claim";
public static final String URL_TASKS_ID_CLAIM_FORCE = API_V1 + "tasks/{taskId}/claim/force";
public static final String URL_TASKS_ID_SELECT_AND_CLAIM = API_V1 + "tasks/select-and-claim";
@@ -54,9 +55,12 @@ public final class RestEndpoints {
public static final String URL_TASKS_ID_REQUEST_CHANGES_FORCE =
API_V1 + "tasks/{taskId}/request-changes/force";
public static final String URL_TASKS_ID_COMPLETE = API_V1 + "tasks/{taskId}/complete";
+ public static final String URL_TASKS_ID_COMPLETE_FORCE = API_V1 + "tasks/{taskId}/complete/force";
public static final String URL_TASKS_ID_CANCEL = API_V1 + "tasks/{taskId}/cancel";
+ public static final String URL_TASKS_ID_TERMINATE = API_V1 + "tasks/{taskId}/terminate";
public static final String URL_TASKS_ID_TRANSFER_WORKBASKET_ID =
API_V1 + "tasks/{taskId}/transfer/{workbasketId}";
+ public static final String URL_TASKS_ID_SET_READ = API_V1 + "tasks/{taskId}/set-read";
// task comment endpoints
public static final String URL_TASK_COMMENTS = API_V1 + "tasks/{taskId}/comments";
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java
index 9e3bf85e1..d5da13b20 100644
--- a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java
@@ -49,6 +49,7 @@ import pro.taskana.task.api.models.Task;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.task.rest.assembler.TaskRepresentationModelAssembler;
import pro.taskana.task.rest.assembler.TaskSummaryRepresentationModelAssembler;
+import pro.taskana.task.rest.models.IsReadRepresentationModel;
import pro.taskana.task.rest.models.TaskRepresentationModel;
import pro.taskana.task.rest.models.TaskSummaryCollectionRepresentationModel;
import pro.taskana.task.rest.models.TaskSummaryPagedRepresentationModel;
@@ -73,6 +74,43 @@ public class TaskController {
this.taskSummaryRepresentationModelAssembler = taskSummaryRepresentationModelAssembler;
}
+ // region CREATE
+
+ /**
+ * This endpoint creates a persistent Task.
+ *
+ * @param taskRepresentationModel the Task which should be created.
+ * @return the created Task
+ * @throws WorkbasketNotFoundException if the referenced Workbasket does not exist
+ * @throws ClassificationNotFoundException if the referenced Classification does not exist
+ * @throws NotAuthorizedException if the current user is not authorized to append a Task to the
+ * referenced Workbasket
+ * @throws TaskAlreadyExistException if the requested Task already exists.
+ * @throws InvalidArgumentException if any input is semantically wrong.
+ * @throws AttachmentPersistenceException if an Attachment with ID will be added multiple times
+ * without using the task-methods
+ * @throws ObjectReferencePersistenceException if an ObjectReference with ID will be added
+ * multiple times without using the task-methods
+ * @title Create a new Task
+ */
+ @PostMapping(path = RestEndpoints.URL_TASKS)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity createTask(
+ @RequestBody TaskRepresentationModel taskRepresentationModel)
+ throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException,
+ TaskAlreadyExistException, InvalidArgumentException, AttachmentPersistenceException,
+ ObjectReferencePersistenceException {
+ Task fromResource = taskRepresentationModelAssembler.toEntityModel(taskRepresentationModel);
+ Task createdTask = taskService.createTask(fromResource);
+
+ return ResponseEntity.status(HttpStatus.CREATED)
+ .body(taskRepresentationModelAssembler.toModel(createdTask));
+ }
+
+ // endregion
+
+ // region READ
+
/**
* This endpoint retrieves a list of existing Tasks. Filters can be applied.
*
@@ -116,50 +154,6 @@ public class TaskController {
return ResponseEntity.ok(pagedModels);
}
- /**
- * This endpoint deletes an aggregation of Tasks and returns the deleted Tasks. Filters can be
- * applied.
- *
- * @title Delete multiple Tasks
- * @param filterParameter the filter parameters
- * @param filterCustomFields the filter parameters regarding TaskCustomFields
- * @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields
- * @return the deleted task summaries
- * @throws InvalidArgumentException TODO: this is never thrown
- * @throws NotAuthorizedException if the current user is not authorized to delete the requested
- * Tasks.
- */
- @DeleteMapping(path = RestEndpoints.URL_TASKS)
- @Transactional(readOnly = true, rollbackFor = Exception.class)
- public ResponseEntity deleteTasks(
- TaskQueryFilterParameter filterParameter,
- TaskQueryFilterCustomFields filterCustomFields,
- TaskQueryFilterCustomIntFields filterCustomIntFields)
- throws InvalidArgumentException, NotAuthorizedException {
- TaskQuery query = taskService.createTaskQuery();
- filterParameter.apply(query);
- filterCustomFields.apply(query);
- filterCustomIntFields.apply(query);
-
- List taskSummaries = query.list();
-
- List taskIdsToDelete =
- taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
-
- BulkOperationResults result =
- taskService.deleteTasks(taskIdsToDelete);
-
- Set failedIds = new HashSet<>(result.getFailedIds());
- List successfullyDeletedTaskSummaries =
- taskSummaries.stream()
- .filter(not(summary -> failedIds.contains(summary.getId())))
- .collect(Collectors.toList());
-
- return ResponseEntity.ok(
- taskSummaryRepresentationModelAssembler.toTaskanaCollectionModel(
- successfullyDeletedTaskSummaries));
- }
-
/**
* This endpoint retrieves a specific Task.
*
@@ -178,6 +172,10 @@ public class TaskController {
return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(task));
}
+ // endregion
+
+ // region UPDATE
+
/**
* This endpoint claims a Task if possible.
*
@@ -198,8 +196,108 @@ public class TaskController {
throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
NotAuthorizedException {
// TODO verify user
- taskService.claim(taskId);
- Task updatedTask = taskService.getTask(taskId);
+ Task updatedTask = taskService.claim(taskId);
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
+ }
+
+ /**
+ * This endpoint force claims a Task if possible even if it is already claimed by someone else.
+ *
+ * @param taskId the Id of the Task which should be force claimed
+ * @param userName TODO: this is currently not used
+ * @return the force claimed Task
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException if the state of Task with taskId is in an END_STATE.
+ * @throws InvalidOwnerException cannot be thrown.
+ * @throws NotAuthorizedException if the current user has no read permissions for the requested
+ * Task.
+ * @title Force claim a Task
+ */
+ @PostMapping(path = RestEndpoints.URL_TASKS_ID_CLAIM_FORCE)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity forceClaimTask(
+ @PathVariable String taskId, @RequestBody(required = false) String userName)
+ throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
+ NotAuthorizedException {
+ // TODO verify user
+ Task updatedTask = taskService.forceClaim(taskId);
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
+ }
+
+ /**
+ * This endpoint selects the first Task returned by the Task Query and claims it.
+ *
+ * @param filterParameter the filter parameters
+ * @param filterCustomFields the filter parameters regarding TaskCustomFields
+ * @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields
+ * @param sortParameter the sort parameters
+ * @return the claimed Task
+ * @throws InvalidOwnerException if the Task is already claimed by someone else
+ * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
+ * the Task is in
+ * @title Select and claim a Task
+ */
+ @PostMapping(path = RestEndpoints.URL_TASKS_ID_SELECT_AND_CLAIM)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity selectAndClaimTask(
+ TaskQueryFilterParameter filterParameter,
+ TaskQueryFilterCustomFields filterCustomFields,
+ TaskQueryFilterCustomIntFields filterCustomIntFields,
+ TaskQuerySortParameter sortParameter)
+ throws InvalidOwnerException, NotAuthorizedException {
+ TaskQuery query = taskService.createTaskQuery();
+
+ filterParameter.apply(query);
+ filterCustomFields.apply(query);
+ filterCustomIntFields.apply(query);
+ sortParameter.apply(query);
+
+ Task selectedAndClaimedTask = taskService.selectAndClaim(query);
+
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(selectedAndClaimedTask));
+ }
+
+ /**
+ * This endpoint cancels the claim of an existing Task if it was claimed by the current user
+ * before.
+ *
+ * @param taskId the Id of the requested Task.
+ * @return the unclaimed Task.
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException if the Task is already in an end state.
+ * @throws InvalidOwnerException if the Task is claimed by a different user.
+ * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
+ * the Task is in
+ * @title Cancel a claimed Task
+ */
+ @DeleteMapping(path = RestEndpoints.URL_TASKS_ID_CLAIM)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity cancelClaimTask(@PathVariable String taskId)
+ throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
+ NotAuthorizedException {
+ Task updatedTask = taskService.cancelClaim(taskId);
+
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
+ }
+
+ /**
+ * This endpoint force cancels the claim of an existing Task.
+ *
+ * @param taskId the Id of the requested Task.
+ * @return the unclaimed Task.
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException if the Task is already in an end state.
+ * @throws InvalidOwnerException if the Task is claimed by a different user.
+ * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
+ * the Task is in
+ * @title Force cancel a claimed Task
+ */
+ @DeleteMapping(path = RestEndpoints.URL_TASKS_ID_CLAIM_FORCE)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity forceCancelClaimTask(@PathVariable String taskId)
+ throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
+ NotAuthorizedException {
+ Task updatedTask = taskService.forceCancelClaim(taskId);
return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
}
@@ -287,83 +385,6 @@ public class TaskController {
return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(task));
}
- /**
- * This endpoint selects the first Task returned by the Task Query and claims it.
- *
- * @param filterParameter the filter parameters
- * @param filterCustomFields the filter parameters regarding TaskCustomFields
- * @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields
- * @param sortParameter the sort parameters
- * @return the claimed Task
- * @throws InvalidOwnerException if the Task is already claimed by someone else
- * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
- * the Task is in
- * @title Select and claim a Task
- */
- @PostMapping(path = RestEndpoints.URL_TASKS_ID_SELECT_AND_CLAIM)
- @Transactional(rollbackFor = Exception.class)
- public ResponseEntity selectAndClaimTask(
- TaskQueryFilterParameter filterParameter,
- TaskQueryFilterCustomFields filterCustomFields,
- TaskQueryFilterCustomIntFields filterCustomIntFields,
- TaskQuerySortParameter sortParameter)
- throws InvalidOwnerException, NotAuthorizedException {
- TaskQuery query = taskService.createTaskQuery();
-
- filterParameter.apply(query);
- filterCustomFields.apply(query);
- filterCustomIntFields.apply(query);
- sortParameter.apply(query);
-
- Task selectedAndClaimedTask = taskService.selectAndClaim(query);
-
- return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(selectedAndClaimedTask));
- }
-
- /**
- * This endpoint cancels the claim of an existing Task if it was claimed by the current user
- * before.
- *
- * @param taskId the Id of the requested Task.
- * @return the unclaimed Task.
- * @throws TaskNotFoundException if the requested Task does not exist.
- * @throws InvalidStateException if the Task is already in an end state.
- * @throws InvalidOwnerException if the Task is claimed by a different user.
- * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
- * the Task is in
- * @title Cancel a claimed Task
- */
- @DeleteMapping(path = RestEndpoints.URL_TASKS_ID_CLAIM)
- @Transactional(rollbackFor = Exception.class)
- public ResponseEntity cancelClaimTask(@PathVariable String taskId)
- throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
- NotAuthorizedException {
- Task updatedTask = taskService.cancelClaim(taskId);
-
- return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
- }
-
- /**
- * This endpoint force cancels the claim of an existing Task.
- *
- * @param taskId the Id of the requested Task.
- * @return the unclaimed Task.
- * @throws TaskNotFoundException if the requested Task does not exist.
- * @throws InvalidStateException if the Task is already in an end state.
- * @throws InvalidOwnerException if the Task is claimed by a different user.
- * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
- * the Task is in
- * @title Force cancel a claimed Task
- */
- @DeleteMapping(path = RestEndpoints.URL_TASKS_ID_CLAIM_FORCE)
- @Transactional(rollbackFor = Exception.class)
- public ResponseEntity forceCancelClaimTask(@PathVariable String taskId)
- throws TaskNotFoundException, InvalidStateException, InvalidOwnerException,
- NotAuthorizedException {
- Task updatedTask = taskService.forceCancelClaim(taskId);
- return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
- }
-
/**
* This endpoint completes a Task.
*
@@ -382,29 +403,32 @@ public class TaskController {
throws TaskNotFoundException, InvalidOwnerException, InvalidStateException,
NotAuthorizedException {
- Task updatedTask = taskService.forceCompleteTask(taskId);
+ Task updatedTask = taskService.completeTask(taskId);
return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
}
/**
- * This endpoint deletes a Task.
+ * This endpoint force completes a Task.
*
- * @title Delete a Task
- * @param taskId the Id of the Task which should be deleted.
- * @return the deleted Task.
+ * @param taskId Id of the requested Task to force complete.
+ * @return the force completed Task
* @throws TaskNotFoundException if the requested Task does not exist.
- * @throws InvalidStateException TODO: this is never thrown
- * @throws NotAuthorizedException if the current user is not authorized to delete the requested
- * Task.
+ * @throws InvalidOwnerException cannot be thrown.
+ * @throws InvalidStateException if the state of the Task with taskId is TERMINATED or CANCELED
+ * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
+ * the Task is in
+ * @title Force complete a Task
*/
- @DeleteMapping(path = RestEndpoints.URL_TASKS_ID)
+ @PostMapping(path = RestEndpoints.URL_TASKS_ID_COMPLETE_FORCE)
@Transactional(rollbackFor = Exception.class)
- public ResponseEntity deleteTask(@PathVariable String taskId)
- throws TaskNotFoundException, InvalidStateException, NotAuthorizedException {
- taskService.forceDeleteTask(taskId);
+ public ResponseEntity forceCompleteTask(@PathVariable String taskId)
+ throws TaskNotFoundException, InvalidOwnerException, InvalidStateException,
+ NotAuthorizedException {
- return ResponseEntity.noContent().build();
+ Task updatedTask = taskService.forceCompleteTask(taskId);
+
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
}
/**
@@ -430,34 +454,23 @@ public class TaskController {
}
/**
- * This endpoint creates a persistent Task.
+ * This endpoint terminates a Task. Termination is an administrative action to complete a Task.
*
- * @param taskRepresentationModel the Task which should be created.
- * @return the created Task
- * @throws WorkbasketNotFoundException if the referenced Workbasket does not exist
- * @throws ClassificationNotFoundException if the referenced Classification does not exist
- * @throws NotAuthorizedException if the current user is not authorized to append a Task to the
- * referenced Workbasket
- * @throws TaskAlreadyExistException if the requested Task already exists.
- * @throws InvalidArgumentException if any input is semantically wrong.
- * @throws AttachmentPersistenceException if an Attachment with ID will be added multiple times
- * without using the task-methods
- * @throws ObjectReferencePersistenceException if an ObjectReference with ID will be added
- * multiple times without using the task-methods
- * @title Create a new Task
+ * @param taskId Id of the requested Task to terminate.
+ * @return the terminated Task
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException if the task is not in state READY or CLAIMED
+ * @throws NotAuthorizedException if the current user isn't an administrator (ADMIN/TASKADMIN)
+ * @title Terminate a Task
*/
- @PostMapping(path = RestEndpoints.URL_TASKS)
+ @PostMapping(path = RestEndpoints.URL_TASKS_ID_TERMINATE)
@Transactional(rollbackFor = Exception.class)
- public ResponseEntity createTask(
- @RequestBody TaskRepresentationModel taskRepresentationModel)
- throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException,
- TaskAlreadyExistException, InvalidArgumentException, AttachmentPersistenceException,
- ObjectReferencePersistenceException {
- Task fromResource = taskRepresentationModelAssembler.toEntityModel(taskRepresentationModel);
- Task createdTask = taskService.createTask(fromResource);
+ public ResponseEntity terminateTask(@PathVariable String taskId)
+ throws TaskNotFoundException, NotAuthorizedException, InvalidStateException {
- return ResponseEntity.status(HttpStatus.CREATED)
- .body(taskRepresentationModelAssembler.toModel(createdTask));
+ Task terminatedTask = taskService.terminateTask(taskId);
+
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(terminatedTask));
}
/**
@@ -527,6 +540,119 @@ public class TaskController {
return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(task));
}
+ /**
+ * This endpoint sets the 'isRead' property of a Task.
+ *
+ * @param taskId Id of the requested Task to set read or unread.
+ * @param isRead if true, the Task property isRead is set to true, else it's set to false
+ * @return the updated Task
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws NotAuthorizedException if the current user has no read permission for the Workbasket
+ * the Task is in
+ * @title Set a Task read or unread
+ */
+ @PostMapping(path = RestEndpoints.URL_TASKS_ID_SET_READ)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity setTaskRead(
+ @PathVariable String taskId, @RequestBody IsReadRepresentationModel isRead)
+ throws TaskNotFoundException, NotAuthorizedException {
+
+ Task updatedTask = taskService.setTaskRead(taskId, isRead.getIsRead());
+
+ return ResponseEntity.ok(taskRepresentationModelAssembler.toModel(updatedTask));
+ }
+
+ // endregion
+
+ // region DELETE
+
+ /**
+ * This endpoint deletes a Task.
+ *
+ * @title Delete a Task
+ * @param taskId the Id of the Task which should be deleted.
+ * @return the deleted Task.
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException If the Task is not in an END_STATE
+ * @throws NotAuthorizedException if the current user isn't an administrator (ADMIN)
+ */
+ @DeleteMapping(path = RestEndpoints.URL_TASKS_ID)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity deleteTask(@PathVariable String taskId)
+ throws TaskNotFoundException, InvalidStateException, NotAuthorizedException {
+ taskService.deleteTask(taskId);
+
+ return ResponseEntity.noContent().build();
+ }
+
+ /**
+ * This endpoint force deletes a Task even if it's not completed.
+ *
+ * @title Force delete a Task
+ * @param taskId the Id of the Task which should be force deleted.
+ * @return the force deleted Task.
+ * @throws TaskNotFoundException if the requested Task does not exist.
+ * @throws InvalidStateException If the Task is not TERMINATED or CANCELLED and the Callback state
+ * of the Task is CALLBACK_PROCESSING_REQUIRED
+ * @throws NotAuthorizedException if the current user isn't an administrator (ADMIN) Task.
+ */
+ @DeleteMapping(path = RestEndpoints.URL_TASKS_ID_FORCE)
+ @Transactional(rollbackFor = Exception.class)
+ public ResponseEntity forceDeleteTask(@PathVariable String taskId)
+ throws TaskNotFoundException, InvalidStateException, NotAuthorizedException {
+ taskService.forceDeleteTask(taskId);
+
+ return ResponseEntity.noContent().build();
+ }
+
+ /**
+ * This endpoint deletes an aggregation of Tasks and returns the deleted Tasks. Filters can be
+ * applied.
+ *
+ * @title Delete multiple Tasks
+ * @param filterParameter the filter parameters
+ * @param filterCustomFields the filter parameters regarding TaskCustomFields
+ * @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields
+ * @return the deleted task summaries
+ * @throws InvalidArgumentException TODO: this is never thrown
+ * @throws NotAuthorizedException if the current user is not authorized to delete the requested
+ * Tasks.
+ */
+ @DeleteMapping(path = RestEndpoints.URL_TASKS)
+ @Transactional(readOnly = true, rollbackFor = Exception.class)
+ public ResponseEntity deleteTasks(
+ TaskQueryFilterParameter filterParameter,
+ TaskQueryFilterCustomFields filterCustomFields,
+ TaskQueryFilterCustomIntFields filterCustomIntFields)
+ throws InvalidArgumentException, NotAuthorizedException {
+ TaskQuery query = taskService.createTaskQuery();
+ filterParameter.apply(query);
+ filterCustomFields.apply(query);
+ filterCustomIntFields.apply(query);
+
+ List taskSummaries = query.list();
+
+ List taskIdsToDelete =
+ taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
+
+ BulkOperationResults result =
+ taskService.deleteTasks(taskIdsToDelete);
+
+ Set failedIds = new HashSet<>(result.getFailedIds());
+ List successfullyDeletedTaskSummaries =
+ taskSummaries.stream()
+ .filter(not(summary -> failedIds.contains(summary.getId())))
+ .collect(Collectors.toList());
+
+ return ResponseEntity.ok(
+ taskSummaryRepresentationModelAssembler.toTaskanaCollectionModel(
+ successfullyDeletedTaskSummaries));
+ }
+
+ // endregion
+
+ // region TaskQuery
+
public enum TaskQuerySortBy implements QuerySortBy {
CLASSIFICATION_KEY(TaskQuery::orderByClassificationKey),
CLASSIFICATION_NAME(TaskQuery::orderByClassificationName),
@@ -608,4 +734,7 @@ public class TaskController {
return super.getSortBy();
}
}
+
+ // endregion
+
}
diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/IsReadRepresentationModel.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/IsReadRepresentationModel.java
new file mode 100644
index 000000000..168c0b6fb
--- /dev/null
+++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/models/IsReadRepresentationModel.java
@@ -0,0 +1,20 @@
+package pro.taskana.task.rest.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.beans.ConstructorProperties;
+
+public class IsReadRepresentationModel {
+
+ /** The value to set the Task property isRead. */
+ @JsonProperty("is-read")
+ private final boolean isRead;
+
+ @ConstructorProperties({"is-read"})
+ public IsReadRepresentationModel(boolean isRead) {
+ this.isRead = isRead;
+ }
+
+ public boolean getIsRead() {
+ return isRead;
+ }
+}
diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java
index fd27aee1c..7932690f0 100644
--- a/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java
+++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/task/rest/TaskControllerIntTest.java
@@ -19,8 +19,11 @@ import java.util.stream.Stream;
import javax.sql.DataSource;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.DynamicTest;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
+import org.junit.jupiter.api.TestInstance;
+import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.function.ThrowingConsumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -38,6 +41,7 @@ import pro.taskana.common.test.rest.RestHelper;
import pro.taskana.common.test.rest.TaskanaSpringBootTest;
import pro.taskana.sampledata.SampleDataGenerator;
import pro.taskana.task.api.TaskState;
+import pro.taskana.task.rest.models.IsReadRepresentationModel;
import pro.taskana.task.rest.models.ObjectReferenceRepresentationModel;
import pro.taskana.task.rest.models.TaskRepresentationModel;
import pro.taskana.task.rest.models.TaskRepresentationModel.CustomAttribute;
@@ -80,1595 +84,6 @@ class TaskControllerIntTest {
sampleDataGenerator.generateSampleData();
}
- @Test
- void should_GetAllTasks() {
- String url = restHelper.toUrl(RestEndpoints.URL_TASKS);
- HttpEntity