TSK-1916: Add requestChanges functionality

This commit is contained in:
ryzheboka 2022-08-09 11:29:06 +02:00 committed by Elena Mokeeva
parent 0b4445b97b
commit cc32b10d82
15 changed files with 417 additions and 38 deletions

View File

@ -3,6 +3,7 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000000', 'ETI:0000000
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000001', 'ETI:000000000000000000000000000000000001', '2018-01-29 15:55:01', '2018-01-30 15:55:00', null , '2018-01-30 15:55:01', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task01' , 'creator_user_id' , 'Lorem ipsum was n Quatsch dolor sit amet.', 'Some custom Note' , 2 , -1 , 'CLAIMED' , 'EXTERN' , 'L110102' , 'CLI:100000000000000000000000000000000005', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , 'pqr' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000002', 'ETI:000000000000000000000000000000000002', '2018-01-29 15:55:02', '2018-01-30 15:55:00', null , '2018-01-30 15:55:02', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Task02' , 'creator_user_id' , 'Lorem ipsum was n Quatsch t. Aber stimmt.', 'Some custom Note' , 2 , -1 , 'CLAIMED' , 'MANUAL' , 'T2000' , 'CLI:100000000000000000000000000000000016', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000066', 'ETI:000000000000000000000000000000000066', '2018-01-29 15:55:02', '2018-01-29 15:55:00', null , '2018-01-30 15:55:02', null , '2018-01-29 15:55:00', '2018-01-29 15:55:00', 'Task03' , 'creator_user_id' , 'Lorem ipsum was n Quatsch t. Aber stimmt.', 'Some custom Note' , 2 , -1 , 'CLAIMED' , 'MANUAL' , 'T2001' , 'CLI:100000000000000000000000000000000024', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'MyCompany1', 'MySystem1', 'MyInstance1' , 'MyType1', 'MyValue1' , true , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:100000000000000000000000000000000066', 'ETI:100000000000000000000000000000000066', '2012-01-29 15:55:02', '2012-01-29 15:55:00', null , '2012-01-30 15:55:02', null , '2012-01-29 15:55:00', '2012-01-29 15:55:00', 'Task003' , 'creator_user_id' , 'was n Quatsch t. Aber stimmt.' , 'Some other custom Note' , 67 , -1 , 'IN_REVIEW' , 'MANUAL' , 'T2001' , 'CLI:100000000000000000000000000000000024', 'WBI:100000000000000000000000000000000006' , 'USER-1-1' , 'DOMAIN_A', 'BPI21' , 'PBPI21' , 'user-1-1' , 'ACompany1' , 'ASystem1' , 'AnInstance1' , 'AType1' , 'AValue1' , true , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000003', 'ETI:000000000000000000000000000000000003', '2018-01-29 15:55:03', null , null , '2018-01-29 15:55:03', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000003' , 'DOC_0000000000000000003' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , 'efg' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000004', 'ETI:000000000000000000000000000000000004', '2018-01-29 15:55:04', null , null , '2018-01-29 15:55:04', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000004' , 'DOC_0000000000000000004' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , '' , 'ade' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );
INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000005', 'ETI:000000000000000000000000000000000005', '2018-01-29 15:55:05', null , null , '2018-01-29 15:55:05', null , '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , -1 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000005' , 'DOC_0000000000000000005' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , 'abc' , '' , '' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 );

View File

