TSK-1280: Sort options for task comments in REST-API

This commit is contained in:
Joerg Heffner 2020-06-04 13:37:55 +02:00 committed by gitgoodjhe
parent d07459fe26
commit 38c9adea5c
6 changed files with 172 additions and 25 deletions

View File

@ -62,7 +62,7 @@ public class UpdateTaskCommentAccTest extends AbstractAccTest {
List<TaskComment> taskComments =
taskService.getTaskComments("TKI:000000000000000000000000000000000000");
assertThat(taskComments).hasSize(3);
assertThat(taskComments.get(1).getTextField()).isEqualTo("some other text in textfield");
assertThat(taskComments.get(0).getTextField()).isEqualTo("some other text in textfield");
TaskComment taskComment =
taskService.getTaskComment("TCI:000000000000000000000000000000000001");
@ -74,7 +74,7 @@ public class UpdateTaskCommentAccTest extends AbstractAccTest {
// make sure the task comment wasn't updated
List<TaskComment> taskCommentsAfterUpdateAttempt =
taskService.getTaskComments("TKI:000000000000000000000000000000000000");
assertThat(taskCommentsAfterUpdateAttempt.get(1).getTextField())
assertThat(taskCommentsAfterUpdateAttempt.get(0).getTextField())
.isEqualTo("some other text in textfield");
}

View File

@ -1,9 +1,9 @@
-- TASK_COMMENT TABLE ID , TASK_ID ,TEXTFIELD ,CREATOR ,COMPLETED ,MODIFIED
-- TASK_COMMENT TABLE ID , TASK_ID ,TEXTFIELD ,CREATOR ,CREATED ,MODIFIED
-- TaskComments for GetTaskCommentAccTest + UpdateTaskCommentAccTest
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000000', 'TKI:000000000000000000000000000000000000', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000001', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000002', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000000', 'TKI:000000000000000000000000000000000000', 'some text in textfield', 'user_1_1', '2017-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000001', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2015-01-29 15:55:00', '2022-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000002', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2020-01-29 15:55:00', '2021-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000003', 'TKI:000000000000000000000000000000000025', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
-- TaskComments for DeleteTaskCommentAccTest
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000004', 'TKI:000000000000000000000000000000000001', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');

View File

@ -1,9 +1,9 @@
-- TASK_COMMENT TABLE ID , TASK_ID ,TEXTFIELD ,CREATOR ,COMPLETED ,MODIFIED
-- TASK_COMMENT TABLE ID , TASK_ID ,TEXTFIELD ,CREATOR ,CREATED ,MODIFIED
-- TaskComments for GetTaskCommentAccTest + UpdateTaskCommentAccTest
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000000', 'TKI:000000000000000000000000000000000000', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000001', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000002', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000000', 'TKI:000000000000000000000000000000000000', 'some text in textfield', 'user_1_1', '2017-01-29 15:55:00', '2018-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000001', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2015-01-29 15:55:00', '2022-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000002', 'TKI:000000000000000000000000000000000000', 'some other text in textfield', 'user_1_1', '2020-01-29 15:55:00', '2021-01-30 15:55:00');
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000003', 'TKI:000000000000000000000000000000000025', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');
-- TaskComments for DeleteTaskCommentAccTest
INSERT INTO TASK_COMMENT VALUES('TCI:000000000000000000000000000000000004', 'TKI:000000000000000000000000000000000001', 'some text in textfield', 'user_1_1', '2018-01-29 15:55:00', '2018-01-30 15:55:00');

View File

