TSK-1331: allowed multiple sortBy declarations in REST api

This commit is contained in:
Mustapha Zorgati 2020-07-09 21:12:54 +02:00
parent 4bba93c5a9
commit 9e44ca6140
10 changed files with 540 additions and 453 deletions

View File

@ -6,6 +6,7 @@ import java.time.ZoneId;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.PagedModel.PageMetadata;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.http.HttpStatus;
@ -19,10 +20,10 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.TaskanaEngineConfiguration;
import pro.taskana.common.api.BaseQuery;
import pro.taskana.common.api.TimeInterval;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.rest.AbstractPagingController;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.simplehistory.impl.HistoryEventImpl;
import pro.taskana.simplehistory.impl.SimpleHistoryServiceImpl;
import pro.taskana.simplehistory.query.HistoryQuery;
@ -42,97 +43,53 @@ public class TaskHistoryEventController extends AbstractPagingController {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskHistoryEventController.class);
private static final String LIKE = "%";
private static final String BUSINESS_PROCESS_ID = "business-process-id";
private static final String BUSINESS_PROCESS_ID_LIKE = "business-process-id-like";
private static final String PARENT_BUSINESS_PROCESS_ID = "parent-business-process-id";
private static final String PARENT_BUSINESS_PROCESS_ID_LIKE = "parent-business-process-id-like";
private static final String TASK_ID = "task-id";
private static final String TASK_ID_LIKE = "task-id-like";
private static final String EVENT_TYPE = "event-type";
private static final String EVENT_TYPE_LIKE = "event-type-like";
private static final String CREATED = "created";
private static final String USER_ID = "user-id";
private static final String USER_ID_LIKE = "user-id-like";
private static final String DOMAIN = "domain";
private static final String WORKBASKET_KEY = "workbasket-key";
private static final String WORKBASKET_KEY_LIKE = "workbasket-key-like";
private static final String POR_COMPANY = "por-company";
private static final String POR_COMPANY_LIKE = "por-company-like";
private static final String POR_SYSTEM = "por-system";
private static final String POR_SYSTEM_LIKE = "por-system-like";
private static final String POR_INSTANCE = "por-instance";
private static final String POR_INSTANCE_LIKE = "por-instance-like";
private static final String POR_TYPE = "por-type";
private static final String POR_TYPE_LIKE = "por-type-like";
private static final String POR_VALUE = "por-value";
private static final String POR_VALUE_LIKE = "por-value-like";
private static final String TASK_CLASSIFICATION_KEY = "task-classification-key";
private static final String TASK_CLASSIFICATION_KEY_LIKE = "task-classification-key-like";
private static final String TASK_CLASSIFICATION_CATEGORY = "task-classification-category";
private static final String TASK_CLASSIFICATION_CATEGORY_LIKE =
"task-classification-category-like";
private static final String ATTACHMENT_CLASSIFICATION_KEY = "attachment-classification-key";
private static final String ATTACHMENT_CLASSIFICATION_KEY_LIKE =
"attachment-classification-key-like";
private static final String CUSTOM_1 = "custom-1";
private static final String CUSTOM_1_LIKE = "custom-1-like";
private static final String CUSTOM_2 = "custom-2";
private static final String CUSTOM_2_LIKE = "custom-2-like";
private static final String CUSTOM_3 = "custom-3";
private static final String CUSTOM_3_LIKE = "custom-3-like";
private static final String CUSTOM_4 = "custom-4";
private static final String CUSTOM_4_LIKE = "custom-4-like";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private static final String PAGING_PAGE = "page";
private static final String PAGING_PAGE_SIZE = "page-size";
private final SimpleHistoryServiceImpl simpleHistoryService;
private final TaskHistoryEventResourceAssembler taskHistoryEventResourceAssembler;
@Autowired
public TaskHistoryEventController(
TaskanaEngineConfiguration taskanaEngineConfiguration,
SimpleHistoryServiceImpl simpleHistoryServiceImpl,
@ -153,7 +110,7 @@ public class TaskHistoryEventController extends AbstractPagingController {
HistoryQuery query = simpleHistoryService.createHistoryQuery();
query = applySortingParams(query, params);
applyFilterParams(query, params);
query = applyFilterParams(query, params);
PageMetadata pageMetadata = null;
List<HistoryEventImpl> historyEvents;
@ -214,90 +171,83 @@ public class TaskHistoryEventController extends AbstractPagingController {
LOGGER.debug("Entry to applySortingParams(params= {})", params);
}
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
BaseQuery.SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = BaseQuery.SortDirection.DESCENDING;
} else {
sortDirection = BaseQuery.SortDirection.ASCENDING;
}
switch (sortBy) {
case (BUSINESS_PROCESS_ID):
query = query.orderByBusinessProcessId(sortDirection);
break;
case (PARENT_BUSINESS_PROCESS_ID):
query = query.orderByParentBusinessProcessId(sortDirection);
break;
case (TASK_ID):
query = query.orderByTaskId(sortDirection);
break;
case (EVENT_TYPE):
query = query.orderByEventType(sortDirection);
break;
case (CREATED):
query = query.orderByCreated(sortDirection);
break;
case (USER_ID):
query = query.orderByUserId(sortDirection);
break;
case (DOMAIN):
query = query.orderByDomain(sortDirection);
break;
case (WORKBASKET_KEY):
query = query.orderByWorkbasketKey(sortDirection);
break;
case (POR_COMPANY):
query = query.orderByPorCompany(sortDirection);
break;
case (POR_SYSTEM):
query = query.orderByPorSystem(sortDirection);
break;
case (POR_INSTANCE):
query = query.orderByPorInstance(sortDirection);
break;
case (POR_TYPE):
query = query.orderByPorType(sortDirection);
break;
case (POR_VALUE):
query = query.orderByPorValue(sortDirection);
break;
case (TASK_CLASSIFICATION_KEY):
query = query.orderByTaskClassificationKey(sortDirection);
break;
case (TASK_CLASSIFICATION_CATEGORY):
query = query.orderByTaskClassificationCategory(sortDirection);
break;
case (ATTACHMENT_CLASSIFICATION_KEY):
query = query.orderByAttachmentClassificationKey(sortDirection);
break;
case (CUSTOM_1):
query = query.orderByCustomAttribute(1, sortDirection);
break;
case (CUSTOM_2):
query = query.orderByCustomAttribute(2, sortDirection);
break;
case (CUSTOM_3):
query = query.orderByCustomAttribute(3, sortDirection);
break;
case (CUSTOM_4):
query = query.orderByCustomAttribute(4, sortDirection);
break;
default:
throw new IllegalArgumentException("Unknown order '" + sortBy + "'");
}
}
params.remove(SORT_BY);
params.remove(SORT_DIRECTION);
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
switch (sortBy) {
case (BUSINESS_PROCESS_ID):
query.orderByBusinessProcessId(sortDirection);
break;
case (PARENT_BUSINESS_PROCESS_ID):
query.orderByParentBusinessProcessId(sortDirection);
break;
case (TASK_ID):
query.orderByTaskId(sortDirection);
break;
case (EVENT_TYPE):
query.orderByEventType(sortDirection);
break;
case (CREATED):
query.orderByCreated(sortDirection);
break;
case (USER_ID):
query.orderByUserId(sortDirection);
break;
case (DOMAIN):
query.orderByDomain(sortDirection);
break;
case (WORKBASKET_KEY):
query.orderByWorkbasketKey(sortDirection);
break;
case (POR_COMPANY):
query.orderByPorCompany(sortDirection);
break;
case (POR_SYSTEM):
query.orderByPorSystem(sortDirection);
break;
case (POR_INSTANCE):
query.orderByPorInstance(sortDirection);
break;
case (POR_TYPE):
query.orderByPorType(sortDirection);
break;
case (POR_VALUE):
query.orderByPorValue(sortDirection);
break;
case (TASK_CLASSIFICATION_KEY):
query.orderByTaskClassificationKey(sortDirection);
break;
case (TASK_CLASSIFICATION_CATEGORY):
query.orderByTaskClassificationCategory(sortDirection);
break;
case (ATTACHMENT_CLASSIFICATION_KEY):
query.orderByAttachmentClassificationKey(sortDirection);
break;
case (CUSTOM_1):
query.orderByCustomAttribute(1, sortDirection);
break;
case (CUSTOM_2):
query.orderByCustomAttribute(2, sortDirection);
break;
case (CUSTOM_3):
query.orderByCustomAttribute(3, sortDirection);
break;
case (CUSTOM_4):
query.orderByCustomAttribute(4, sortDirection);
break;
default:
throw new IllegalArgumentException("Unknown order '" + sortBy + "'");
}
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", query);
LOGGER.debug("Exit from applySortingParams(), returning: {}", query);
}
return query;
}
private void applyFilterParams(HistoryQuery query, MultiValueMap<String, String> params) {
private HistoryQuery applyFilterParams(HistoryQuery query, MultiValueMap<String, String> params) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applyFilterParams(query= {}, params= {})", query, params);
}
@ -485,6 +435,8 @@ public class TaskHistoryEventController extends AbstractPagingController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyFilterParams(), returning {}", query);
}
return query;
}
private TimeInterval getTimeIntervalOf(String[] created) {

View File

@ -0,0 +1,8 @@
package pro.taskana.common.internal.util;
@FunctionalInterface
public interface CheckedBiConsumer<T, U, E extends Throwable> {
void accept(T t, U u) throws E;
}

View File

@ -3,6 +3,7 @@ package pro.taskana.classification.rest;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.MediaTypes;
import org.springframework.hateoas.PagedModel.PageMetadata;
import org.springframework.hateoas.config.EnableHypermediaSupport;
@ -31,13 +32,13 @@ import pro.taskana.classification.rest.assembler.ClassificationRepresentationMod
import pro.taskana.classification.rest.assembler.ClassificationSummaryRepresentationModelAssembler;
import pro.taskana.classification.rest.models.ClassificationRepresentationModel;
import pro.taskana.classification.rest.models.ClassificationSummaryRepresentationModel;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.DomainNotFoundException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.AbstractPagingController;
import pro.taskana.common.rest.Mapping;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.common.rest.models.TaskanaPagedModel;
/** Controller for all {@link Classification} related endpoints. */
@ -48,56 +49,33 @@ public class ClassificationController extends AbstractPagingController {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassificationController.class);
private static final String LIKE = "%";
private static final String NAME = "name";
private static final String NAME_LIKE = "name-like";
private static final String KEY = "key";
private static final String DOMAIN = "domain";
private static final String CATEGORY = "category";
private static final String TYPE = "type";
private static final String CUSTOM_1_LIKE = "custom-1-like";
private static final String CUSTOM_2_LIKE = "custom-2-like";
private static final String CUSTOM_3_LIKE = "custom-3-like";
private static final String CUSTOM_4_LIKE = "custom-4-like";
private static final String CUSTOM_5_LIKE = "custom-5-like";
private static final String CUSTOM_6_LIKE = "custom-6-like";
private static final String CUSTOM_7_LIKE = "custom-7-like";
private static final String CUSTOM_8_LIKE = "custom-8-like";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private final ClassificationService classificationService;
private final ClassificationRepresentationModelAssembler modelAssembler;
private final ClassificationSummaryRepresentationModelAssembler summaryModelAssembler;
private final ClassificationRepresentationModelAssembler
classificationRepresentationModelAssembler;
private final ClassificationSummaryRepresentationModelAssembler
classificationSummaryRepresentationModelAssembler;
@Autowired
ClassificationController(
ClassificationService classificationService,
ClassificationRepresentationModelAssembler classificationRepresentationModelAssembler,
ClassificationSummaryRepresentationModelAssembler
classificationSummaryRepresentationModelAssembler) {
ClassificationRepresentationModelAssembler modelAssembler,
ClassificationSummaryRepresentationModelAssembler summaryModelAssembler) {
this.classificationService = classificationService;
this.classificationRepresentationModelAssembler = classificationRepresentationModelAssembler;
this.classificationSummaryRepresentationModelAssembler =
classificationSummaryRepresentationModelAssembler;
this.modelAssembler = modelAssembler;
this.summaryModelAssembler = summaryModelAssembler;
}
@GetMapping(path = Mapping.URL_CLASSIFICATIONS)
@ -110,16 +88,14 @@ public class ClassificationController extends AbstractPagingController {
}
ClassificationQuery query = classificationService.createClassificationQuery();
query = applyFilterParams(query, params);
query = applySortingParams(query, params);
applyFilterParams(query, params);
PageMetadata pageMetadata = getPageMetadata(params, query);
List<ClassificationSummary> classificationSummaries = getQueryList(query, pageMetadata);
ResponseEntity<TaskanaPagedModel<ClassificationSummaryRepresentationModel>> response =
ResponseEntity.ok(
classificationSummaryRepresentationModelAssembler.toPageModel(
classificationSummaries, pageMetadata));
ResponseEntity.ok(summaryModelAssembler.toPageModel(classificationSummaries, pageMetadata));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from getClassifications(), returning {}", response);
}
@ -137,7 +113,7 @@ public class ClassificationController extends AbstractPagingController {
Classification classification = classificationService.getClassification(classificationId);
ResponseEntity<ClassificationRepresentationModel> response =
ResponseEntity.ok(classificationRepresentationModelAssembler.toModel(classification));
ResponseEntity.ok(modelAssembler.toModel(classification));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from getClassification(), returning {}", response);
}
@ -154,13 +130,11 @@ public class ClassificationController extends AbstractPagingController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to createClassification(resource= {})", resource);
}
Classification classification =
classificationRepresentationModelAssembler.toEntityModel(resource);
Classification classification = modelAssembler.toEntityModel(resource);
classification = classificationService.createClassification(classification);
ResponseEntity<ClassificationRepresentationModel> response =
ResponseEntity.status(HttpStatus.CREATED)
.body(classificationRepresentationModelAssembler.toModel(classification));
ResponseEntity.status(HttpStatus.CREATED).body(modelAssembler.toModel(classification));
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from createClassification(), returning {}", response);
}
@ -184,11 +158,9 @@ public class ClassificationController extends AbstractPagingController {
ResponseEntity<ClassificationRepresentationModel> result;
if (classificationId.equals(resource.getClassificationId())) {
Classification classification =
classificationRepresentationModelAssembler.toEntityModel(resource);
Classification classification = modelAssembler.toEntityModel(resource);
classification = classificationService.updateClassification(classification);
result =
ResponseEntity.ok(classificationRepresentationModelAssembler.toModel(classification));
result = ResponseEntity.ok(modelAssembler.toModel(classification));
} else {
throw new InvalidArgumentException(
"ClassificationId ('"
@ -223,43 +195,35 @@ public class ClassificationController extends AbstractPagingController {
LOGGER.debug("Entry to applySortingParams(query= {}, params= {})", query, params);
}
// sorting
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = SortDirection.DESCENDING;
} else {
sortDirection = SortDirection.ASCENDING;
}
switch (sortBy) {
case (CATEGORY):
query = query.orderByCategory(sortDirection);
break;
case (DOMAIN):
query = query.orderByDomain(sortDirection);
break;
case (KEY):
query = query.orderByKey(sortDirection);
break;
case (NAME):
query = query.orderByName(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
}
params.remove(SORT_BY);
params.remove(SORT_DIRECTION);
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
switch (sortBy) {
case (CATEGORY):
query.orderByCategory(sortDirection);
break;
case (DOMAIN):
query.orderByDomain(sortDirection);
break;
case (KEY):
query.orderByKey(sortDirection);
break;
case (NAME):
query.orderByName(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", query);
}
return query;
}
private void applyFilterParams(ClassificationQuery query, MultiValueMap<String, String> params)
private ClassificationQuery applyFilterParams(
ClassificationQuery query, MultiValueMap<String, String> params)
throws InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applyFilterParams(query= {}, params= {})", query, params);
@ -330,5 +294,6 @@ public class ClassificationController extends AbstractPagingController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyFilterParams(), returning {}", query);
}
return query;
}
}