@ -0,0 +1,81 @@
package acceptance.events.task;
import static org.assertj.core.api.Assertions.assertThat;
import acceptance.AbstractAccTest;
import java.time.Instant;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import pro.taskana.common.test.security.JaasExtension;
import pro.taskana.common.test.security.WithAccessId;
import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl;
import pro.taskana.simplehistory.impl.TaskHistoryQueryImpl;
import pro.taskana.simplehistory.impl.task.TaskHistoryQueryMapper;
import pro.taskana.spi.history.api.events.task.TaskHistoryEvent;
import pro.taskana.spi.history.api.events.task.TaskHistoryEventType;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.TaskState;
import pro.taskana.task.api.models.Task;
@ExtendWith(JaasExtension.class)
class CreateHistoryEventOnTaskRequestChangesAccTest extends AbstractAccTest {
private final TaskService taskService = taskanaEngine.getTaskService();
private final SimpleHistoryServiceImpl historyService = getHistoryService();
@WithAccessId(user = "user-1-1")
@Test
void should_CreateClaimedHistoryEvent_When_TaskIsClaimed() throws Exception {
final String taskId = "TKI:100000000000000000000000000000000066";
final Instant oldModified = taskService.getTask(taskId).getModified();
TaskHistoryQueryMapper taskHistoryQueryMapper = getHistoryQueryMapper();
List<TaskHistoryEvent> events =
taskHistoryQueryMapper.queryHistoryEvents(
(TaskHistoryQueryImpl) historyService.createTaskHistoryQuery().taskIdIn(taskId));
assertThat(events).isEmpty();
assertThat(taskService.getTask(taskId).getState()).isEqualTo(TaskState.IN_REVIEW);
Task task = taskService.requestChanges(taskId);
assertThat(task.getState()).isEqualTo(TaskState.READY);
events =
taskHistoryQueryMapper.queryHistoryEvents(
(TaskHistoryQueryImpl) historyService.createTaskHistoryQuery().taskIdIn(taskId));
TaskHistoryEvent event = events.get(0);
assertThat(event.getEventType()).isEqualTo(TaskHistoryEventType.CHANGES_REQUESTED.getName());
event = historyService.getTaskHistoryEvent(event.getId());
assertThat(event.getDetails()).isNotNull();
JSONArray changes = new JSONObject(event.getDetails()).getJSONArray("changes");
JSONObject expectedModified =
new JSONObject()
.put("newValue", task.getModified().toString())
.put("fieldName", "modified")
.put("oldValue", oldModified.toString());
JSONObject expectedState =
new JSONObject()
.put("newValue", "READY")
.put("fieldName", "state")
.put("oldValue", "IN_REVIEW");
JSONObject expectedOwner =
new JSONObject().put("newValue", "").put("fieldName", "owner").put("oldValue", "user-1-1");
JSONArray expectedChanges =
new JSONArray().put(expectedModified).put(expectedState).put(expectedOwner);
assertThat(changes.similar(expectedChanges)).isTrue();
}
}

View File