@ -1,5 +1,6 @@
package pro.taskana.task.rest;
import java.util.Comparator;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -9,12 +10,14 @@ import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.common.api.exceptions.ConcurrencyException;
@ -29,13 +32,21 @@ import pro.taskana.task.api.models.TaskComment;
import pro.taskana.task.rest.assembler.TaskCommentRepresentationModelAssembler;
import pro.taskana.task.rest.models.TaskCommentRepresentationModel;
/** Controller for all {@link TaskComment} related endpoints. */
/**
* Controller for all {@link TaskComment} related endpoints.
*/
@RestController
@EnableHypermediaSupport(type = HypermediaType.HAL)
public class TaskCommentController {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskCommentController.class);
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private static final String CREATED = "created";
private static final String MODIFIED = "modified";
private final TaskService taskService;
private final TaskCommentRepresentationModelAssembler taskCommentRepresentationModelAssembler;
@ -52,7 +63,7 @@ public class TaskCommentController {
public ResponseEntity<TaskCommentRepresentationModel> getTaskComment(
@PathVariable String taskCommentId)
throws NotAuthorizedException, TaskNotFoundException, TaskCommentNotFoundException,
InvalidArgumentException {
InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to getTaskComment(taskCommentId= {})", taskCommentId);
}
@ -75,13 +86,19 @@ public class TaskCommentController {
@GetMapping(path = Mapping.URL_TASK_GET_POST_COMMENTS)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>> getTaskComments(
@PathVariable String taskId) throws NotAuthorizedException, TaskNotFoundException {
@PathVariable String taskId,
@RequestParam(required = false) MultiValueMap<String, String> params)
throws NotAuthorizedException, TaskNotFoundException, InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to getTaskComments(taskId= {})", taskId);
}
List<TaskComment> taskComments = taskService.getTaskComments(taskId);
//TODO Maybe introduce a query for task comments
applySortingParams(taskComments, params);
TaskanaPagedModel<TaskCommentRepresentationModel> taskCommentListResource =
taskCommentRepresentationModelAssembler.toPageModel(taskComments, null);
@ -100,7 +117,7 @@ public class TaskCommentController {
public ResponseEntity<TaskCommentRepresentationModel> deleteTaskComment(
@PathVariable String taskCommentId)
throws NotAuthorizedException, TaskNotFoundException, TaskCommentNotFoundException,
InvalidArgumentException {
InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to deleteTaskComment(taskCommentId= {})", taskCommentId);
}
@ -122,7 +139,7 @@ public class TaskCommentController {
@PathVariable String taskCommentId,
@RequestBody TaskCommentRepresentationModel taskCommentRepresentationModel)
throws NotAuthorizedException, TaskNotFoundException, TaskCommentNotFoundException,
InvalidArgumentException, ConcurrencyException {
InvalidArgumentException, ConcurrencyException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
"Entry to updateTaskComment(taskCommentId= {}, taskCommentResource= {})",
@ -186,4 +203,47 @@ public class TaskCommentController {
return result;
}
private List<TaskComment> applySortingParams(List<TaskComment> taskComments,
MultiValueMap<String, String> params) throws InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("Entry to applySortingParams(taskComments= {}, params= {})", taskComments, params);
}
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
switch (sortBy) {
case (CREATED):
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
taskComments.sort(Comparator.comparing(TaskComment::getCreated).reversed());
} else {
taskComments.sort(Comparator.comparing(TaskComment::getCreated));
}
break;
case (MODIFIED):
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
taskComments.sort(Comparator.comparing(TaskComment::getModified).reversed());
} else {
taskComments.sort(Comparator.comparing(TaskComment::getModified));
}
break;
default:
throw new InvalidArgumentException("Unknown sort attribute: " + sortBy);
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", taskComments);
}
return taskComments;
}
}

View File

