TSK-1965: create endpoints for all state transitions (#2024)
* TSK-1965: created endpoint for force claiming a task * TSK-1965: created endpoint for force completing a task * TSK-1965: restructured TaskController to match TaskService structure * TSK-1965: structured TaskControllerIntTest * TSK-1965: created endpoint for terminating a task * TSK-1965: created endpoint for setting a task read
This commit is contained in:
parent
e40623ce0f
commit
b9b49f47a1
|
@ -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 {
|
|||
*
|
||||
* <p>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}
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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<TaskRepresentationModel> 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<TaskSummaryCollectionRepresentationModel> deleteTasks(
|
||||
TaskQueryFilterParameter filterParameter,
|
||||
TaskQueryFilterCustomFields filterCustomFields,
|
||||
TaskQueryFilterCustomIntFields filterCustomIntFields)
|
||||
throws InvalidArgumentException, NotAuthorizedException {
|
||||
TaskQuery query = taskService.createTaskQuery();
|
||||
filterParameter.apply(query);
|
||||
filterCustomFields.apply(query);
|
||||
filterCustomIntFields.apply(query);
|
||||
|
||||
List<TaskSummary> taskSummaries = query.list();
|
||||
|
||||
List<String> taskIdsToDelete =
|
||||
taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||
|
||||
BulkOperationResults<String, TaskanaException> result =
|
||||
taskService.deleteTasks(taskIdsToDelete);
|
||||
|
||||
Set<String> failedIds = new HashSet<>(result.getFailedIds());
|
||||
List<TaskSummary> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> deleteTask(@PathVariable String taskId)
|
||||
throws TaskNotFoundException, InvalidStateException, NotAuthorizedException {
|
||||
taskService.forceDeleteTask(taskId);
|
||||
public ResponseEntity<TaskRepresentationModel> 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<TaskRepresentationModel> createTask(
|
||||
@RequestBody TaskRepresentationModel taskRepresentationModel)
|
||||
throws WorkbasketNotFoundException, ClassificationNotFoundException, NotAuthorizedException,
|
||||
TaskAlreadyExistException, InvalidArgumentException, AttachmentPersistenceException,
|
||||
ObjectReferencePersistenceException {
|
||||
Task fromResource = taskRepresentationModelAssembler.toEntityModel(taskRepresentationModel);
|
||||
Task createdTask = taskService.createTask(fromResource);
|
||||
public ResponseEntity<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskRepresentationModel> 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<TaskSummaryCollectionRepresentationModel> deleteTasks(
|
||||
TaskQueryFilterParameter filterParameter,
|
||||
TaskQueryFilterCustomFields filterCustomFields,
|
||||
TaskQueryFilterCustomIntFields filterCustomIntFields)
|
||||
throws InvalidArgumentException, NotAuthorizedException {
|
||||
TaskQuery query = taskService.createTaskQuery();
|
||||
filterParameter.apply(query);
|
||||
filterCustomFields.apply(query);
|
||||
filterCustomIntFields.apply(query);
|
||||
|
||||
List<TaskSummary> taskSummaries = query.list();
|
||||
|
||||
List<String> taskIdsToDelete =
|
||||
taskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
|
||||
|
||||
BulkOperationResults<String, TaskanaException> result =
|
||||
taskService.deleteTasks(taskIdsToDelete);
|
||||
|
||||
Set<String> failedIds = new HashSet<>(result.getFailedIds());
|
||||
List<TaskSummary> 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<TaskQuery> {
|
||||
CLASSIFICATION_KEY(TaskQuery::orderByClassificationKey),
|
||||
CLASSIFICATION_NAME(TaskQuery::orderByClassificationName),
|
||||
|
@ -608,4 +734,7 @@ public class TaskController {
|
|||
return super.getSortBy();
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -19,6 +19,7 @@ import pro.taskana.task.api.TaskService;
|
|||
import pro.taskana.task.api.models.Task;
|
||||
import pro.taskana.task.internal.models.ObjectReferenceImpl;
|
||||
import pro.taskana.task.rest.assembler.TaskRepresentationModelAssembler;
|
||||
import pro.taskana.task.rest.models.IsReadRepresentationModel;
|
||||
import pro.taskana.task.rest.models.TaskRepresentationModel;
|
||||
|
||||
@ExtendWith(JaasExtension.class)
|
||||
|
@ -34,6 +35,21 @@ class TaskControllerRestDocTest extends BaseRestDocTest {
|
|||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(delete(RestEndpoints.URL_TASKS_ID, "TKI:000000000000000000000000000000000039"))
|
||||
.andExpect(MockMvcResultMatchers.status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forceDeleteTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(
|
||||
delete(RestEndpoints.URL_TASKS_ID_FORCE, "TKI:000000000000000000000000000000000005"))
|
||||
.andExpect(MockMvcResultMatchers.status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteTasksDocTest() throws Exception {
|
||||
mockMvc
|
||||
|
@ -60,6 +76,15 @@ class TaskControllerRestDocTest extends BaseRestDocTest {
|
|||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forceClaimTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(
|
||||
post(
|
||||
RestEndpoints.URL_TASKS_ID_CLAIM_FORCE, "TKI:000000000000000000000000000000000003"))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cancelClaimTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
|
@ -139,6 +164,16 @@ class TaskControllerRestDocTest extends BaseRestDocTest {
|
|||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void forceCompleteTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(
|
||||
post(
|
||||
RestEndpoints.URL_TASKS_ID_COMPLETE_FORCE,
|
||||
"TKI:000000000000000000000000000000000003"))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void cancelTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
|
@ -147,6 +182,26 @@ class TaskControllerRestDocTest extends BaseRestDocTest {
|
|||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void terminateTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(
|
||||
post(RestEndpoints.URL_TASKS_ID_TERMINATE, "TKI:000000000000000000000000000000000000"))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setTaskReadDocTest() throws Exception {
|
||||
|
||||
IsReadRepresentationModel isRead = new IsReadRepresentationModel(true);
|
||||
|
||||
mockMvc
|
||||
.perform(
|
||||
post(RestEndpoints.URL_TASKS_ID_SET_READ, "TKI:000000000000000000000000000000000025")
|
||||
.content(objectMapper.writeValueAsString(isRead)))
|
||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createTaskDocTest() throws Exception {
|
||||
final Task task = taskService.newTask("WBI:100000000000000000000000000000000004");
|
||||
|
@ -165,13 +220,6 @@ class TaskControllerRestDocTest extends BaseRestDocTest {
|
|||
.andExpect(MockMvcResultMatchers.status().isCreated());
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
.perform(delete(RestEndpoints.URL_TASKS_ID, "TKI:000000000000000000000000000000000001"))
|
||||
.andExpect(MockMvcResultMatchers.status().isNoContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void transferTaskDocTest() throws Exception {
|
||||
mockMvc
|
||||
|
|
Loading…
Reference in New Issue