@ -0,0 +1,183 @@
package acceptance.task.requestchanges;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowableOfType;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.jupiter.api.function.ThrowingConsumer;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.internal.util.EnumUtil;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.TaskState;
import pro.taskana.task.api.exceptions.InvalidOwnerException;
import pro.taskana.task.api.exceptions.InvalidTaskStateException;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.Task;
import pro.taskana.testapi.DefaultTestEntities;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.exceptions.MismatchedWorkbasketPermissionException;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@TaskanaIntegrationTest
class RequestChangesAccTest {
@TaskanaInject TaskService taskService;
ClassificationSummary defaultClassificationSummary;
WorkbasketSummary defaultWorkbasketSummary;
ObjectReference defaultObjectReference;
@WithAccessId(user = "businessadmin")
@BeforeAll
void setup(ClassificationService classificationService, WorkbasketService workbasketService)
throws Exception {
defaultClassificationSummary =
defaultTestClassification().buildAndStoreAsSummary(classificationService);
defaultWorkbasketSummary = defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService);
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(defaultWorkbasketSummary.getId())
.accessId("user-1-1")
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService);
defaultObjectReference = DefaultTestEntities.defaultTestObjectReference().build();
}
@WithAccessId(user = "user-1-1")
@Test
void should_RequestChanges_When_TaskIsClaimed() throws Exception {
Instant now = Instant.now();
Task task = createTaskInReviewByUser("user-1-1").buildAndStore(taskService);
Task result = taskService.requestChanges(task.getId());
assertThat(result.getState()).isEqualTo(TaskState.READY);
assertThat(result.getOwner()).isNull();
assertThat(result.getModified()).isAfterOrEqualTo(now);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ForceRequestChanges_WhenTaskIsInReviewByDifferentUser() throws Exception {
Instant now = Instant.now();
Task task = createTaskInReviewByUser("user-1-2").buildAndStore(taskService);
Task result = taskService.forceRequestChanges(task.getId());
assertThat(result.getState()).isEqualTo(TaskState.READY);
assertThat(result.getOwner()).isNull();
assertThat(result.getModified()).isAfterOrEqualTo(now);
}
@WithAccessId(user = "user-1-1")
@TestFactory
Stream<DynamicTest> should_ForceRequestChanges_WhenTaskIsNotInEndState() {
List<TaskState> testCases = Arrays.asList(EnumUtil.allValuesExceptFor(TaskState.END_STATES));
ThrowingConsumer<TaskState> test =
state -> {
Instant now = Instant.now();
Task task = createDefaultTask().state(state).buildAndStore(taskService);
Task result = taskService.forceRequestChanges(task.getId());
assertThat(result.getState()).isEqualTo(TaskState.READY);
assertThat(result.getOwner()).isNull();
assertThat(result.getModified()).isAfterOrEqualTo(now);
};
return DynamicTest.stream(testCases.iterator(), TaskState::name, test);
}
@WithAccessId(user = "user-1-1")
@Test
void should_ThrowException_When_RequestingReviewAndTaskIsInReviewByDifferentOwner()
throws Exception {
Task task = createTaskInReviewByUser("user-1-2").buildAndStore(taskService);
ThrowingCallable call = () -> taskService.requestChanges(task.getId());
InvalidOwnerException e = catchThrowableOfType(call, InvalidOwnerException.class);
assertThat(e.getCurrentUserId()).isEqualTo("user-1-1");
assertThat(e.getTaskId()).isEqualTo(task.getId());
}
@WithAccessId(user = "user-1-1")
@TestFactory
Stream<DynamicTest> should_ThrowException_When_RequestingChangesAndTaskIsNotInReview() {
List<TaskState> invalidStates = Arrays.asList(EnumUtil.allValuesExceptFor(TaskState.IN_REVIEW));
ThrowingConsumer<TaskState> test =
state -> {
Task task = createDefaultTask().state(state).buildAndStore(taskService);
ThrowingCallable call = () -> taskService.requestChanges(task.getId());
InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class);
assertThat(e.getRequiredTaskStates()).containsExactly(TaskState.IN_REVIEW);
assertThat(e.getTaskState()).isEqualTo(state);
assertThat(e.getTaskId()).isEqualTo(task.getId());
};
return DynamicTest.stream(invalidStates.iterator(), TaskState::name, test);
}
@WithAccessId(user = "user-1-2")
@Test
void should_ThrowException_When_UserHasNoWorkbasketPermission() throws Exception {
Task task = createTaskInReviewByUser("user-1-1").buildAndStore(taskService, "user-1-1");
ThrowingCallable call = () -> taskService.requestChanges(task.getId());
MismatchedWorkbasketPermissionException e =
catchThrowableOfType(call, MismatchedWorkbasketPermissionException.class);
assertThat(e.getRequiredPermissions()).containsExactly(WorkbasketPermission.READ);
assertThat(e.getCurrentUserId()).isEqualTo("user-1-2");
assertThat(e.getWorkbasketId()).isEqualTo(defaultWorkbasketSummary.getId());
assertThat(e.getDomain()).isNull();
assertThat(e.getWorkbasketKey()).isNull();
}
@WithAccessId(user = "user-1-1")
@TestFactory
Stream<DynamicTest> should_ThrowException_When_ForceRequestChangesAndTaskIsInEndState() {
List<TaskState> endStates = Arrays.asList(TaskState.END_STATES);
ThrowingConsumer<TaskState> test =
state -> {
Task task = createDefaultTask().state(state).buildAndStore(taskService);
ThrowingCallable call = () -> taskService.forceRequestChanges(task.getId());
InvalidTaskStateException e = catchThrowableOfType(call, InvalidTaskStateException.class);
assertThat(e.getRequiredTaskStates())
.containsExactlyInAnyOrder(EnumUtil.allValuesExceptFor(TaskState.END_STATES));
assertThat(e.getTaskState()).isEqualTo(state);
assertThat(e.getTaskId()).isEqualTo(task.getId());
};
return DynamicTest.stream(endStates.iterator(), TaskState::name, test);
}
private TaskBuilder createTaskInReviewByUser(String owner) {
return createDefaultTask().owner(owner).state(TaskState.IN_REVIEW);
}
private TaskBuilder createDefaultTask() {
return TaskBuilder.newTask()
.classificationSummary(defaultClassificationSummary)
.workbasketSummary(defaultWorkbasketSummary)
.primaryObjRef(defaultObjectReference);
}
}

View File

