diff --git a/history/taskana-simplehistory-rest-spring/src/main/java/pro/taskana/simplehistory/rest/TaskHistoryEventController.java b/history/taskana-simplehistory-rest-spring/src/main/java/pro/taskana/simplehistory/rest/TaskHistoryEventController.java index 8965a58f5..6c927a15a 100644 --- a/history/taskana-simplehistory-rest-spring/src/main/java/pro/taskana/simplehistory/rest/TaskHistoryEventController.java +++ b/history/taskana-simplehistory-rest-spring/src/main/java/pro/taskana/simplehistory/rest/TaskHistoryEventController.java @@ -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 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 params) { + private HistoryQuery applyFilterParams(HistoryQuery query, MultiValueMap 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) { diff --git a/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/CheckedBiConsumer.java b/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/CheckedBiConsumer.java new file mode 100644 index 000000000..095eca446 --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/common/internal/util/CheckedBiConsumer.java @@ -0,0 +1,8 @@ +package pro.taskana.common.internal.util; + +@FunctionalInterface +public interface CheckedBiConsumer { + + void accept(T t, U u) throws E; + +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/ClassificationController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/ClassificationController.java index d34815e52..9d90a4255 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/ClassificationController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/classification/rest/ClassificationController.java @@ -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 classificationSummaries = getQueryList(query, pageMetadata); ResponseEntity> 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 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 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 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 params) + private ClassificationQuery applyFilterParams( + ClassificationQuery query, MultiValueMap 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; } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryHelper.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryHelper.java new file mode 100644 index 000000000..dbe44deca --- /dev/null +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/QueryHelper.java @@ -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 params, + CheckedBiConsumer 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 allSortBy = params.remove(SORT_BY); + List 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 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 allSortBy, List 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 allSortBy, List 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)); + } + } +} diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/TaskanaRestExceptionHandler.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/TaskanaRestExceptionHandler.java index 66fcbe045..af63c7256 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/TaskanaRestExceptionHandler.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/common/rest/TaskanaRestExceptionHandler.java @@ -36,106 +36,112 @@ import pro.taskana.workbasket.api.exceptions.WorkbasketInUseException; public class TaskanaRestExceptionHandler extends ResponseEntityExceptionHandler { @ExceptionHandler(InvalidArgumentException.class) - protected ResponseEntity handleInvalidArgument( + protected ResponseEntity handleInvalidArgument( InvalidArgumentException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.BAD_REQUEST, false); } @ExceptionHandler(NotAuthorizedException.class) - protected ResponseEntity handleNotAuthorized(NotAuthorizedException ex, WebRequest req) { + protected ResponseEntity handleNotAuthorized( + NotAuthorizedException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.FORBIDDEN); } @ExceptionHandler(NotFoundException.class) - protected ResponseEntity handleTaskNotFound(NotFoundException ex, WebRequest req) { + protected ResponseEntity handleTaskNotFound( + NotFoundException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.NOT_FOUND); } @ExceptionHandler(TaskAlreadyExistException.class) - protected ResponseEntity handleTaskAlreadyExist( + protected ResponseEntity handleTaskAlreadyExist( TaskAlreadyExistException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(NotAuthorizedToQueryWorkbasketException.class) - protected ResponseEntity handleNotAuthorizedToQueryWorkbasket( + protected ResponseEntity handleNotAuthorizedToQueryWorkbasket( NotAuthorizedToQueryWorkbasketException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.FORBIDDEN); } @ExceptionHandler(InvalidStateException.class) - protected ResponseEntity handleInvalidState(InvalidStateException ex, WebRequest req) { + protected ResponseEntity handleInvalidState( + InvalidStateException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(InvalidOwnerException.class) - protected ResponseEntity handleInvalidOwner(InvalidOwnerException ex, WebRequest req) { + protected ResponseEntity handleInvalidOwner( + InvalidOwnerException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(ClassificationAlreadyExistException.class) - protected ResponseEntity handleClassificationAlreadyExist( + protected ResponseEntity handleClassificationAlreadyExist( ClassificationAlreadyExistException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(DuplicateKeyException.class) - protected ResponseEntity handleDuplicateKey(DuplicateKeyException ex, WebRequest req) { + protected ResponseEntity handleDuplicateKey( + DuplicateKeyException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(ConcurrencyException.class) - protected ResponseEntity handleConcurrencyException( + protected ResponseEntity handleConcurrencyException( ConcurrencyException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(WorkbasketInUseException.class) - protected ResponseEntity handleWorkbasketInUse( + protected ResponseEntity handleWorkbasketInUse( WorkbasketInUseException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.LOCKED); } @ExceptionHandler(WorkbasketAlreadyExistException.class) - protected ResponseEntity handleWorkbasketAlreadyExist( + protected ResponseEntity handleWorkbasketAlreadyExist( WorkbasketAlreadyExistException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(WorkbasketAccessItemAlreadyExistException.class) - protected ResponseEntity handleWorkbasketAccessItemAlreadyExist( + protected ResponseEntity handleWorkbasketAccessItemAlreadyExist( WorkbasketAccessItemAlreadyExistException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.CONFLICT); } @ExceptionHandler(InvalidWorkbasketException.class) - protected ResponseEntity handleInvalidWorkbasket( + protected ResponseEntity handleInvalidWorkbasket( InvalidWorkbasketException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.BAD_REQUEST); } @ExceptionHandler(DomainNotFoundException.class) - protected ResponseEntity handleDomainNotFound( + protected ResponseEntity handleDomainNotFound( DomainNotFoundException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.BAD_REQUEST); } @ExceptionHandler(MaxUploadSizeExceededException.class) - protected ResponseEntity handleMaxUploadSizeExceededException( + protected ResponseEntity handleMaxUploadSizeExceededException( MaxUploadSizeExceededException ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.PAYLOAD_TOO_LARGE); } @ExceptionHandler(Exception.class) - protected ResponseEntity handleGeneralException(Exception ex, WebRequest req) { + protected ResponseEntity handleGeneralException(Exception ex, WebRequest req) { return buildResponse(ex, req, HttpStatus.BAD_REQUEST); } - private ResponseEntity buildResponse(Exception ex, WebRequest req, HttpStatus status) { + private ResponseEntity buildResponse( + Exception ex, WebRequest req, HttpStatus status) { return buildResponse(ex, req, status, true); } - private ResponseEntity buildResponse( + private ResponseEntity buildResponse( Exception ex, WebRequest req, HttpStatus status, boolean logExceptionOnError) { TaskanaErrorData errorData = new TaskanaErrorData(status, ex, req); if (logExceptionOnError) { diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskCommentController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskCommentController.java index a77a7592f..389a56da8 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskCommentController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskCommentController.java @@ -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 taskComments = taskService.getTaskComments(taskId); // TODO Maybe introduce a query for task comments - applySortingParams(taskComments, params); + taskComments = applySortingParams(taskComments, params); TaskanaPagedModel taskCommentListResource = taskCommentRepresentationModelAssembler.toPageModel(taskComments, null); @@ -204,42 +204,33 @@ public class TaskCommentController { private List applySortingParams( List taskComments, MultiValueMap 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 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; } } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java index 3022450f2..8862bb598 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/task/rest/TaskController.java @@ -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 params) + private TaskQuery applySortingParams(TaskQuery query, MultiValueMap 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) { diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketAccessItemController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketAccessItemController.java index becd7792b..05b12c211 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketAccessItemController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketAccessItemController.java @@ -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 workbasketAccessItems = getQueryList(query, pageMetadata); TaskanaPagedModel pagedResources = - workbasketAccessItemRepresentationModelAssembler.toPageModel( - workbasketAccessItems, pageMetadata); + modelAssembler.toPageModel(workbasketAccessItems, pageMetadata); ResponseEntity> response = ResponseEntity.ok(pagedResources); @@ -134,7 +125,8 @@ public class WorkbasketAccessItemController extends AbstractPagingController { return response; } - private void getAccessIds(WorkbasketAccessItemQuery query, MultiValueMap params) { + private WorkbasketAccessItemQuery applyAccessIdIn( + WorkbasketAccessItemQuery query, MultiValueMap 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 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; } diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketController.java index 391549b0f..4d78895fd 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/workbasket/rest/WorkbasketController.java @@ -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 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 params) - throws InvalidArgumentException { + private WorkbasketQuery applyFilterParams( + WorkbasketQuery query, MultiValueMap 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; } } diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/QueryHelperTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/QueryHelperTest.java new file mode 100644 index 000000000..c461b3998 --- /dev/null +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/common/rest/QueryHelperTest.java @@ -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 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 map = new LinkedMultiValueMap<>(); + String key = "unknown"; + List 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 map = new LinkedMultiValueMap<>(); + MockBiConsumer consumer = mock(MockBiConsumer.class); + + applyAndRemoveSortingParams(map, consumer); + + verifyNoInteractions(consumer); + } + + @Test + void should_CallConsumerWithSortByValue_When_MapContainsOneSortBy() throws Exception { + MultiValueMap 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 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 + should_CallConsumerWithDescSortDirection_When_MapDoesContainsDescSortDirection() { + Iterator testCases = Arrays.asList("desc", "DESC", "Desc", "desC", "DeSc").iterator(); + ThrowingConsumer test = + desc -> { + MultiValueMap 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 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 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 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 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 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 {} +}