View File

@ -0,0 +1,80 @@
package pro.taskana.common.rest;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.MultiValueMap;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.internal.util.CheckedBiConsumer;
public class QueryHelper {
public static final String SORT_BY = "sort-by";
public static final String ORDER_DIRECTION = "order";
private static final Logger LOGGER = LoggerFactory.getLogger(QueryHelper.class);
private QueryHelper() {
// no op
}
public static void applyAndRemoveSortingParams(
MultiValueMap<String, String> params,
CheckedBiConsumer<String, SortDirection, InvalidArgumentException> consumer)
throws InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applyAndRemoveSortingParams(params= {})", params);
}
if (params == null || consumer == null) {
throw new InvalidArgumentException("params or consumer can't be null!");
}
List<String> allSortBy = params.remove(SORT_BY);
List<String> allOrderBy = params.remove(ORDER_DIRECTION);
verifyNotOnlyOrderByExists(allSortBy, allOrderBy);
verifyAmountOfSortByAndOrderByMatches(allSortBy, allOrderBy);
if (allSortBy != null) {
for (int i = 0; i < allSortBy.size(); i++) {
consumer.accept(allSortBy.get(i), getSortDirectionForIndex(allOrderBy, i));
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyAndRemoveSortingParams()");
}
}
private static SortDirection getSortDirectionForIndex(List<String> allOrderBy, int i) {
SortDirection sortDirection = SortDirection.ASCENDING;
if (allOrderBy != null && !allOrderBy.isEmpty() && "desc".equalsIgnoreCase(allOrderBy.get(i))) {
sortDirection = SortDirection.DESCENDING;
}
return sortDirection;
}
private static void verifyNotOnlyOrderByExists(List<String> allSortBy, List<String> allOrderBy)
throws InvalidArgumentException {
if (allSortBy == null && allOrderBy != null) {
throw new InvalidArgumentException(
String.format(
"Only '%s' were provided. Please also provide '%s' parameter(s)",
ORDER_DIRECTION, SORT_BY));
}
}
private static void verifyAmountOfSortByAndOrderByMatches(
List<String> allSortBy, List<String> allOrderBy) throws InvalidArgumentException {
if (allSortBy != null
&& allOrderBy != null
&& allSortBy.size() != allOrderBy.size()
&& !allOrderBy.isEmpty()) {
throw new InvalidArgumentException(
String.format(
"The amount of '%s' and '%s' does not match. "
+ "Please specify an '%s' for each '%s' or no '%s' parameters at all.",
SORT_BY, ORDER_DIRECTION, ORDER_DIRECTION, SORT_BY, ORDER_DIRECTION));
}
}
}