@ -64,7 +64,7 @@ class RequestReviewAccTest {
@WithAccessId(user = "user-1-1")
@Test
void should_RequestReview_WhenTaskIsClaimed() throws Exception {
void should_RequestReview_When_TaskIsClaimed() throws Exception {
Instant now = Instant.now();
Task task = createTaskClaimedByUser("user-1-1").buildAndStore(taskService);
@ -77,7 +77,7 @@ class RequestReviewAccTest {
@WithAccessId(user = "user-1-1")
@Test
void should_ForceRequestReview_WhenTaskIsClaimedByDifferentUser() throws Exception {
void should_ForceRequestReview_When_TaskIsClaimedByDifferentUser() throws Exception {
Instant now = Instant.now();
Task task = createTaskClaimedByUser("user-1-2").buildAndStore(taskService);
@ -90,7 +90,7 @@ class RequestReviewAccTest {
@WithAccessId(user = "user-1-1")
@Test
void should_ForceRequestReview_WhenTaskIsInReviewByDifferentUser() throws Exception {
void should_ForceRequestReview_When_TaskIsInReviewByDifferentUser() throws Exception {
Instant now = Instant.now();
Task task =
createTaskClaimedByUser("user-1-2").state(TaskState.IN_REVIEW).buildAndStore(taskService);
@ -104,7 +104,7 @@ class RequestReviewAccTest {
@WithAccessId(user = "user-1-1")
@TestFactory
Stream<DynamicTest> should_ForceRequestReview_WhenTaskIsNotInEndState() {
Stream<DynamicTest> should_ForceRequestReview_When_TaskIsNotInEndState() {
List<TaskState> testCases = Arrays.asList(EnumUtil.allValuesExceptFor(TaskState.END_STATES));
ThrowingConsumer<TaskState> test =
state -> {

View File

@ -6,6 +6,7 @@ public enum TaskHistoryEventType {
CLAIMED("CLAIMED"),
CLAIM_CANCELLED("CLAIM_CANCELLED"),
REQUESTED_REVIEW("REQUESTED_REVIEW"),
CHANGES_REQUESTED("CHANGES_REQUESTED"),
COMPLETED("COMPLETED"),
CANCELLED("CANCELLED"),
TERMINATED("TERMINATED"),

View File

@ -0,0 +1,13 @@
package pro.taskana.spi.history.api.events.task;
import pro.taskana.task.api.models.Task;
/** The TaskRequestChangesEvent is fired if changes on a {@linkplain Task} are requested. */
public class TaskRequestChangesEvent extends TaskHistoryEvent {
public TaskRequestChangesEvent(String id, Task task, String userId, String details) {
super(id, task, userId, details);
eventType = (TaskHistoryEventType.CHANGES_REQUESTED.getName());
created = task.getModified();
}
}

View File

@ -133,6 +133,42 @@ public interface TaskService {
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException;
/**
* Request changes for an existing {@linkplain Task} that is in {@linkplain TaskState#IN_REVIEW}.
* The {@linkplain TaskState} is changed to {@linkplain TaskState#READY} after changes have been
* requested.
*
* @param taskId the {@linkplain Task#getId() id} of the specified {@linkplain Task}
* @return the {@linkplain Task} after changes have been requested
* @throws InvalidTaskStateException if the {@linkplain Task#getState() state} of the {@linkplain
* Task} with taskId is not in {@linkplain TaskState#IN_REVIEW}
* @throws TaskNotFoundException if the {@linkplain Task} with taskId wasn't found
* @throws InvalidOwnerException if the {@linkplain Task} is claimed by another user
* @throws NotAuthorizedException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the {@linkplain Workbasket} the {@linkplain Task} is in
*/
Task requestChanges(String taskId)
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException;
/**
* Request changes for an existing {@linkplain Task} even if the current user is not the
* {@linkplain Task#getOwner() owner} or the Task is not in {@linkplain TaskState#IN_REVIEW} yet.
* The {@linkplain TaskState} is changed to {@linkplain TaskState#READY} after changes have been
* requested.
*
* @param taskId the {@linkplain Task#getId() id} of the specified {@linkplain Task}
* @return the {@linkplain Task} after changes have been requested
* @throws InvalidTaskStateException cannot be thrown
* @throws TaskNotFoundException if the {@linkplain Task} with taskId wasn't found
* @throws InvalidOwnerException if the {@linkplain Task} is claimed by another user
* @throws NotAuthorizedException if the current user has no {@linkplain
* WorkbasketPermission#READ} for the {@linkplain Workbasket} the {@linkplain Task} is in
*/
Task forceRequestChanges(String taskId)
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException;
/**
* Complete a claimed {@linkplain Task} as {@linkplain Task#getOwner() owner} or {@linkplain
* TaskanaRole#ADMIN} and update {@linkplain Task#getState() state} and timestamps.

View File

@ -47,6 +47,7 @@ import pro.taskana.spi.history.api.events.task.TaskClaimCancelledEvent;
import pro.taskana.spi.history.api.events.task.TaskClaimedEvent;
import pro.taskana.spi.history.api.events.task.TaskCompletedEvent;
import pro.taskana.spi.history.api.events.task.TaskCreatedEvent;
import pro.taskana.spi.history.api.events.task.TaskRequestChangesEvent;
import pro.taskana.spi.history.api.events.task.TaskRequestReviewEvent;
import pro.taskana.spi.history.api.events.task.TaskTerminatedEvent;
import pro.taskana.spi.history.api.events.task.TaskUpdatedEvent;
@ -182,6 +183,20 @@ public class TaskServiceImpl implements TaskService {
return requestReview(taskId, true);
}
@Override
public Task requestChanges(String taskId)
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException {
return requestChanges(taskId, false);
}
@Override
public Task forceRequestChanges(String taskId)
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException {
return requestChanges(taskId, true);
}
@Override
public Task completeTask(String taskId)
throws TaskNotFoundException, InvalidOwnerException, InvalidStateException,
@ -1251,6 +1266,53 @@ public class TaskServiceImpl implements TaskService {
return task;
}
private Task requestChanges(String taskId, boolean force)
throws InvalidTaskStateException, TaskNotFoundException, NotAuthorizedException,
InvalidOwnerException {
String userId = taskanaEngine.getEngine().getCurrentUserContext().getUserid();
TaskImpl task;
try {
taskanaEngine.openConnection();
task = (TaskImpl) getTask(taskId);
TaskImpl oldTask = duplicateTaskExactly(task);
if (force && task.getState().isEndState()) {
throw new InvalidTaskStateException(
task.getId(), task.getState(), EnumUtil.allValuesExceptFor(TaskState.END_STATES));
}
if (!force && task.getState() != TaskState.IN_REVIEW) {
throw new InvalidTaskStateException(task.getId(), task.getState(), TaskState.IN_REVIEW);
}
if (!force && !task.getOwner().equals(userId)) {
throw new InvalidOwnerException(userId, task.getId());
}
task.setState(TaskState.READY);
task.setOwner(null);
task.setModified(Instant.now());
taskMapper.update(task);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Requested changes for Task '{}' by user '{}'.", taskId, userId);
}
if (historyEventManager.isEnabled()) {
String changeDetails =
ObjectAttributeChangeDetector.determineChangesInAttributes(oldTask, task);
historyEventManager.createEvent(
new TaskRequestChangesEvent(
IdGenerator.generateWithPrefix(IdGenerator.ID_PREFIX_TASK_HISTORY_EVENT),
task,
taskanaEngine.getEngine().getCurrentUserContext().getUserid(),
changeDetails));
}
} finally {
taskanaEngine.returnConnection();
}
return task;
}
private static void claimActionsOnTask(TaskSummaryImpl task, String userId, Instant now) {
task.setOwner(userId);
task.setModified(now);

View File

@ -50,7 +50,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
taskService.completeTask(taskId);
long totalTasksCount = taskService.createTaskQuery().count();
assertThat(totalTasksCount).isEqualTo(98);
assertThat(totalTasksCount).isEqualTo(99);
taskanaEngine.getConfiguration().setTaskCleanupJobAllCompletedSameParentBusiness(false);
@ -58,14 +58,14 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
job.run();
totalTasksCount = taskService.createTaskQuery().count();
assertThat(totalTasksCount).isEqualTo(79);
assertThat(totalTasksCount).isEqualTo(80);
}
@WithAccessId(user = "admin")
@Test
void shouldCleanCompletedTasksUntilDateWithSameParentBussiness() throws Exception {
long totalTasksCount = taskService.createTaskQuery().count();
assertThat(totalTasksCount).isEqualTo(97);
assertThat(totalTasksCount).isEqualTo(98);
taskanaEngine.getConfiguration().setTaskCleanupJobAllCompletedSameParentBusiness(true);
@ -84,7 +84,7 @@ class TaskCleanupJobAccTest extends AbstractAccTest {
job.run();
totalTasksCount = taskService.createTaskQuery().count();
assertThat(totalTasksCount).isEqualTo(77);
assertThat(totalTasksCount).isEqualTo(78);
}
@WithAccessId(user = "admin")

View File

@ -166,7 +166,7 @@ class SetOwnerAccTest extends AbstractAccTest {
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
BulkOperationResults<String, TaskanaException> results =
taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds);
assertThat(allTaskSummaries).hasSize(97);
assertThat(allTaskSummaries).hasSize(98);
assertThat(results.containsErrors()).isTrue();
Condition<Object> invalidTaskStateException =
@ -177,11 +177,11 @@ class SetOwnerAccTest extends AbstractAccTest {
c -> c.getClass() == MismatchedWorkbasketPermissionException.class,
"MismatchedWorkbasketPermissionException");
assertThat(results.getErrorMap())
.hasSize(95)
.hasSize(96)
.extractingFromEntries(Entry::getValue)
.hasOnlyElementsOfTypes(InvalidTaskStateException.class, NotAuthorizedException.class)
.areExactly(37, invalidTaskStateException)
.areExactly(58, mismatchedWorkbasketPermissionException);
.areExactly(59, mismatchedWorkbasketPermissionException);
}
@WithAccessId(user = "admin")
@ -193,10 +193,10 @@ class SetOwnerAccTest extends AbstractAccTest {
allTaskSummaries.stream().map(TaskSummary::getId).collect(Collectors.toList());
BulkOperationResults<String, TaskanaException> results =
taskanaEngine.getTaskService().setOwnerOfTasks("theWorkaholic", allTaskIds);
assertThat(allTaskSummaries).hasSize(97);
assertThat(allTaskSummaries).hasSize(98);
assertThat(results.containsErrors()).isTrue();
assertThat(results.getErrorMap())
.hasSize(49)
.hasSize(50)
.extractingFromEntries(Entry::getValue)
.hasOnlyElementsOfType(InvalidStateException.class);
}

View File

@ -254,7 +254,7 @@ class QueryTasksAccTest extends AbstractAccTest {
throws InvalidArgumentException {
List<TaskSummary> query =
taskService.createTaskQuery().customAttributeIn(TaskCustomField.CUSTOM_1, "").list();
assertThat(query).hasSize(94);
assertThat(query).hasSize(95);
}
@Nested
@ -280,7 +280,7 @@ class QueryTasksAccTest extends AbstractAccTest {
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"%ert"}, 3),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"dd%"}, 1),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"%dd_"}, 1),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 97),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"%"}, 98),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"___"}, 4),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"___"}, 4));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
@ -312,22 +312,22 @@ class QueryTasksAccTest extends AbstractAccTest {
// carefully constructed to always return exactly 2 results
List<Triplet<TaskCustomField, String[], Integer>> list =
List.of(
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 96),
Triplet.of(TaskCustomField.CUSTOM_1, new String[] {"custom1"}, 97),
Triplet.of(TaskCustomField.CUSTOM_2, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 96),
Triplet.of(TaskCustomField.CUSTOM_3, new String[] {"custom3"}, 97),
Triplet.of(TaskCustomField.CUSTOM_4, new String[] {""}, 2),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 90),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 93),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 95),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 97),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 96),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 96),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 96),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 96),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 96),
Triplet.of(TaskCustomField.CUSTOM_5, new String[] {"ew", "al", "el"}, 91),
Triplet.of(TaskCustomField.CUSTOM_6, new String[] {"11", "vvg"}, 94),
Triplet.of(TaskCustomField.CUSTOM_7, new String[] {"custom7", "ijk"}, 96),
Triplet.of(TaskCustomField.CUSTOM_8, new String[] {"not_existing"}, 98),
Triplet.of(TaskCustomField.CUSTOM_9, new String[] {"custom9"}, 97),
Triplet.of(TaskCustomField.CUSTOM_10, new String[] {"custom10"}, 97),
Triplet.of(TaskCustomField.CUSTOM_11, new String[] {"custom11"}, 97),
Triplet.of(TaskCustomField.CUSTOM_12, new String[] {"custom12"}, 97),
Triplet.of(TaskCustomField.CUSTOM_13, new String[] {"custom13"}, 97),
Triplet.of(TaskCustomField.CUSTOM_14, new String[] {"abc"}, 0),
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 96),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 96));
Triplet.of(TaskCustomField.CUSTOM_15, new String[] {"custom15"}, 97),
Triplet.of(TaskCustomField.CUSTOM_16, new String[] {"custom16"}, 97));
assertThat(list).hasSameSizeAs(TaskCustomField.values());
return DynamicTest.stream(
@ -376,7 +376,7 @@ class QueryTasksAccTest extends AbstractAccTest {
throws InvalidArgumentException {
List<TaskSummary> results =
taskService.createTaskQuery().customAttributeIn(TaskCustomField.CUSTOM_9, "").list();
assertThat(results).hasSize(95);
assertThat(results).hasSize(96);
}
@WithAccessId(user = "admin")
@ -388,7 +388,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeIn(TaskCustomField.CUSTOM_9, "", null)
.list();
assertThat(results).hasSize(96);
assertThat(results).hasSize(97);
}
@WithAccessId(user = "admin")
@ -400,14 +400,14 @@ class QueryTasksAccTest extends AbstractAccTest {
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.list();
assertThat(results).hasSize(96);
assertThat(results).hasSize(97);
results =
taskService
.createTaskQuery()
.customAttributeNotIn(TaskCustomField.CUSTOM_9, null, "custom9")
.list();
assertThat(results).hasSize(95);
assertThat(results).hasSize(96);
results =
taskService
@ -415,7 +415,7 @@ class QueryTasksAccTest extends AbstractAccTest {
.customAttributeNotIn(TaskCustomField.CUSTOM_9, new String[] {null})
.customAttributeNotIn(TaskCustomField.CUSTOM_10, "custom10")
.list();
assertThat(results).hasSize(95);
assertThat(results).hasSize(96);
}
@WithAccessId(user = "admin")

View File

@ -48,7 +48,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
switch (taskanaEngine.getCurrentUserContext().getUserid()) {
case "admin":
case "taskadmin":
expectedSize = 97;
expectedSize = 98;
break;
case "businessadmin":
case "monitor":
@ -58,7 +58,7 @@ class QueryTasksByRoleAccTest extends AbstractAccTest {
expectedSize = 26;
break;
case "user-1-1":
expectedSize = 7;
expectedSize = 8;
break;
case "user-taskrouter":
expectedSize = 0;

View File

@ -70,7 +70,7 @@ class QueryTasksByTimeIntervalsAccTest extends AbstractAccTest {
List<TaskSummary> results =
taskService.createTaskQuery().createdWithin(interval1).orderByCreated(asc).list();
assertThat(results).hasSize(38);
assertThat(results).hasSize(39);
TaskSummary previousSummary = null;
for (TaskSummary taskSummary : results) {
Instant cr = taskSummary.getCreated();

View File

@ -31,9 +31,9 @@ class QueryTasksWithPaginationAccTest extends AbstractAccTest {
void testQueryAllPaged() {
TaskQuery taskQuery = taskanaEngine.getTaskService().createTaskQuery();
long numberOfTasks = taskQuery.count();
assertThat(numberOfTasks).isEqualTo(97);
assertThat(numberOfTasks).isEqualTo(98);
List<TaskSummary> tasks = taskQuery.orderByDue(DESCENDING).list();
assertThat(tasks).hasSize(97);
assertThat(tasks).hasSize(98);
List<TaskSummary> tasksp = taskQuery.orderByDue(DESCENDING).listPage(4, 5);
assertThat(tasksp).hasSize(5);
tasksp = taskQuery.orderByDue(DESCENDING).listPage(5, 5);

View File

@ -181,6 +181,8 @@ class DeleteWorkbasketAccTest extends AbstractAccTest {
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:000000000000000000000000000000000066");
taskService.forceCompleteTask(task.getId());
task = (TaskImpl) taskService.getTask("TKI:100000000000000000000000000000000066");
taskService.forceCompleteTask(task.getId());
boolean canBeDeletedNow = workbasketService.deleteWorkbasket(wb.getId());
assertThat(canBeDeletedNow).isFalse();