From 101072d3e94d53ff1f448608f60ab9c3c740246e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Heffner?= <56156750+gitgoodjhe@users.noreply.github.com> Date: Thu, 9 Apr 2020 09:35:43 +0200 Subject: [PATCH] TSK-1188 Enable combo filter for tasks --- .../java/pro/taskana/task/api/TaskQuery.java | 17 +++++ .../task/api/WildcardSearchFields.java | 51 +++++++++++++ .../taskana/task/internal/TaskQueryImpl.java | 38 ++++++++-- .../task/internal/TaskQueryMapper.java | 2 + .../acceptance/task/QueryTasksAccTest.java | 2 + .../QueryTasksByWildcardSearchAccTest.java | 72 +++++++++++++++++++ .../main/resources/sql/sample-data/task.sql | 4 +- .../src/main/resources/sql/test-data/task.sql | 4 +- .../java/pro/taskana/rest/TaskController.java | 42 +++++++++++ .../taskana/rest/TaskControllerIntTest.java | 47 ++++++++++++ .../src/test/resources/asciidoc/rest-api.adoc | 11 ++- 11 files changed, 279 insertions(+), 11 deletions(-) create mode 100644 lib/taskana-core/src/main/java/pro/taskana/task/api/WildcardSearchFields.java create mode 100644 lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWildcardSearchAccTest.java diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskQuery.java b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskQuery.java index 0c084158d..a11b211f7 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskQuery.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/api/TaskQuery.java @@ -611,6 +611,23 @@ public interface TaskQuery extends BaseQuery { */ TaskQuery orderByClassificationName(SortDirection sortDirection); + /** + * Add your wildcard search value for pattern matching to your query. It will be compared in SQL + * with the LIKE operator. You may use a wildcard like % to specify the pattern. + * + * @param wildcardSearchValue the wildcard search value + * @return the query + */ + TaskQuery wildcardSearchValueLike(String wildcardSearchValue); + + /** + * Add the values of the wildcard search fields for exact matching to your query. + * + * @param wildcardSearchFields the values of your wildcard search fields + * @return the query + */ + TaskQuery wildcardSearchFieldsIn(WildcardSearchFields... wildcardSearchFields); + /** * This method sorts the query result according to the completed timestamp. * diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/api/WildcardSearchFields.java b/lib/taskana-core/src/main/java/pro/taskana/task/api/WildcardSearchFields.java new file mode 100644 index 000000000..6a9ef124d --- /dev/null +++ b/lib/taskana-core/src/main/java/pro/taskana/task/api/WildcardSearchFields.java @@ -0,0 +1,51 @@ +package pro.taskana.task.api; + +import java.util.Arrays; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public enum WildcardSearchFields { + NAME("NAME"), + DESCRIPTION("DESCRIPTION"), + CUSTOM_1("CUSTOM_1"), + CUSTOM_2("CUSTOM_2"), + CUSTOM_3("CUSTOM_3"), + CUSTOM_4("CUSTOM_4"), + CUSTOM_5("CUSTOM_5"), + CUSTOM_6("CUSTOM_6"), + CUSTOM_7("CUSTOM_7"), + CUSTOM_8("CUSTOM_8"), + CUSTOM_9("CUSTOM_9"), + CUSTOM_10("CUSTOM_10"), + CUSTOM_11("CUSTOM_11"), + CUSTOM_12("CUSTOM_12"), + CUSTOM_13("CUSTOM_13"), + CUSTOM_14("CUSTOM_14"), + CUSTOM_15("CUSTOM_15"), + CUSTOM_16("CUSTOM_16"); + + WildcardSearchFields(String name) { + this.name = name; + } + + private static final Map STRING_TO_ENUM = + Arrays.stream(values()) + .collect( + Collectors.toMap( + WildcardSearchFields::toString, + e -> e, + (first, second) -> first, + () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER))); + private String name; + + public static WildcardSearchFields fromString(String name) { + + return STRING_TO_ENUM.get(name); + } + + @Override + public String toString() { + return name; + } +} diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryImpl.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryImpl.java index 79c948f46..17f9afd1d 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryImpl.java @@ -24,6 +24,7 @@ import pro.taskana.task.api.ObjectReferenceQuery; import pro.taskana.task.api.TaskQuery; import pro.taskana.task.api.TaskQueryColumnName; import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.WildcardSearchFields; import pro.taskana.task.api.models.TaskSummary; import pro.taskana.task.internal.models.TaskSummaryImpl; import pro.taskana.workbasket.api.WorkbasketPermission; @@ -147,6 +148,8 @@ public class TaskQueryImpl implements TaskQuery { private TimeInterval[] dueIn; private List orderBy; private List orderColumns; + private WildcardSearchFields[] wildcardSearchFieldsIn; + private String wildcardSearchValueLike; private boolean useDistinctKeyword = false; private boolean joinWithAttachments = false; @@ -737,6 +740,18 @@ public class TaskQueryImpl implements TaskQuery { : addOrderCriteria("c.NAME", sortDirection); } + @Override + public TaskQuery wildcardSearchValueLike(String wildcardSearchValue) { + this.wildcardSearchValueLike = wildcardSearchValue; + return this; + } + + @Override + public TaskQuery wildcardSearchFieldsIn(WildcardSearchFields... wildcardSearchFields) { + this.wildcardSearchFieldsIn = wildcardSearchFields; + return this; + } + @Override public TaskQuery orderByCompleted(SortDirection sortDirection) { return addOrderCriteria("COMPLETED", sortDirection); @@ -1588,6 +1603,14 @@ public class TaskQueryImpl implements TaskQuery { addAttachmentClassificationNameToSelectClauseForOrdering; } + public WildcardSearchFields[] getWildcardSearchFieldIn() { + return wildcardSearchFieldsIn; + } + + public String getWildcardSearchValueLike() { + return wildcardSearchValueLike; + } + private String getDatabaseId() { return this.taskanaEngine.getSqlSession().getConfiguration().getDatabaseId(); } @@ -1663,7 +1686,8 @@ public class TaskQueryImpl implements TaskQuery { .getWorkbasketService() .checkAuthorization(workbasketId, WorkbasketPermission.OPEN, WorkbasketPermission.READ); } catch (WorkbasketNotFoundException e) { - LOGGER.warn("The workbasket with the ID '" + workbasketId + "' does not exist.", e); + LOGGER.warn( + String.format("The workbasket with the ID ' %s ' does not exist.", workbasketId), e); } } @@ -1680,11 +1704,9 @@ public class TaskQueryImpl implements TaskQuery { WorkbasketPermission.READ); } catch (WorkbasketNotFoundException e) { LOGGER.warn( - "The workbasket with the KEY '" - + keyDomain.getKey() - + "' and DOMAIN '" - + keyDomain.getDomain() - + "'does not exist.", + String.format( + "The workbasket with the KEY ' %s ' and DOMAIN ' %s ' does not exist.", + keyDomain.getKey(), keyDomain.getDomain()), e); } } @@ -1899,6 +1921,10 @@ public class TaskQueryImpl implements TaskQuery { + addClassificationNameToSelectClauseForOrdering + ", addAttachmentClassificationNameToSelectClauseForOrdering=" + addAttachmentClassificationNameToSelectClauseForOrdering + + ", wildcardSearchFieldsIn=" + + wildcardSearchFieldsIn + + ", wildcardSearchValueLike=" + + wildcardSearchValueLike + "]"; } } diff --git a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryMapper.java b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryMapper.java index 06a24f60a..5679e8876 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryMapper.java +++ b/lib/taskana-core/src/main/java/pro/taskana/task/internal/TaskQueryMapper.java @@ -133,6 +133,7 @@ public interface TaskQueryMapper { + "AND a.REF_VALUE IN(#{item}) " + "AND (UPPER(a.REF_VALUE) LIKE #{item}) " + " AND ( ( a.RECEIVED >= #{item.begin} AND a.RECEIVED <=#{item.end} )) " + + "AND (t.${item} LIKE #{wildcardSearchValueLike}) " + "" + "ORDER BY ${item} " + "") @@ -319,6 +320,7 @@ public interface TaskQueryMapper { + "AND a.REF_VALUE IN(#{item}) " + "AND (UPPER(a.REF_VALUE) LIKE #{item}) " + " AND ( ( a.RECEIVED >= #{item.begin} AND a.RECEIVED <=#{item.end} )) " + + "AND (t.${item} LIKE #{wildcardSearchValueLike}) " + " " + "), Y (ID, EXTERNAL_ID, CREATED, CLAIMED, COMPLETED, MODIFIED, PLANNED, DUE, NAME, CREATOR, DESCRIPTION, NOTE, PRIORITY, STATE, TCLASSIFICATION_KEY, " + " CLASSIFICATION_CATEGORY, CLASSIFICATION_ID, WORKBASKET_ID, DOMAIN, WORKBASKET_KEY, BUSINESS_PROCESS_ID, PARENT_BUSINESS_PROCESS_ID, OWNER, " diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java index 028304657..49804460d 100644 --- a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksAccTest.java @@ -642,6 +642,8 @@ class QueryTasksAccTest extends AbstractAccTest { void testQueryForOrderByWorkbasketIdDesc() { List results = taskService.createTaskQuery().orderByWorkbasketId(DESCENDING).list(); + .orderByCustomAttribute("4", DESCENDING) + assertEquals("99rty", results.get(0).getCustomAttribute("4")); assertThat(results) .hasSizeGreaterThan(2) diff --git a/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWildcardSearchAccTest.java b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWildcardSearchAccTest.java new file mode 100644 index 000000000..381cc3d1a --- /dev/null +++ b/lib/taskana-core/src/test/java/acceptance/task/QueryTasksByWildcardSearchAccTest.java @@ -0,0 +1,72 @@ +package acceptance.task; + +import static org.assertj.core.api.Assertions.assertThat; + +import acceptance.AbstractAccTest; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import pro.taskana.common.api.BaseQuery.SortDirection; +import pro.taskana.security.JaasExtension; +import pro.taskana.security.WithAccessId; +import pro.taskana.task.api.TaskService; +import pro.taskana.task.api.WildcardSearchFields; +import pro.taskana.task.api.models.TaskSummary; + +@ExtendWith(JaasExtension.class) +public class QueryTasksByWildcardSearchAccTest extends AbstractAccTest { + + @WithAccessId( + userName = "teamlead_1", + groupNames = {"group_1", "group_2"}) + @Test + void should_ReturnAllTasksByWildcardSearch_For_ProvidedSearchValue() { + TaskService taskService = taskanaEngine.getTaskService(); + + WildcardSearchFields[] wildcards = { + WildcardSearchFields.CUSTOM_3, WildcardSearchFields.CUSTOM_4, WildcardSearchFields.NAME + }; + + List foundTasks = + taskService + .createTaskQuery() + .wildcardSearchFieldsIn(wildcards) + .wildcardSearchValueLike("%99%") + .orderByName(SortDirection.ASCENDING) + .list(); + + assertThat(foundTasks).hasSize(4); + } + + @WithAccessId( + userName = "teamlead_1", + groupNames = {"group_1", "group_2"}) + @Test + void should_ReturnAllTasks_When_ProvidingNoSearchFieldsOrValue() { + + TaskService taskService = taskanaEngine.getTaskService(); + + WildcardSearchFields[] wildcards = { + WildcardSearchFields.CUSTOM_3, WildcardSearchFields.CUSTOM_4, WildcardSearchFields.NAME + }; + + List foundTasks = + taskService + .createTaskQuery() + .wildcardSearchFieldsIn(wildcards) + .orderByName(SortDirection.ASCENDING) + .list(); + + assertThat(foundTasks).hasSize(83); + + foundTasks = + taskService + .createTaskQuery() + .wildcardSearchValueLike("%99%") + .orderByName(SortDirection.ASCENDING) + .list(); + + assertThat(foundTasks).hasSize(83); + } +} diff --git a/lib/taskana-data/src/main/resources/sql/sample-data/task.sql b/lib/taskana-data/src/main/resources/sql/sample-data/task.sql index 8d9036d17..78de1cbd8 100644 --- a/lib/taskana-data/src/main/resources/sql/sample-data/task.sql +++ b/lib/taskana-data/src/main/resources/sql/sample-data/task.sql @@ -8,8 +8,8 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000005', 'ETI:0000000 INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000006', 'ETI:000000000000000000000000000000000006', RELATIVE_DATE(-5) , null , null , RELATIVE_DATE(0) , RELATIVE_DATE(-5) , RELATIVE_DATE(0) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000006' , 'DOC_0000000000000000006' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000007', 'ETI:000000000000000000000000000000000007', RELATIVE_DATE(-6) , null , null , RELATIVE_DATE(0) , RELATIVE_DATE(-6) , RELATIVE_DATE(0) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000007' , 'DOC_0000000000000000007' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , null , null , 'ffg' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000008', 'ETI:000000000000000000000000000000000008', RELATIVE_DATE(-6) , null , null , RELATIVE_DATE(0) , RELATIVE_DATE(-6) , RELATIVE_DATE(0) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000008' , 'DOC_0000000000000000008' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000009', 'ETI:000000000000000000000000000000000009', RELATIVE_DATE(-7) , null , null , RELATIVE_DATE(0) , RELATIVE_DATE(-7) , RELATIVE_DATE(-4) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000010', 'ETI:000000000000000000000000000000000010', RELATIVE_DATE(-8) , null , null , RELATIVE_DATE(-2) , RELATIVE_DATE(-8) , RELATIVE_DATE(-2) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , 'rty' , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000009', 'ETI:000000000000000000000000000000000009', RELATIVE_DATE(-7) , null , null , RELATIVE_DATE(0) , RELATIVE_DATE(-7) , RELATIVE_DATE(-4) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , 'rty99' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000010', 'ETI:000000000000000000000000000000000010', RELATIVE_DATE(-8) , null , null , RELATIVE_DATE(-2) , RELATIVE_DATE(-8) , RELATIVE_DATE(-2) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , '99rty' , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000011', 'ETI:000000000000000000000000000000000011', RELATIVE_DATE(-8) , null , null , RELATIVE_DATE(-2) , RELATIVE_DATE(-8) , RELATIVE_DATE(2) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000011' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000012', 'ETI:000000000000000000000000000000000012', RELATIVE_DATE(-8) , null , null , RELATIVE_DATE(-2) , RELATIVE_DATE(-7) , RELATIVE_DATE(10) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000012' , 'DOC_0000000000000000012' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000013', 'ETI:000000000000000000000000000000000013', RELATIVE_DATE(-8) , null , null , RELATIVE_DATE(-2) , RELATIVE_DATE(-7) , RELATIVE_DATE(10) , 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000013' , 'DOC_0000000000000000013' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , 'rty' , null , null , null , null , null , null , null , null , 'abc' , null , null ); diff --git a/lib/taskana-data/src/main/resources/sql/test-data/task.sql b/lib/taskana-data/src/main/resources/sql/test-data/task.sql index 8dfb8e7ac..6ae79f111 100644 --- a/lib/taskana-data/src/main/resources/sql/test-data/task.sql +++ b/lib/taskana-data/src/main/resources/sql/test-data/task.sql @@ -8,8 +8,8 @@ INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000005', 'ETI:0000000 INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000006', 'ETI:000000000000000000000000000000000006', '2018-01-29 15:55:06', null , '2018-01-30 16:55:06', '2018-01-29 15:55:06', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000006' , 'DOC_0000000000000000006' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000007', 'ETI:000000000000000000000000000000000007', '2018-01-29 15:55:07', null , null , '2018-01-29 15:55:07', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000007' , 'DOC_0000000000000000007' , null , '00' , 'PASystem' , '00' , 'VNR' , '11223344' , false , false , null , 'NONE' , null , null , null , 'ffg' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000008', 'ETI:000000000000000000000000000000000008', '2018-01-29 15:55:08', null , null , '2018-01-29 15:55:08', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000008' , 'DOC_0000000000000000008' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000009', 'ETI:000000000000000000000000000000000009', '2018-01-29 15:55:09', null , null , '2018-01-29 15:55:09', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); -INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000010', 'ETI:000000000000000000000000000000000010', '2018-01-29 15:55:10', null , null , '2018-01-29 15:55:10', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , 'rty' , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000009', 'ETI:000000000000000000000000000000000009', '2018-01-29 15:55:09', null , null , '2018-01-29 15:55:09', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000009' , 'DOC_0000000000000000009' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , 'rty99' , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); +INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000010', 'ETI:000000000000000000000000000000000010', '2018-01-29 15:55:10', null , null , '2018-01-29 15:55:10', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000010' , 'DOC_0000000000000000010' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , '99rty' , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000011', 'ETI:000000000000000000000000000000000011', '2018-01-29 15:55:11', null , null , '2018-01-29 15:55:11', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000011' , 'DOC_0000000000000000011' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000012', 'ETI:000000000000000000000000000000000012', '2018-01-29 15:55:12', null , null , '2018-01-29 15:55:12', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000012' , 'DOC_0000000000000000012' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , null , null , null , null , null , null , null , null , null , 'abc' , null , null ); INSERT INTO TASK VALUES('TKI:000000000000000000000000000000000013', 'ETI:000000000000000000000000000000000013', '2018-01-29 15:55:13', null , null , '2018-01-29 15:55:13', '2018-01-29 15:55:00', '2018-01-30 15:55:00', 'Widerruf' , 'creator_user_id' , 'Widerruf' , null , 2 , 'READY' , 'EXTERN' , 'L1050' , 'CLI:100000000000000000000000000000000003', 'WBI:100000000000000000000000000000000001' , 'GPK_KSC' , 'DOMAIN_A', 'PI_0000000000013' , 'DOC_0000000000000000013' , null , '00' , 'PASystem' , '00' , 'VNR' , '22334455' , false , false , null , 'NONE' , null , null , null , null , null , 'rty' , null , null , null , null , null , null , null , null , 'abc' , null , null ); diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java index 463b518a0..b5e6dac53 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskController.java @@ -3,6 +3,8 @@ package pro.taskana.rest; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.hateoas.config.EnableHypermediaSupport; @@ -35,6 +37,7 @@ import pro.taskana.rest.resource.TaskSummaryResourceAssembler; import pro.taskana.task.api.TaskQuery; import pro.taskana.task.api.TaskService; import pro.taskana.task.api.TaskState; +import pro.taskana.task.api.WildcardSearchFields; import pro.taskana.task.api.exceptions.AttachmentPersistenceException; import pro.taskana.task.api.exceptions.InvalidOwnerException; import pro.taskana.task.api.exceptions.InvalidStateException; @@ -77,6 +80,8 @@ public class TaskController extends AbstractPagingController { private static final String PLANNED_UNTIL = "planned-until"; private static final String PLANNED_FROM = "planned-from"; private static final String EXTERNAL_ID = "external-id"; + private static final String WILDCARD_SEARCH_VALUE = "wildcard-search-value"; + private static final String WILDCARD_SEARCH_FIELDS = "wildcard-search-fields"; private static final String SORT_BY = "sort-by"; private static final String SORT_DIRECTION = "order"; @@ -295,6 +300,7 @@ public class TaskController extends AbstractPagingController { params.remove(PRIORITY); } if (params.containsKey(STATE)) { + TaskState[] states = extractStates(params); taskQuery.stateIn(states); params.remove(STATE); @@ -382,6 +388,18 @@ public class TaskController extends AbstractPagingController { updateTaskQueryWithIndefiniteTimeInterval(taskQuery, params, DUE_TO, timeInterval); } + if (params.containsKey(WILDCARD_SEARCH_FIELDS) && params.containsKey(WILDCARD_SEARCH_VALUE)) { + + String[] requestedWildcardSearchFields = + extractCommaSeparatedFields(params.get(WILDCARD_SEARCH_FIELDS)); + + taskQuery.wildcardSearchFieldsIn(createWildcardSearchFields(requestedWildcardSearchFields)); + + taskQuery.wildcardSearchValueLike(params.getFirst(WILDCARD_SEARCH_VALUE)); + params.remove(WILDCARD_SEARCH_FIELDS); + params.remove(WILDCARD_SEARCH_VALUE); + } + if (params.containsKey(EXTERNAL_ID)) { String[] externalIds = extractCommaSeparatedFields(params.get(EXTERNAL_ID)); taskQuery.externalIdIn(externalIds); @@ -395,6 +413,14 @@ public class TaskController extends AbstractPagingController { return taskQuery; } + private WildcardSearchFields[] createWildcardSearchFields(String[] wildcardFields) { + + return Stream.of(wildcardFields) + .map(WildcardSearchFields::fromString) + .filter(Objects::nonNull) + .toArray(WildcardSearchFields[]::new); + } + private void updateTaskQueryWithWorkbasketKey( TaskQuery taskQuery, MultiValueMap params) throws InvalidArgumentException { @@ -442,6 +468,22 @@ public class TaskController extends AbstractPagingController { + PLANNED_UNTIL + "\""); } + + if (params.containsKey(WILDCARD_SEARCH_FIELDS) && !params.containsKey(WILDCARD_SEARCH_VALUE) + || !params.containsKey(WILDCARD_SEARCH_FIELDS) + && params.containsKey(WILDCARD_SEARCH_VALUE)) { + + throw new IllegalArgumentException( + "It is prohibited to use the params " + + "\"" + + WILDCARD_SEARCH_FIELDS + + "\"" + + " or " + + "\"" + + WILDCARD_SEARCH_VALUE + + "\"" + + " without one another. If one is provided you must provide the other one as well"); + } } private void updateTaskQueryWithIndefiniteTimeInterval( diff --git a/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java index f5e939fd9..6a03bc8a4 100644 --- a/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java +++ b/rest/taskana-rest-spring/src/test/java/pro/taskana/rest/TaskControllerIntTest.java @@ -210,6 +210,53 @@ class TaskControllerIntTest { assertThat(response.getBody().getContent()).hasSize(6); } + @Test + void should_ReturnAllTasksByWildcardSearch_For_ProvidedSearchValue() { + ResponseEntity response = + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?wildcard-search-value=%99%" + + "&wildcard-search-fields=NAME,custom_3,CuStOM_4", + HttpMethod.GET, + new HttpEntity(restHelper.getHeadersAdmin()), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + assertThat(response.getBody().getLink(Link.REL_SELF)).isNotNull(); + assertThat(response.getBody().getContent()).hasSize(4); + } + + @Test + void should_ThrowException_When_ProvidingInvalidWildcardSearchParameters() { + + ThrowingCallable httpCall = + () -> { + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + "?wildcard-search-value=%rt%", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + }; + assertThatThrownBy(httpCall) + .isInstanceOf(HttpClientErrorException.class) + .hasMessageContaining("400") + .extracting(ex -> ((HttpClientErrorException) ex).getStatusCode()) + .isEqualTo(HttpStatus.BAD_REQUEST); + + ThrowingCallable httpCall2 = + () -> { + template.exchange( + restHelper.toUrl(Mapping.URL_TASKS) + + "?wildcard-search-fields=NAME,CUSTOM_3,CUSTOM_4", + HttpMethod.GET, + restHelper.defaultRequest(), + ParameterizedTypeReference.forType(TaskSummaryListResource.class)); + }; + assertThatThrownBy(httpCall2) + .isInstanceOf(HttpClientErrorException.class) + .hasMessageContaining("400") + .extracting(ex -> ((HttpClientErrorException) ex).getStatusCode()) + .isEqualTo(HttpStatus.BAD_REQUEST); + } + @Test void testGetAllTasksByWorkbasketIdWithinSingleDueTimeInterval() { diff --git a/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc b/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc index 848690de0..038cd756b 100644 --- a/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc +++ b/rest/taskana-rest-spring/src/test/resources/asciidoc/rest-api.adoc @@ -122,7 +122,16 @@ The list generated in the response can be filtered using following parameters in name | priority | state | classification.key | workbasket-id | {workbasket-key , domain} | + owner | por.company | por.system | por.instance | por.type | por.value + planned | planned-from | planned-until | due | due-from | due-until | + -external-id +external-id | wildcard-search-value | wildcard-search-fields + +When filtering a wildcard search you must provide both paremters + +(wildcard-search-value and wildcard-search-fields). Valid wildcard-search-fields are: + + +-name + +-description + +-custom_1 to custom_16 + + +These fields are case insensitive! + If it is sufficient to filter the list with a single time interval, use the parameters + planned-from/due-from and planned-until/due-until. +