View File

@ -36,106 +36,112 @@ import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException;
public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(InvalidArgumentException.class)
protected ResponseEntity<Object> handleInvalidArgument(
protected ResponseEntity<TaskanaErrorData> handleInvalidArgument(
InvalidArgumentException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.BAD_REQUEST, false);
}
@ExceptionHandler(NotAuthorizedException.class)
protected ResponseEntity<Object> handleNotAuthorized(NotAuthorizedException ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleNotAuthorized(
NotAuthorizedException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.FORBIDDEN);
}
@ExceptionHandler(NotFoundException.class)
protected ResponseEntity<Object> handleTaskNotFound(NotFoundException ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleTaskNotFound(
NotFoundException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(TaskAlreadyExistException.class)
protected ResponseEntity<Object> handleTaskAlreadyExist(
protected ResponseEntity<TaskanaErrorData> handleTaskAlreadyExist(
TaskAlreadyExistException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(NotAuthorizedToQueryWorkbasketException.class)
protected ResponseEntity<Object> handleNotAuthorizedToQueryWorkbasket(
protected ResponseEntity<TaskanaErrorData> handleNotAuthorizedToQueryWorkbasket(
NotAuthorizedToQueryWorkbasketException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.FORBIDDEN);
}
@ExceptionHandler(InvalidStateException.class)
protected ResponseEntity<Object> handleInvalidState(InvalidStateException ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleInvalidState(
InvalidStateException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(InvalidOwnerException.class)
protected ResponseEntity<Object> handleInvalidOwner(InvalidOwnerException ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleInvalidOwner(
InvalidOwnerException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(ClassificationAlreadyExistException.class)
protected ResponseEntity<Object> handleClassificationAlreadyExist(
protected ResponseEntity<TaskanaErrorData> handleClassificationAlreadyExist(
ClassificationAlreadyExistException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(DuplicateKeyException.class)
protected ResponseEntity<Object> handleDuplicateKey(DuplicateKeyException ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleDuplicateKey(
DuplicateKeyException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(ConcurrencyException.class)
protected ResponseEntity<Object> handleConcurrencyException(
protected ResponseEntity<TaskanaErrorData> handleConcurrencyException(
ConcurrencyException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(WorkbasketInUseException.class)
protected ResponseEntity<Object> handleWorkbasketInUse(
protected ResponseEntity<TaskanaErrorData> handleWorkbasketInUse(
WorkbasketInUseException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.LOCKED);
}
@ExceptionHandler(WorkbasketAlreadyExistException.class)
protected ResponseEntity<Object> handleWorkbasketAlreadyExist(
protected ResponseEntity<TaskanaErrorData> handleWorkbasketAlreadyExist(
WorkbasketAlreadyExistException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(WorkbasketAccessItemAlreadyExistException.class)
protected ResponseEntity<Object> handleWorkbasketAccessItemAlreadyExist(
protected ResponseEntity<TaskanaErrorData> handleWorkbasketAccessItemAlreadyExist(
WorkbasketAccessItemAlreadyExistException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.CONFLICT);
}
@ExceptionHandler(InvalidWorkbasketException.class)
protected ResponseEntity<Object> handleInvalidWorkbasket(
protected ResponseEntity<TaskanaErrorData> handleInvalidWorkbasket(
InvalidWorkbasketException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(DomainNotFoundException.class)
protected ResponseEntity<Object> handleDomainNotFound(
protected ResponseEntity<TaskanaErrorData> handleDomainNotFound(
DomainNotFoundException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
protected ResponseEntity<Object> handleMaxUploadSizeExceededException(
protected ResponseEntity<TaskanaErrorData> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.PAYLOAD_TOO_LARGE);
}
@ExceptionHandler(Exception.class)
protected ResponseEntity<Object> handleGeneralException(Exception ex, WebRequest req) {
protected ResponseEntity<TaskanaErrorData> handleGeneralException(Exception ex, WebRequest req) {
return buildResponse(ex, req, HttpStatus.BAD_REQUEST);
}
private ResponseEntity<Object> buildResponse(Exception ex, WebRequest req, HttpStatus status) {
private ResponseEntity<TaskanaErrorData> buildResponse(
Exception ex, WebRequest req, HttpStatus status) {
return buildResponse(ex, req, status, true);
}
private ResponseEntity<Object> buildResponse(
private ResponseEntity<TaskanaErrorData> buildResponse(
Exception ex, WebRequest req, HttpStatus status, boolean logExceptionOnError) {
TaskanaErrorData errorData = new TaskanaErrorData(status, ex, req);
if (logExceptionOnError) {

View File

@ -20,10 +20,12 @@ 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.BaseQuery.SortDirection;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.Mapping;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.common.rest.models.TaskanaPagedModel;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.exceptions.TaskCommentNotFoundException;
@ -39,8 +41,6 @@ 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";
@ -94,7 +94,7 @@ public class TaskCommentController {
List<TaskComment> taskComments = taskService.getTaskComments(taskId);
// TODO Maybe introduce a query for task comments
applySortingParams(taskComments, params);
taskComments = applySortingParams(taskComments, params);
TaskanaPagedModel<TaskCommentRepresentationModel> taskCommentListResource =
taskCommentRepresentationModelAssembler.toPageModel(taskComments, null);
@ -204,42 +204,33 @@ public class TaskCommentController {
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));
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
Comparator<TaskComment> comparator;
switch (sortBy) {
case (CREATED):
comparator = Comparator.comparing(TaskComment::getCreated);
break;
case (MODIFIED):
comparator = Comparator.comparing(TaskComment::getModified);
break;
default:
throw new InvalidArgumentException("Unknown sort attribute: " + sortBy);
}
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));
if (sortDirection == SortDirection.DESCENDING) {
comparator = comparator.reversed();
}
break;
default:
throw new InvalidArgumentException("Unknown sort attribute: " + sortBy);
}
}
taskComments.sort(comparator);
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", taskComments);
}
return taskComments;
}
}

View File

@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.classification.api.exceptions.ClassificationNotFoundException;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.BulkOperationResults;
import pro.taskana.common.api.KeyDomain;
import pro.taskana.common.api.TimeInterval;
@ -36,6 +35,7 @@ import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.api.exceptions.TaskanaException;
import pro.taskana.common.rest.AbstractPagingController;
import pro.taskana.common.rest.Mapping;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.common.rest.models.TaskanaPagedModel;
import pro.taskana.task.api.TaskQuery;
import pro.taskana.task.api.TaskService;
@ -92,9 +92,6 @@ public class TaskController extends AbstractPagingController {
private static final String WILDCARD_SEARCH_FIELDS = "wildcard-search-fields";
private static final String CUSTOM = "custom";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private static final String INDEFINITE = "";
private final TaskService taskService;
@ -492,6 +489,9 @@ public class TaskController extends AbstractPagingController {
LOGGER.debug("Exit from applyFilterParams(), returning {}", taskQuery);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyFilterParams(), query: {}", taskQuery);
}
return taskQuery;
}
@ -658,58 +658,50 @@ public class TaskController extends AbstractPagingController {
return null;
}
private TaskQuery applySortingParams(TaskQuery taskQuery, MultiValueMap<String, String> params)
private TaskQuery applySortingParams(TaskQuery query, MultiValueMap<String, String> params)
throws InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applySortingParams(taskQuery= {}, params= {})", taskQuery, params);
LOGGER.debug("Entry to applySortingParams(query= {}, params= {})", query, params);
}
// sorting
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = SortDirection.DESCENDING;
} else {
sortDirection = SortDirection.ASCENDING;
}
switch (sortBy) {
case (CLASSIFICATION_KEY):
taskQuery = taskQuery.orderByClassificationKey(sortDirection);
break;
case (POR_TYPE):
taskQuery = taskQuery.orderByPrimaryObjectReferenceType(sortDirection);
break;
case (POR_VALUE):
taskQuery = taskQuery.orderByPrimaryObjectReferenceValue(sortDirection);
break;
case (STATE):
taskQuery = taskQuery.orderByState(sortDirection);
break;
case (NAME):
taskQuery = taskQuery.orderByName(sortDirection);
break;
case (DUE):
taskQuery = taskQuery.orderByDue(sortDirection);
break;
case (PLANNED):
taskQuery = taskQuery.orderByPlanned(sortDirection);
break;
case (PRIORITY):
taskQuery = taskQuery.orderByPriority(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown filter attribute: " + sortBy);
}
}
params.remove(SORT_BY);
params.remove(SORT_DIRECTION);
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
switch (sortBy) {
case (CLASSIFICATION_KEY):
query.orderByClassificationKey(sortDirection);
break;
case (POR_TYPE):
query.orderByPrimaryObjectReferenceType(sortDirection);
break;
case (POR_VALUE):
query.orderByPrimaryObjectReferenceValue(sortDirection);
break;
case (STATE):
query.orderByState(sortDirection);
break;
case (NAME):
query.orderByName(sortDirection);
break;
case (DUE):
query.orderByDue(sortDirection);
break;
case (PLANNED):
query.orderByPlanned(sortDirection);
break;
case (PRIORITY):
query.orderByPriority(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown filter attribute: " + sortBy);
}
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", taskQuery);
LOGGER.debug("Exit from applySortingParams(), returning {}", query);
}
return taskQuery;
return query;
}
private int[] extractPriorities(String[] prioritiesInString) {

View File

@ -15,11 +15,11 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.common.api.BaseQuery;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.AbstractPagingController;
import pro.taskana.common.rest.Mapping;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.common.rest.ldap.LdapClient;
import pro.taskana.common.rest.models.TaskanaPagedModel;
import pro.taskana.workbasket.api.WorkbasketAccessItemQuery;
@ -43,26 +43,18 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
private static final String ACCESS_ID_LIKE = "access-id-like";
private static final String ACCESS_IDS = "access-ids";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
final LdapClient ldapClient;
private final LdapClient ldapClient;
private final WorkbasketService workbasketService;
private final WorkbasketAccessItemRepresentationModelAssembler
workbasketAccessItemRepresentationModelAssembler;
private final WorkbasketAccessItemRepresentationModelAssembler modelAssembler;
@Autowired
public WorkbasketAccessItemController(
LdapClient ldapClient,
WorkbasketService workbasketService,
WorkbasketAccessItemRepresentationModelAssembler
workbasketAccessItemRepresentationModelAssembler) {
WorkbasketAccessItemRepresentationModelAssembler modelAssembler) {
this.ldapClient = ldapClient;
this.workbasketService = workbasketService;
this.workbasketAccessItemRepresentationModelAssembler =
workbasketAccessItemRepresentationModelAssembler;
this.modelAssembler = modelAssembler;
}
/**
@ -82,16 +74,15 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
}
WorkbasketAccessItemQuery query = workbasketService.createWorkbasketAccessItemQuery();
getAccessIds(query, params);
applyFilterParams(query, params);
query = applyAccessIdIn(query, params);
query = applyFilterParams(query, params);
query = applySortingParams(query, params);
PageMetadata pageMetadata = getPageMetadata(params, query);
List<WorkbasketAccessItem> workbasketAccessItems = getQueryList(query, pageMetadata);
TaskanaPagedModel<WorkbasketAccessItemRepresentationModel> pagedResources =
workbasketAccessItemRepresentationModelAssembler.toPageModel(
workbasketAccessItems, pageMetadata);
modelAssembler.toPageModel(workbasketAccessItems, pageMetadata);
ResponseEntity<TaskanaPagedModel<WorkbasketAccessItemRepresentationModel>> response =
ResponseEntity.ok(pagedResources);
@ -134,7 +125,8 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
return response;
}
private void getAccessIds(WorkbasketAccessItemQuery query, MultiValueMap<String, String> params) {
private WorkbasketAccessItemQuery applyAccessIdIn(
WorkbasketAccessItemQuery query, MultiValueMap<String, String> params) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to getAccessIds(query= {}, params= {})", query, params);
}
@ -148,9 +140,10 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from getAccessIds(), returning {}", query);
}
return query;
}
private void applyFilterParams(
private WorkbasketAccessItemQuery applyFilterParams(
WorkbasketAccessItemQuery query, MultiValueMap<String, String> params) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applyFilterParams(query= {}, params= {})", query, params);
@ -174,9 +167,11 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
query.accessIdLike(LIKE + params.get(ACCESS_ID_LIKE).get(0) + LIKE);
params.remove(ACCESS_ID_LIKE);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyFilterParams(), returning {}", query);
}
return query;
}
private WorkbasketAccessItemQuery applySortingParams(
@ -186,33 +181,24 @@ public class WorkbasketAccessItemController extends AbstractPagingController {
LOGGER.debug("Entry to applySortingParams(query= {}, params= {})", query, params);
}
// sorting
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
BaseQuery.SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = BaseQuery.SortDirection.DESCENDING;
} else {
sortDirection = BaseQuery.SortDirection.ASCENDING;
}
switch (sortBy) {
case (WORKBASKET_KEY):
query = query.orderByWorkbasketKey(sortDirection);
break;
case (ACCESS_ID):
query = query.orderByAccessId(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
}
params.remove(SORT_BY);
params.remove(SORT_DIRECTION);
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
switch (sortBy) {
case (WORKBASKET_KEY):
query.orderByWorkbasketKey(sortDirection);
break;
case (ACCESS_ID):
query.orderByAccessId(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", query);
}
return query;
}

View File

@ -21,13 +21,13 @@ 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.BaseQuery.SortDirection;
import pro.taskana.common.api.exceptions.ConcurrencyException;
import pro.taskana.common.api.exceptions.DomainNotFoundException;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.rest.AbstractPagingController;
import pro.taskana.common.rest.Mapping;
import pro.taskana.common.rest.QueryHelper;
import pro.taskana.common.rest.models.TaskanaPagedModel;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketQuery;
@ -68,9 +68,6 @@ public class WorkbasketController extends AbstractPagingController {
private static final String TYPE = "type";
private static final String DESCRIPTION = "description";
private static final String SORT_BY = "sort-by";
private static final String SORT_DIRECTION = "order";
private final WorkbasketService workbasketService;
private final WorkbasketRepresentationModelAssembler workbasketRepresentationModelAssembler;
@ -106,7 +103,7 @@ public class WorkbasketController extends AbstractPagingController {
WorkbasketQuery query = workbasketService.createWorkbasketQuery();
query = applySortingParams(query, params);
applyFilterParams(query, params);
query = applyFilterParams(query, params);
PageMetadata pageMetadata = getPageMetadata(params, query);
List<WorkbasketSummary> workbasketSummaries = getQueryList(query, pageMetadata);
@ -348,47 +345,38 @@ public class WorkbasketController extends AbstractPagingController {
LOGGER.debug("Entry to applySortingParams(query= {}, params={})", query, params);
}
// sorting
String sortBy = params.getFirst(SORT_BY);
if (sortBy != null) {
SortDirection sortDirection;
if (params.getFirst(SORT_DIRECTION) != null
&& "desc".equals(params.getFirst(SORT_DIRECTION))) {
sortDirection = SortDirection.DESCENDING;
} else {
sortDirection = SortDirection.ASCENDING;
}
switch (sortBy) {
case (NAME):
query = query.orderByName(sortDirection);
break;
case (KEY):
query = query.orderByKey(sortDirection);
break;
case (OWNER):
query = query.orderByOwner(sortDirection);
break;
case (TYPE):
query = query.orderByType(sortDirection);
break;
case (DESCRIPTION):
query = query.orderByDescription(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
}
params.remove(SORT_BY);
params.remove(SORT_DIRECTION);
QueryHelper.applyAndRemoveSortingParams(
params,
(sortBy, sortDirection) -> {
switch (sortBy) {
case (NAME):
query.orderByName(sortDirection);
break;
case (KEY):
query.orderByKey(sortDirection);
break;
case (OWNER):
query.orderByOwner(sortDirection);
break;
case (TYPE):
query.orderByType(sortDirection);
break;
case (DESCRIPTION):
query.orderByDescription(sortDirection);
break;
default:
throw new InvalidArgumentException("Unknown order '" + sortBy + "'");
}
});
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applySortingParams(), returning {}", query);
}
return query;
}
private void applyFilterParams(WorkbasketQuery query, MultiValueMap<String, String> params)
throws InvalidArgumentException {
private WorkbasketQuery applyFilterParams(
WorkbasketQuery query, MultiValueMap<String, String> params) throws InvalidArgumentException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Entry to applyFilterParams(query= {}, params= {})", query, params);
}
@ -428,82 +416,22 @@ public class WorkbasketController extends AbstractPagingController {
query.domainIn(extractCommaSeparatedFields(params.get(DOMAIN)));
params.remove(DOMAIN);
}
if (params.containsKey(TYPE)) {
switch (params.getFirst(TYPE)) {
case "PERSONAL":
query.typeIn(WorkbasketType.PERSONAL);
break;
case "GROUP":
query.typeIn(WorkbasketType.GROUP);
break;
case "CLEARANCE":
query.typeIn(WorkbasketType.CLEARANCE);
break;
case "TOPIC":
query.typeIn(WorkbasketType.TOPIC);
break;
default:
throw new InvalidArgumentException(
"Unknown Workbasket type '" + params.getFirst(TYPE) + "'");
String type = params.getFirst(TYPE);
if (type != null) {
try {
query.typeIn(WorkbasketType.valueOf(type));
} catch (IllegalArgumentException e) {
throw new InvalidArgumentException("Unknown Workbasket type '" + type + "'");
}
params.remove(TYPE);
}
if (params.containsKey(REQUIRED_PERMISSION)) {
for (String authorization : params.getFirst(REQUIRED_PERMISSION).split(",")) {
switch (authorization.trim()) {
case "READ":
query.callerHasPermission(WorkbasketPermission.READ);
break;
case "OPEN":
query.callerHasPermission(WorkbasketPermission.OPEN);
break;
case "APPEND":
query.callerHasPermission(WorkbasketPermission.APPEND);
break;
case "TRANSFER":
query.callerHasPermission(WorkbasketPermission.TRANSFER);
break;
case "DISTRIBUTE":
query.callerHasPermission(WorkbasketPermission.DISTRIBUTE);
break;
case "CUSTOM_1":
query.callerHasPermission(WorkbasketPermission.CUSTOM_1);
break;
case "CUSTOM_2":
query.callerHasPermission(WorkbasketPermission.CUSTOM_2);
break;
case "CUSTOM_3":
query.callerHasPermission(WorkbasketPermission.CUSTOM_3);
break;
case "CUSTOM_4":
query.callerHasPermission(WorkbasketPermission.CUSTOM_4);
break;
case "CUSTOM_5":
query.callerHasPermission(WorkbasketPermission.CUSTOM_5);
break;
case "CUSTOM_6":
query.callerHasPermission(WorkbasketPermission.CUSTOM_6);
break;
case "CUSTOM_7":
query.callerHasPermission(WorkbasketPermission.CUSTOM_7);
break;
case "CUSTOM_8":
query.callerHasPermission(WorkbasketPermission.CUSTOM_8);
break;
case "CUSTOM_9":
query.callerHasPermission(WorkbasketPermission.CUSTOM_9);
break;
case "CUSTOM_10":
query.callerHasPermission(WorkbasketPermission.CUSTOM_10);
break;
case "CUSTOM_11":
query.callerHasPermission(WorkbasketPermission.CUSTOM_11);
break;
case "CUSTOM_12":
query.callerHasPermission(WorkbasketPermission.CUSTOM_12);
break;
default:
throw new InvalidArgumentException("Unknown authorization '" + authorization + "'");
String permissions = params.getFirst(REQUIRED_PERMISSION);
if (permissions != null) {
for (String authorization : permissions.split(",")) {
try {
query.callerHasPermission(WorkbasketPermission.valueOf(authorization.trim()));
} catch (IllegalArgumentException e) {
throw new InvalidArgumentException("Unknown authorization '" + authorization + "'", e);
}
}
params.remove(REQUIRED_PERMISSION);
@ -511,5 +439,7 @@ public class WorkbasketController extends AbstractPagingController {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Exit from applyFilterParams(), returning {}", query);
}
return query;
}
}

View File

@ -0,0 +1,177 @@
package pro.taskana.common.rest;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static pro.taskana.common.rest.QueryHelper.applyAndRemoveSortingParams;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
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 org.mockito.InOrder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.internal.util.CheckedBiConsumer;
class QueryHelperTest {
@Test
void should_removeSortByAndOrderDirection_When_ApplyingSortingParams() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Collections.singletonList("sort-by"));
map.put(QueryHelper.ORDER_DIRECTION, Collections.singletonList("order"));
applyAndRemoveSortingParams(map, mock(MockBiConsumer.class));
assertThat(map).isEmpty();
}
@Test
void should_ignoreMapContent_When_ApplyingSortingParams() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
String key = "unknown";
List<String> value = Collections.singletonList("sort-by");
map.put(key, value);
map.put(QueryHelper.SORT_BY, Collections.singletonList("sort-by"));
applyAndRemoveSortingParams(map, mock(MockBiConsumer.class));
assertThat(map).containsExactly(new SimpleEntry<>(key, value));
}
@Test
void should_NotCallConsumer_When_MapDoesNotContainSortBy() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
verifyNoInteractions(consumer);
}
@Test
void should_CallConsumerWithSortByValue_When_MapContainsOneSortBy() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Collections.singletonList("sort-by-value"));
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
verify(consumer).accept(eq("sort-by-value"), any());
verifyNoMoreInteractions(consumer);
}
@Test
void should_CallConsumerWithAscSortDirection_When_MapDoesNotContainSortDirection()
throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Collections.singletonList("sort-by-value"));
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
verify(consumer).accept(any(), eq(SortDirection.ASCENDING));
verifyNoMoreInteractions(consumer);
}
@TestFactory
Stream<DynamicTest>
should_CallConsumerWithDescSortDirection_When_MapDoesContainsDescSortDirection() {
Iterator<String> testCases = Arrays.asList("desc", "DESC", "Desc", "desC", "DeSc").iterator();
ThrowingConsumer<String> test =
desc -> {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Collections.singletonList("sort-by-value"));
map.put(QueryHelper.ORDER_DIRECTION, Collections.singletonList(desc));
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
verify(consumer).accept(any(), eq(SortDirection.DESCENDING));
verifyNoMoreInteractions(consumer);
};
return DynamicTest.stream(testCases, s -> "Order by: " + s, test);
}
@Test
void should_callConsumerMultipleTimes_When_MapContainsMultipleSortBy() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Arrays.asList("sort-by-value1", "sort-by-value2"));
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
InOrder inOrder = inOrder(consumer);
inOrder.verify(consumer).accept(eq("sort-by-value1"), any());
inOrder.verify(consumer).accept(eq("sort-by-value2"), any());
verifyNoMoreInteractions(consumer);
}
@Test
void should_matchSortDirectionForEachSortBy_When_MapContainsMultipleSortByAndOrderBy()
throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Arrays.asList("sort-by-value1", "sort-by-value2"));
map.put(QueryHelper.ORDER_DIRECTION, Arrays.asList("desc", "asc"));
MockBiConsumer consumer = mock(MockBiConsumer.class);
applyAndRemoveSortingParams(map, consumer);
verify(consumer).accept("sort-by-value1", SortDirection.DESCENDING);
verify(consumer).accept("sort-by-value2", SortDirection.ASCENDING);
verifyNoMoreInteractions(consumer);
}
@Test
void should_throwError_When_MapContainsOrderByButNoSortBy() {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.ORDER_DIRECTION, Collections.singletonList("desc"));
assertThatThrownBy(() -> applyAndRemoveSortingParams(map, mock(MockBiConsumer.class)))
.isInstanceOf(InvalidArgumentException.class);
}
@Test
void should_throwError_When_SortByAndOrderByCountDoesNotMatch() {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Arrays.asList("1", "2"));
map.put(QueryHelper.ORDER_DIRECTION, Collections.singletonList("desc"));
assertThatThrownBy(() -> applyAndRemoveSortingParams(map, mock(MockBiConsumer.class)))
.isInstanceOf(InvalidArgumentException.class);
}
@Test
void should_throwError_When_ConsumerRaisesException() throws Exception {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.put(QueryHelper.SORT_BY, Collections.singletonList("1"));
MockBiConsumer consumer = mock(MockBiConsumer.class);
doThrow(new InvalidArgumentException("")).when(consumer).accept(any(), any());
assertThatThrownBy(() -> applyAndRemoveSortingParams(map, consumer))
.isInstanceOf(InvalidArgumentException.class);
}
@Test
void should_throwError_When_ConsumerIsNull() {
assertThatThrownBy(() -> applyAndRemoveSortingParams(new LinkedMultiValueMap<>(), null))
.isInstanceOf(InvalidArgumentException.class);
}
@Test
void should_throwError_When_MapIsNull() {
assertThatThrownBy(() -> applyAndRemoveSortingParams(null, mock(MockBiConsumer.class)))
.isInstanceOf(InvalidArgumentException.class);
}
private abstract static class MockBiConsumer
implements CheckedBiConsumer<String, SortDirection, InvalidArgumentException> {}
}