@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.time.Instant;
import java.util.Comparator;
import org.assertj.core.api.ThrowableAssert.ThrowingCallable;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
@ -25,19 +26,23 @@ import pro.taskana.common.rest.TaskanaSpringBootTest;
import pro.taskana.common.rest.models.TaskanaPagedModel;
import pro.taskana.task.rest.models.TaskCommentRepresentationModel;
/** Test TaskCommentController. */
/**
* Test TaskCommentController.
*/
@TaskanaSpringBootTest
class TaskCommentControllerIntTest {
private static final ParameterizedTypeReference<TaskanaPagedModel<TaskCommentRepresentationModel>>
TASK_COMMENT_PAGE_MODEL_TYPE =
new ParameterizedTypeReference<TaskanaPagedModel<TaskCommentRepresentationModel>>() {};
new ParameterizedTypeReference<TaskanaPagedModel<TaskCommentRepresentationModel>>() {
};
private static RestTemplate template;
@Value("${taskana.schemaName:TASKANA}")
public String schemaName;
@Autowired RestHelper restHelper;
@Autowired
RestHelper restHelper;
@BeforeAll
static void init() {
@ -83,6 +88,85 @@ class TaskCommentControllerIntTest {
.isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void should_ReturnSortedAndOrederedTaskCommentsSortedByModified_When_UsingSortAndOrderParams() {
String url =
restHelper.toUrl(Mapping.URL_TASK_GET_POST_COMMENTS,
"TKI:000000000000000000000000000000000000");
ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>>
getTaskCommentsSortedByModifiedOrderedByDescendingResponse = template.exchange(
url + "?sort-by=modified&order=desc",
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
assertThat(getTaskCommentsSortedByModifiedOrderedByDescendingResponse.getBody().getContent())
.hasSize(3)
.extracting(TaskCommentRepresentationModel::getModified)
.isSortedAccordingTo(Comparator.reverseOrder());
ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>>
getTaskCommentsSortedByModifiedOrderedByAscendingResponse = template.exchange(
url + "?sort-by=modified",
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
assertThat(getTaskCommentsSortedByModifiedOrderedByAscendingResponse.getBody().getContent())
.hasSize(3)
.extracting(TaskCommentRepresentationModel::getModified)
.isSortedAccordingTo(Comparator.naturalOrder());
ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>>
getTaskCommentsSortedByCreatedOrderedByDescendingResponse = template.exchange(
url + "?sort-by=created&order=desc",
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
assertThat(getTaskCommentsSortedByCreatedOrderedByDescendingResponse.getBody().getContent())
.hasSize(3)
.extracting(TaskCommentRepresentationModel::getCreated)
.isSortedAccordingTo(Comparator.reverseOrder());
ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>>
getTaskCommentsSortedByCreatedOrderedByAscendingResponse = template.exchange(
url + "?sort-by=created",
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
assertThat(getTaskCommentsSortedByCreatedOrderedByAscendingResponse.getBody().getContent())
.hasSize(3)
.extracting(TaskCommentRepresentationModel::getCreated)
.isSortedAccordingTo(Comparator.naturalOrder());
}
@Test
void should_ThrowException_When_UsingInvalidSortParam() {
String url =
restHelper.toUrl(Mapping.URL_TASK_GET_POST_COMMENTS,
"TKI:000000000000000000000000000000000000");
ThrowingCallable httpCall =
() -> {
template.exchange(
url + "?sort-by=invalidSortParam",
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersUser_1_1()),
TASK_COMMENT_PAGE_MODEL_TYPE);
};
assertThatThrownBy(httpCall)
.extracting(ex -> ((HttpClientErrorException) ex).getStatusCode())
.isEqualTo(HttpStatus.BAD_REQUEST);
}
@Disabled("Disabled until Authorization check is up!")
@Test
void should_FailToReturnTaskComment_When_TaskIstNotVisible() {
@ -219,7 +303,6 @@ class TaskCommentControllerIntTest {
@Test
void should_FailToUpdateTaskComment_When_TaskCommentIdInResourceDoesNotMatchPathVariable() {
String url =
restHelper.toUrl(Mapping.URL_TASK_COMMENT, "TCI:000000000000000000000000000000000000");
@ -259,12 +342,12 @@ class TaskCommentControllerIntTest {
ResponseEntity<TaskanaPagedModel<TaskCommentRepresentationModel>>
getTaskCommentsBeforeDeleteionResponse =
template.exchange(
restHelper.toUrl(
Mapping.URL_TASK_COMMENTS, "TKI:000000000000000000000000000000000001"),
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
template.exchange(
restHelper.toUrl(
Mapping.URL_TASK_COMMENTS, "TKI:000000000000000000000000000000000001"),
HttpMethod.GET,
new HttpEntity<String>(restHelper.getHeadersAdmin()),
TASK_COMMENT_PAGE_MODEL_TYPE);
assertThat(getTaskCommentsBeforeDeleteionResponse.getBody().getContent()).hasSize(2);
String url =

View File

@ -321,6 +321,10 @@ include::{snippets}/GetAllTaskCommentsForSpecificTaskDocTest/http-response.adoc[
include::{snippets}/GetAllTaskCommentsForSpecificTaskDocTest/response-fields.adoc[]
=== Sort options
sort-by={ created | modified } | order={ desc | asc }
=== Get a specific task comment