Closes #2246 - add grouping by sors and pors

This commit is contained in:
Elena Mokeeva 2023-03-30 17:08:33 +02:00 committed by Elena Mokeeva
parent f81b54f640
commit c43ddbeb88
23 changed files with 803 additions and 38 deletions

View File

@ -289,6 +289,8 @@ class TaskanaConfigurationTest {
boolean expectedAddAdditionalUserInfo = true;
Set<WorkbasketPermission> expectedMinimalPermissionsToAssignDomains =
Set.of(WorkbasketPermission.CUSTOM_2);
// database configuration
boolean expectedUseSpecificDb2Taskquery = false;
// when
TaskanaConfiguration configuration =
@ -346,6 +348,7 @@ class TaskanaConfigurationTest {
// user configuration
.addAdditionalUserInfo(expectedAddAdditionalUserInfo)
.minimalPermissionsToAssignDomains(expectedMinimalPermissionsToAssignDomains)
.useSpecificDb2Taskquery(expectedUseSpecificDb2Taskquery)
.build();
// then
@ -478,6 +481,8 @@ class TaskanaConfigurationTest {
// user configuration
.addAdditionalUserInfo(true)
.minimalPermissionsToAssignDomains(Set.of(WorkbasketPermission.CUSTOM_2))
//database configuration
.useSpecificDb2Taskquery(false)
.build();
TaskanaConfiguration copyConfiguration = new Builder(configuration).build();

View File

@ -179,7 +179,8 @@ class GetTaskAccTest {
assertThat(readTask.getCustomField(TaskCustomField.CUSTOM_16)).isEqualTo("custom16");
assertThatCode(() -> readTask.getCustomAttributeMap().put("X", "Y")).doesNotThrowAnyException();
assertThatCode(() -> readTask.getCallbackInfo().put("X", "Y")).doesNotThrowAnyException();
assertThat(readTask).hasNoNullFieldsOrPropertiesExcept("ownerLongName", "completed");
assertThat(readTask)
.hasNoNullFieldsOrPropertiesExcept("ownerLongName", "completed", "groupByCount");
}
@WithAccessId(user = "user-1-1")

View File

@ -249,7 +249,7 @@ class TaskQueryImplAccTest {
List<TaskSummary> list = taskService.createTaskQuery().workbasketIdIn(wb.getId()).list();
assertThat(list).containsExactlyInAnyOrder(taskSummary1, taskSummary2);
assertThat(taskSummary1).hasNoNullFieldsOrPropertiesExcept("ownerLongName");
assertThat(taskSummary1).hasNoNullFieldsOrPropertiesExcept("ownerLongName", "groupByCount");
}
@WithAccessId(user = "user-1-1")
@ -697,6 +697,9 @@ class TaskQueryImplAccTest {
taskInWorkbasket(wb)
.completed(Instant.parse("2020-02-01T00:00:00Z"))
.buildAndStoreAsSummary(taskService);
taskInWorkbasket(wb)
.completed(null)
.buildAndStoreAsSummary(taskService);
}
@WithAccessId(user = "user-1-1")
@ -871,6 +874,7 @@ class TaskQueryImplAccTest {
taskSummary1 = taskInWorkbasket(wb).note("Note1").buildAndStoreAsSummary(taskService);
taskSummary2 = taskInWorkbasket(wb).note("Note2").buildAndStoreAsSummary(taskService);
taskSummary3 = taskInWorkbasket(wb).note("Lorem ipsum").buildAndStoreAsSummary(taskService);
taskInWorkbasket(wb).note(null).buildAndStoreAsSummary(taskService);
}
@WithAccessId(user = "user-1-1")
@ -1772,8 +1776,16 @@ class TaskQueryImplAccTest {
wb = createWorkbasketWithPermission();
por1 = defaultTestObjectReference().company("15").build();
ObjectReference por2 = defaultTestObjectReference().build();
taskSummary1 = taskInWorkbasket(wb).primaryObjRef(por1).buildAndStoreAsSummary(taskService);
taskSummary2 = taskInWorkbasket(wb).primaryObjRef(por2).buildAndStoreAsSummary(taskService);
taskSummary1 =
taskInWorkbasket(wb)
.primaryObjRef(por1)
.due(Instant.parse("2022-11-15T09:42:00.000Z"))
.buildAndStoreAsSummary(taskService);
taskSummary2 =
taskInWorkbasket(wb)
.primaryObjRef(por2)
.due(Instant.parse("2022-11-15T09:45:00.000Z"))
.buildAndStoreAsSummary(taskService);
}
@WithAccessId(user = "user-1-1")
@ -3066,13 +3078,17 @@ class TaskQueryImplAccTest {
.type("SecondType")
.build();
taskSummary2 =
taskInWorkbasket(wb).objectReferences(sor2).buildAndStoreAsSummary(taskService);
taskInWorkbasket(wb)
.objectReferences(sor2)
.due(Instant.parse("2022-11-15T09:42:00.000Z"))
.buildAndStoreAsSummary(taskService);
ObjectReference sor2copy = sor2.copy();
ObjectReference sor1copy = sor1.copy();
taskSummary3 =
taskInWorkbasket(wb)
.objectReferences(sor2copy, sor1copy)
.due(Instant.parse("2022-11-15T09:45:00.000Z"))
.buildAndStoreAsSummary(taskService);
ObjectReference sor3 =

View File

@ -0,0 +1,334 @@
package acceptance.task.query;
import static org.assertj.core.api.Assertions.assertThat;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestClassification;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestObjectReference;
import static pro.taskana.testapi.DefaultTestEntities.defaultTestWorkbasket;
import java.time.Instant;
import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.classification.api.ClassificationService;
import pro.taskana.classification.api.models.ClassificationSummary;
import pro.taskana.common.api.BaseQuery.SortDirection;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.task.api.TaskService;
import pro.taskana.task.api.models.ObjectReference;
import pro.taskana.task.api.models.TaskSummary;
import pro.taskana.testapi.TaskanaConfigurationModifier;
import pro.taskana.testapi.TaskanaInject;
import pro.taskana.testapi.TaskanaIntegrationTest;
import pro.taskana.testapi.builder.ObjectReferenceBuilder;
import pro.taskana.testapi.builder.TaskAttachmentBuilder;
import pro.taskana.testapi.builder.TaskBuilder;
import pro.taskana.testapi.builder.UserBuilder;
import pro.taskana.testapi.builder.WorkbasketAccessItemBuilder;
import pro.taskana.testapi.security.WithAccessId;
import pro.taskana.user.api.UserService;
import pro.taskana.workbasket.api.WorkbasketPermission;
import pro.taskana.workbasket.api.WorkbasketService;
import pro.taskana.workbasket.api.models.WorkbasketSummary;
@TaskanaIntegrationTest
@DisabledIfEnvironmentVariable(named = "DB", matches = "ORACLE")
class TaskQueryImplGroupByAccTest implements TaskanaConfigurationModifier {
@TaskanaInject TaskService taskService;
@TaskanaInject WorkbasketService workbasketService;
@TaskanaInject CurrentUserContext currentUserContext;
@TaskanaInject ClassificationService classificationService;
@TaskanaInject UserService userService;
ClassificationSummary defaultClassificationSummary;
WorkbasketSummary defaultWorkbasket;
TaskSummary taskSummary1;
TaskSummary taskSummary2;
TaskSummary taskSummary3;
@Override
public TaskanaConfiguration.Builder modify(TaskanaConfiguration.Builder builder) {
return builder.addAdditionalUserInfo(true).useSpecificDb2Taskquery(false);
}
@WithAccessId(user = "user-1-1")
@BeforeAll
void setup() throws Exception {
UserBuilder.newUser()
.id("user-1-1")
.longName("Mustermann, Max - (user-1-1)")
.firstName("Max")
.lastName("Mustermann")
.buildAndStore(userService, "businessadmin");
defaultClassificationSummary =
defaultTestClassification().buildAndStoreAsSummary(classificationService, "businessadmin");
defaultWorkbasket = createWorkbasketWithPermission();
ObjectReference sor2 =
ObjectReferenceBuilder.newObjectReference()
.company("FirstCompany")
.value("FirstValue")
.type("SecondType")
.build();
ObjectReference por2 = defaultTestObjectReference().build();
taskSummary2 =
taskInWorkbasket(defaultWorkbasket)
.owner("user-1-1")
.primaryObjRef(por2)
.objectReferences(sor2)
.due(Instant.parse("2022-11-10T09:45:00.000Z"))
.name("Name2")
.attachments(
TaskAttachmentBuilder.newAttachment()
.channel("A")
.classificationSummary(defaultClassificationSummary)
.objectReference(por2)
.build())
.buildAndStore(taskService)
.asSummary();
ObjectReference por1 = defaultTestObjectReference().company("15").build();
ObjectReference sor1 =
ObjectReferenceBuilder.newObjectReference()
.company("FirstCompany")
.value("FirstValue")
.type("FirstType")
.build();
taskSummary1 =
taskInWorkbasket(defaultWorkbasket)
.owner("user-1-1")
.primaryObjRef(por1)
.objectReferences(sor1)
.due(Instant.parse("2022-11-09T09:42:00.000Z"))
.name("Name3")
.attachments(
TaskAttachmentBuilder.newAttachment()
.channel("B")
.classificationSummary(defaultClassificationSummary)
.objectReference(por1)
.build())
.buildAndStoreAsSummary(taskService);
ObjectReference sor2copy = sor2.copy();
ObjectReference sor1copy = sor1.copy();
taskSummary3 =
taskInWorkbasket(defaultWorkbasket)
.owner("user-1-1")
.objectReferences(sor2copy, sor1copy)
.due(Instant.parse("2022-11-15T09:45:00.000Z"))
.name("Name1")
.attachments(
TaskAttachmentBuilder.newAttachment()
.channel("C")
.classificationSummary(defaultClassificationSummary)
.objectReference(por1)
.build())
.buildAndStoreAsSummary(taskService);
taskInWorkbasket(createWorkbasketWithPermission()).buildAndStore(taskService);
}
private TaskBuilder taskInWorkbasket(WorkbasketSummary wb) {
return TaskBuilder.newTask()
.classificationSummary(defaultClassificationSummary)
.primaryObjRef(defaultTestObjectReference().build())
.workbasketSummary(wb);
}
private WorkbasketSummary createWorkbasketWithPermission() throws Exception {
WorkbasketSummary workbasketSummary =
defaultTestWorkbasket().buildAndStoreAsSummary(workbasketService, "businessadmin");
persistPermission(workbasketSummary);
return workbasketSummary;
}
private void persistPermission(WorkbasketSummary workbasketSummary) throws Exception {
WorkbasketAccessItemBuilder.newWorkbasketAccessItem()
.workbasketId(workbasketSummary.getId())
.accessId(currentUserContext.getUserid())
.permission(WorkbasketPermission.OPEN)
.permission(WorkbasketPermission.READ)
.permission(WorkbasketPermission.APPEND)
.buildAndStore(workbasketService, "businessadmin");
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupByPor_When_OrderingByName() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupByPor()
.orderByName(SortDirection.ASCENDING)
.list();
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary3)
.extracting("groupByCount")
.containsExactly(3);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupByPor_When_JoiningWithAllTablesAndPaging() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupByPor()
.ownerLongNameNotIn("Unexisting")
.attachmentChannelNotLike("Unexisting")
.sorTypeLike("%Type%")
.orderByOwnerLongName(SortDirection.ASCENDING)
.orderByAttachmentChannel(SortDirection.ASCENDING)
.orderByClassificationName(SortDirection.ASCENDING)
.listPage(1, 1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary2)
.extracting("groupByCount")
.containsExactly(3);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupBySor_When_JoiningWithAllTablesAndPaging() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupBySor("SecondType")
.ownerLongNameNotIn("Unexisting")
.attachmentChannelNotLike("Unexisting")
.sorTypeLike("%Type%")
.orderByOwnerLongName(SortDirection.ASCENDING)
.orderByAttachmentChannel(SortDirection.ASCENDING)
.orderByClassificationName(SortDirection.ASCENDING)
.listPage(1, 1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary2)
.extracting("groupByCount")
.containsExactly(2);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupByPorWithOrderingByDue_When_OrderingByPorValue() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupByPor()
.orderByPrimaryObjectReferenceValue(SortDirection.ASCENDING)
.list();
assertThat(list).hasSize(1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary1)
.extracting("groupByCount")
.containsExactly(3);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupByPorWithOrderingByDue_When_NotOrdering() {
List<TaskSummary> list =
taskService.createTaskQuery().workbasketIdIn(defaultWorkbasket.getId()).groupByPor().list();
assertThat(list).hasSize(1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary1)
.extracting("groupByCount")
.containsExactly(3);
}
@WithAccessId(user = "user-1-1")
@Test
void should_UseSingleCorrectly_When_GroupingByPor() {
TaskSummary result =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupByPor()
.single();
assertThat(result)
.usingRecursiveComparison()
.ignoringFields("groupByCount")
.isEqualTo(taskSummary1);
assertThat(result.getGroupByCount()).isEqualTo(3);
}
@WithAccessId(user = "user-1-1")
@Test
void should_UseSingleCorrectly_When_GroupingBySor() {
TaskSummary result =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupBySor("SecondType")
.single();
assertThat(result)
.usingRecursiveComparison()
.ignoringFields("groupByCount")
.isEqualTo(taskSummary2);
assertThat(result.getGroupByCount()).isEqualTo(2);
}
@WithAccessId(user = "user-1-1")
@Test
void should_Count_When_GroupingByPor() {
Long numberOfTasks =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupByPor()
.count();
assertThat(numberOfTasks).isEqualTo(1);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupBySor_When_OrderingByName() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupBySor("SecondType")
.orderByName(SortDirection.ASCENDING)
.list();
assertThat(list).hasSize(1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary3)
.extracting("groupByCount")
.containsExactly(2);
}
@WithAccessId(user = "user-1-1")
@Test
void should_GroupBySorWithOrderingByDue_When_NotOrdering() {
List<TaskSummary> list =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupBySor("SecondType")
.list();
assertThat(list).hasSize(1);
assertThat(list)
.usingRecursiveFieldByFieldElementComparatorIgnoringFields("groupByCount")
.containsExactly(taskSummary2)
.extracting("groupByCount")
.containsExactly(2);
}
@WithAccessId(user = "user-1-1")
@Test
void should_Count_When_GroupingBySor() {
Long numberOfTasks =
taskService
.createTaskQuery()
.workbasketIdIn(defaultWorkbasket.getId())
.groupBySor("SecondType")
.count();
assertThat(numberOfTasks).isEqualTo(1);
}
}

View File

@ -53,6 +53,8 @@ taskana.jobs.customJobs=A | B | C
# user configuration
taskana.user.addAdditionalUserInfo=true
taskana.user.minimalPermissionsToAssignDomains=READ | OPEN
# database configuration
taskana.feature.useSpecificDb2Taskquery=false
# custom configuration
my_custom_property1=my_custom_value1
my_custom_property2=my_custom_value2

View File

@ -123,6 +123,10 @@ public class TaskanaConfiguration {
private final Set<WorkbasketPermission> minimalPermissionsToAssignDomains;
// endregion
// region database configuration
private final boolean useSpecificDb2Taskquery;
// endregion
// region custom configuration
private final Map<String, String> properties;
// endregion
@ -194,6 +198,8 @@ public class TaskanaConfiguration {
this.addAdditionalUserInfo = builder.addAdditionalUserInfo;
this.minimalPermissionsToAssignDomains =
Collections.unmodifiableSet(builder.minimalPermissionsToAssignDomains);
// database configuration
this.useSpecificDb2Taskquery = builder.useSpecificDb2Taskquery;
// custom configuration
this.properties = Map.copyOf(builder.properties);
}
@ -388,6 +394,10 @@ public class TaskanaConfiguration {
return minimalPermissionsToAssignDomains;
}
public boolean isUseSpecificDb2Taskquery() {
return useSpecificDb2Taskquery;
}
/**
* return all properties loaded from taskana properties file. Per Design the normal Properties are
* not immutable, so we return here an ImmutableMap, because we don't want direct changes in the
@ -447,6 +457,7 @@ public class TaskanaConfiguration {
customJobs,
addAdditionalUserInfo,
minimalPermissionsToAssignDomains,
useSpecificDb2Taskquery,
properties);
}
@ -481,6 +492,7 @@ public class TaskanaConfiguration {
&& simpleHistoryCleanupJobAllCompletedSameParentBusiness
== other.simpleHistoryCleanupJobAllCompletedSameParentBusiness
&& taskUpdatePriorityJobEnabled == other.taskUpdatePriorityJobEnabled
&& useSpecificDb2Taskquery == other.useSpecificDb2Taskquery
&& taskUpdatePriorityJobBatchSize == other.taskUpdatePriorityJobBatchSize
&& userInfoRefreshJobEnabled == other.userInfoRefreshJobEnabled
&& addAdditionalUserInfo == other.addAdditionalUserInfo
@ -596,6 +608,8 @@ public class TaskanaConfiguration {
+ addAdditionalUserInfo
+ ", minimalPermissionsToAssignDomains="
+ minimalPermissionsToAssignDomains
+ ", useSpecificDb2Taskquery="
+ useSpecificDb2Taskquery
+ ", properties="
+ properties
+ "]";
@ -743,6 +757,11 @@ public class TaskanaConfiguration {
private Set<WorkbasketPermission> minimalPermissionsToAssignDomains = new HashSet<>();
// endregion
// region database configuration
@TaskanaProperty("taskana.feature.useSpecificDb2Taskquery")
private boolean useSpecificDb2Taskquery = true;
// endregion
// region custom configuration
private Map<String, String> properties = Collections.emptyMap();
// endregion
@ -845,6 +864,8 @@ public class TaskanaConfiguration {
// user configuration
this.addAdditionalUserInfo = conf.addAdditionalUserInfo;
this.minimalPermissionsToAssignDomains = conf.minimalPermissionsToAssignDomains;
// database configuration
this.useSpecificDb2Taskquery = conf.useSpecificDb2Taskquery;
// custom configuration
this.properties = conf.properties;
}
@ -1128,6 +1149,11 @@ public class TaskanaConfiguration {
}
// endregion
// region database configuration
public Builder useSpecificDb2Taskquery(boolean useSpecificDb2Taskquery) {
this.useSpecificDb2Taskquery = useSpecificDb2Taskquery;
return this;
}
public TaskanaConfiguration build() {
adjustConfiguration();

View File

@ -1217,6 +1217,29 @@ public interface TaskQuery extends BaseQuery<TaskSummary, TaskQueryColumnName> {
*/
TaskQuery orderByPrimaryObjectReferenceValue(SortDirection sortDirection);
// endregion
// region groupBy
/**
* Group the {@linkplain Task Tasks} that will be returned by this query according to the value of
* their {@linkplain Task#getPrimaryObjRef() primary ObjectReference}. Only one Task will be
* returned for all Tasks with the same value.
*
* @return the query
*/
TaskQuery groupByPor();
/**
* Group the {@linkplain Task Tasks} that will be returned by this query according to the value of
* their {@linkplain Task#getSecondaryObjectReferences() secondary ObjectReference} with the
* specified type. Only one Task will be returned for all Tasks with the same value.
*
* @param type the type of the relevant {@linkplain Task#getSecondaryObjectReferences() secondary
* ObjectReference}
* @return the query
*/
TaskQuery groupBySor(String type);
// endregion
// region read

View File

@ -80,6 +80,16 @@ public interface TaskSummary {
*/
Instant getReceived();
/**
* Returns the number of {@linkplain Task Tasks} that are grouped together with this {@linkplain
* Task} by a {@linkplain pro.taskana.task.api.TaskQuery}. It's only not NULL when using
* {@linkplain pro.taskana.task.api.TaskQuery#groupByPor()} or {@linkplain
* pro.taskana.task.api.TaskQuery#groupBySor(String)}.
*
* @return the number of {@linkplain Task Tasks} grouped toghether with this {@linkplain Task}
*/
Integer getGroupByCount();
/**
* Returns the time when the {@linkplain Task} is due.
*

View File

@ -53,7 +53,8 @@ public class TaskQueryImpl implements TaskQuery {
private static final Logger LOGGER = LoggerFactory.getLogger(TaskQueryImpl.class);
private final InternalTaskanaEngine taskanaEngine;
private final TaskServiceImpl taskService;
private final List<String> orderBy;
private final List<String> orderByOuter;
private final List<String> orderByInner;
private TaskQueryColumnName columnName;
private String[] accessIdIn;
@ -70,7 +71,8 @@ public class TaskQueryImpl implements TaskQuery {
private boolean addAttachmentClassificationNameToSelectClauseForOrdering = false;
private boolean addWorkbasketNameToSelectClauseForOrdering = false;
private boolean joinWithUserInfo;
private boolean groupByPor;
private String groupBySor;
private String[] taskId;
private String[] taskIdNotIn;
private String[] externalIdIn;
@ -338,7 +340,8 @@ public class TaskQueryImpl implements TaskQuery {
TaskQueryImpl(InternalTaskanaEngine taskanaEngine) {
this.taskanaEngine = taskanaEngine;
this.taskService = (TaskServiceImpl) taskanaEngine.getEngine().getTaskService();
this.orderBy = new ArrayList<>();
this.orderByOuter = new ArrayList<>();
this.orderByInner = new ArrayList<>();
this.filterByAccessIdIn = true;
this.withoutAttachment = false;
this.joinWithUserInfo = taskanaEngine.getEngine().getConfiguration().isAddAdditionalUserInfo();
@ -767,7 +770,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByClassificationName(SortDirection sortDirection) {
joinWithClassifications = true;
addClassificationNameToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("CNAME", sortDirection)
: addOrderCriteria("c.NAME", sortDirection);
}
@ -1066,6 +1070,30 @@ public class TaskQueryImpl implements TaskQuery {
return addOrderCriteria("POR_VALUE", sortDirection);
}
@Override
public TaskQuery groupByPor() {
if (taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery()
&& getDB().equals(DB.DB2)) {
throw new SystemException(
"taskana.feature.useSpecificDb2Taskquery needs to be set to false "
+ "in order to group by por.");
}
groupByPor = true;
return this;
}
@Override
public TaskQuery groupBySor(String type) {
if (taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery()
&& getDB().equals(DB.DB2)) {
throw new SystemException(
"taskana.feature.useSpecificDb2Taskquery needs to be set to false "
+ "in order to group by sor.");
}
groupBySor = type;
return sorTypeIn(type);
}
@Override
public TaskQuery readEquals(Boolean isRead) {
this.isRead = isRead;
@ -1096,7 +1124,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentClassificationId(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentColumnsToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("ACLASSIFICATION_ID", sortDirection)
: addOrderCriteria("a.CLASSIFICATION_ID", sortDirection);
}
@ -1133,7 +1162,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentClassificationKey(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentColumnsToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("ACLASSIFICATION_KEY", sortDirection)
: addOrderCriteria("a.CLASSIFICATION_KEY", sortDirection);
}
@ -1170,7 +1200,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentClassificationName(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentClassificationNameToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("ACNAME", sortDirection)
: addOrderCriteria("ac.NAME", sortDirection);
}
@ -1207,7 +1238,10 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentChannel(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentColumnsToSelectClauseForOrdering = true;
return addOrderCriteria("CHANNEL", sortDirection);
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("CHANNEL", sortDirection)
: addOrderCriteria("a.CHANNEL", sortDirection);
}
@Override
@ -1242,7 +1276,10 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentReference(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentColumnsToSelectClauseForOrdering = true;
return addOrderCriteria("REF_VALUE", sortDirection);
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("REF_VALUE", sortDirection)
: addOrderCriteria("a.REF_VALUE", sortDirection);
}
@Override
@ -1265,7 +1302,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByAttachmentReceived(SortDirection sortDirection) {
joinWithAttachments = true;
addAttachmentColumnsToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("ARECEIVED", sortDirection)
: addOrderCriteria("a.RECEIVED", sortDirection);
}
@ -1926,7 +1964,8 @@ public class TaskQueryImpl implements TaskQuery {
public TaskQuery orderByWorkbasketName(SortDirection sortDirection) {
joinWithWorkbaskets = true;
addWorkbasketNameToSelectClauseForOrdering = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("WNAME", sortDirection)
: addOrderCriteria("w.NAME", sortDirection);
}
@ -1934,7 +1973,8 @@ public class TaskQueryImpl implements TaskQuery {
@Override
public TaskQuery orderByOwnerLongName(SortDirection sortDirection) {
joinWithUserInfo = true;
return DB.DB2 == getDB()
return (DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery())
? addOrderCriteria("ULONG_NAME", sortDirection)
: addOrderCriteria("u.LONG_NAME", sortDirection);
}
@ -1987,7 +2027,8 @@ public class TaskQueryImpl implements TaskQuery {
try {
taskanaEngine.openConnection();
this.columnName = columnName;
this.orderBy.clear();
this.orderByOuter.clear();
this.orderByInner.clear();
this.addOrderCriteria(columnName.toString(), sortDirection);
checkForIllegalParamCombinations();
checkOpenAndReadPermissionForSpecifiedWorkbaskets();
@ -2069,7 +2110,9 @@ public class TaskQueryImpl implements TaskQuery {
// optimized query for db2 can't be used for now in case of selectAndClaim because of temporary
// tables and the "for update" clause clashing in db2
private String getLinkToMapperScript() {
if (DB.DB2 == getDB() && !selectAndClaim) {
if (DB.DB2 == getDB()
&& !selectAndClaim
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery()) {
return LINK_TO_MAPPER_DB2;
} else if (selectAndClaim && DB.ORACLE == getDB()) {
return LINK_TO_MAPPER_ORACLE;
@ -2079,7 +2122,10 @@ public class TaskQueryImpl implements TaskQuery {
}
private String getLinkToCounterTaskScript() {
return DB.DB2 == getDB() ? LINK_TO_COUNTER_DB2 : LINK_TO_COUNTER;
return DB.DB2 == getDB()
&& taskanaEngine.getEngine().getConfiguration().isUseSpecificDb2Taskquery()
? LINK_TO_COUNTER_DB2
: LINK_TO_COUNTER;
}
private void validateAllTimeIntervals(TimeInterval[] intervals) {
@ -2239,7 +2285,16 @@ public class TaskQueryImpl implements TaskQuery {
if (sortDirection == null) {
sortDirection = SortDirection.ASCENDING;
}
orderBy.add(columnName + " " + sortDirection);
orderByInner.add(columnName + " " + sortDirection);
if (columnName.startsWith("a") || columnName.startsWith("w") || columnName.startsWith("c")) {
orderByOuter.add(columnName.replace(".", "").toUpperCase() + " " + sortDirection);
} else {
if (columnName.startsWith("u")) {
orderByOuter.add(columnName.replace(".", "").substring(1) + " " + sortDirection);
} else {
orderByOuter.add(columnName + " " + sortDirection);
}
}
return this;
}
@ -2249,8 +2304,8 @@ public class TaskQueryImpl implements TaskQuery {
+ taskanaEngine
+ ", taskService="
+ taskService
+ ", orderBy="
+ orderBy
+ ", orderByOuter="
+ orderByOuter
+ ", columnName="
+ columnName
+ ", accessIdIn="
@ -2279,6 +2334,10 @@ public class TaskQueryImpl implements TaskQuery {
+ addAttachmentClassificationNameToSelectClauseForOrdering
+ ", addWorkbasketNameToSelectClauseForOrdering="
+ addWorkbasketNameToSelectClauseForOrdering
+ ", groupByPor="
+ groupByPor
+ ", groupBySor="
+ groupBySor
+ ", taskId="
+ Arrays.toString(taskId)
+ ", taskIdNotIn="

View File

@ -43,6 +43,7 @@ public interface TaskQueryMapper {
@Result(property = "primaryObjRefImpl.value", column = "POR_VALUE")
@Result(property = "isRead", column = "IS_READ")
@Result(property = "isTransferred", column = "IS_TRANSFERRED")
@Result(property = "groupByCount", column = "R_COUNT")
@Result(property = "custom1", column = "CUSTOM_1")
@Result(property = "custom2", column = "CUSTOM_2")
@Result(property = "custom3", column = "CUSTOM_3")

View File

@ -24,15 +24,22 @@ public class TaskQuerySqlProvider {
@SuppressWarnings("unused")
public static String queryTaskSummaries() {
return OPENING_SCRIPT_TAG
+ openOuterClauseForGroupByPorOrSor()
+ "SELECT <if test=\"useDistinctKeyword\">DISTINCT</if> "
+ commonSelectFields()
+ "<if test='groupBySor != null'>, o.VALUE as SOR_VALUE </if>"
+ "<if test=\"addAttachmentColumnsToSelectClauseForOrdering\">"
+ ", a.CLASSIFICATION_ID, a.CLASSIFICATION_KEY, a.CHANNEL, a.REF_VALUE, a.RECEIVED"
+ ", a.CLASSIFICATION_ID as ACLASSIFICATION_ID, "
+ "a.CLASSIFICATION_KEY as ACLASSIFICATION_KEY, a.CHANNEL as ACHANNEL, "
+ "a.REF_VALUE as AREF_VALUE, a.RECEIVED as ARECEIVED"
+ "</if>"
+ "<if test=\"addClassificationNameToSelectClauseForOrdering\">, c.NAME </if>"
+ "<if test=\"addAttachmentClassificationNameToSelectClauseForOrdering\">, ac.NAME </if>"
+ "<if test=\"addWorkbasketNameToSelectClauseForOrdering\">, w.NAME </if>"
+ "<if test=\"joinWithUserInfo\">, u.LONG_NAME </if>"
+ "<if test=\"addClassificationNameToSelectClauseForOrdering\">, c.NAME as CNAME </if>"
+ "<if test=\"addAttachmentClassificationNameToSelectClauseForOrdering\">, "
+ "ac.NAME as ACNAME </if>"
+ "<if test=\"addWorkbasketNameToSelectClauseForOrdering\">, w.NAME as WNAME </if>"
+ "<if test=\"joinWithUserInfo\">, u.LONG_NAME</if>"
+ groupByPorIfActive()
+ groupBySorIfActive()
+ "FROM TASK t "
+ "<if test=\"joinWithAttachments\">"
+ "LEFT JOIN ATTACHMENT a ON t.ID = a.TASK_ID "
@ -57,8 +64,10 @@ public class TaskQuerySqlProvider {
+ commonTaskWhereStatement()
+ "<if test='selectAndClaim == true'> AND t.STATE = 'READY' </if>"
+ CLOSING_WHERE_TAG
+ "<if test='!orderBy.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderBy' separator=',' >${item}</foreach>"
+ closeOuterClauseForGroupByPor()
+ closeOuterClauseForGroupBySor()
+ "<if test='!orderByOuter.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderByOuter' separator=',' >${item}</foreach>"
+ "</if> "
+ "<if test='selectAndClaim == true'> "
+ "FETCH FIRST ROW ONLY FOR UPDATE"
@ -128,8 +137,8 @@ public class TaskQuerySqlProvider {
+ db2selectFields()
+ "FROM Y "
+ "WHERE FLAG = 1 "
+ "<if test='!orderBy.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderBy' separator=',' >${item}</foreach>"
+ "<if test='!orderByOuter.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderByOuter' separator=',' >${item}</foreach>"
+ "</if> "
+ "<if test='selectAndClaim == true'>"
+ "FETCH FIRST ROW ONLY FOR UPDATE WITH RS USE AND KEEP UPDATE LOCKS"
@ -184,8 +193,8 @@ public class TaskQuerySqlProvider {
+ commonTaskWhereStatement()
+ "<if test='selectAndClaim == true'> AND t.STATE = 'READY' </if>"
+ CLOSING_WHERE_TAG
+ "<if test='!orderBy.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderBy' separator=',' >${item}</foreach>"
+ "<if test='!orderByOuter.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderByOuter' separator=',' >${item}</foreach>"
+ "</if> "
+ "fetch first 1 rows only "
+ ") FOR UPDATE"
@ -196,6 +205,14 @@ public class TaskQuerySqlProvider {
public static String countQueryTasks() {
return OPENING_SCRIPT_TAG
+ "SELECT COUNT( <if test=\"useDistinctKeyword\">DISTINCT</if> t.ID) "
+ "<if test=\"groupByPor or groupBySor != null\"> "
+ "FROM (SELECT t.ID, t.POR_VALUE "
+ "</if> "
+ "<if test=\"groupBySor != null\"> "
+ ", o.VALUE as SOR_VALUE "
+ "</if> "
+ groupByPorIfActive()
+ groupBySorIfActive()
+ "FROM TASK t "
+ "<if test=\"joinWithAttachments\">"
+ "LEFT JOIN ATTACHMENT a ON t.ID = a.TASK_ID "
@ -216,6 +233,8 @@ public class TaskQuerySqlProvider {
+ checkForAuthorization()
+ commonTaskWhereStatement()
+ CLOSING_WHERE_TAG
+ closeOuterClauseForGroupByPor()
+ closeOuterClauseForGroupBySor()
+ CLOSING_SCRIPT_TAG;
}
@ -286,8 +305,8 @@ public class TaskQuerySqlProvider {
+ checkForAuthorization()
+ commonTaskWhereStatement()
+ CLOSING_WHERE_TAG
+ "<if test='!orderBy.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderBy' separator=',' >"
+ "<if test='!orderByInner.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderByInner' separator=',' >"
+ "<choose>"
+ "<when test=\"item.contains('TCLASSIFICATION_KEY ASC')\">"
+ "t.CLASSIFICATION_KEY ASC"
@ -379,6 +398,108 @@ public class TaskQuerySqlProvider {
+ "</if>";
}
private static String groupByPorIfActive() {
return "<if test=\"groupByPor\"> "
+ ", ROW_NUMBER() OVER (PARTITION BY POR_VALUE "
+ "<if test='!orderByInner.isEmpty() and !orderByInner.get(0).equals(\"POR_VALUE ASC\") "
+ "and !orderByInner.get(0).equals(\"POR_VALUE DESC\")'>"
+ "ORDER BY <foreach item='item' collection='orderByInner' separator=',' >${item}</foreach>"
+ "</if> "
+ "<if test='orderByInner.isEmpty() or orderByInner.get(0).equals(\"POR_VALUE ASC\") "
+ "or orderByInner.get(0).equals(\"POR_VALUE DESC\")'>"
+ "ORDER BY DUE ASC"
+ "</if> "
+ ")"
+ "AS rn"
+ "</if> ";
}
private static String groupBySorIfActive() {
return "<if test='groupBySor != null'> "
+ ", ROW_NUMBER() OVER (PARTITION BY o.VALUE "
+ "<if test='!orderByInner.isEmpty()'>"
+ "ORDER BY <foreach item='item' collection='orderByInner' separator=',' >${item}</foreach>"
+ "</if> "
+ "<if test='orderByInner.isEmpty()'>"
+ "ORDER BY DUE ASC"
+ "</if> "
+ ")"
+ "AS rn"
+ "</if> ";
}
private static String openOuterClauseForGroupByPorOrSor() {
return "<if test=\"groupByPor or groupBySor != null\"> "
+ "SELECT * FROM ("
+ "</if> ";
}
private static String closeOuterClauseForGroupByPor() {
return "<if test=\"groupByPor\"> "
+ ") t LEFT JOIN"
+ " (SELECT POR_VALUE as PVALUE, COUNT(POR_VALUE) AS R_COUNT "
+ "FROM (SELECT DISTINCT t.id , POR_VALUE "
+ "FROM TASK t"
+ "<if test=\"joinWithAttachments\">"
+ "LEFT JOIN ATTACHMENT a ON t.ID = a.TASK_ID "
+ "</if>"
+ "<if test=\"joinWithSecondaryObjectReferences\">"
+ "LEFT JOIN OBJECT_REFERENCE o ON t.ID = o.TASK_ID "
+ "</if>"
+ "<if test=\"joinWithClassifications\">"
+ "LEFT JOIN CLASSIFICATION c ON t.CLASSIFICATION_ID = c.ID "
+ "</if>"
+ "<if test=\"joinWithAttachmentClassifications\">"
+ "LEFT JOIN CLASSIFICATION ac ON a.CLASSIFICATION_ID = ac.ID "
+ "</if>"
+ "<if test=\"joinWithWorkbaskets\">"
+ "LEFT JOIN WORKBASKET w ON t.WORKBASKET_ID = w.ID "
+ "</if>"
+ "<if test=\"joinWithUserInfo\">"
+ "LEFT JOIN USER_INFO u ON t.owner = u.USER_ID "
+ "</if>"
+ OPENING_WHERE_TAG
+ checkForAuthorization()
+ commonTaskWhereStatement()
+ "<if test='selectAndClaim == true'> AND t.STATE = 'READY' </if>"
+ CLOSING_WHERE_TAG
+ ") as y "
+ "GROUP BY POR_VALUE) AS tt ON t.POR_VALUE=tt.PVALUE "
+ "WHERE rn = 1"
+ "</if> ";
}
private static String closeOuterClauseForGroupBySor() {
return "<if test='groupBySor != null'> "
+ ") t LEFT JOIN"
+ " (SELECT o.VALUE, COUNT(o.VALUE) AS R_COUNT "
+ "FROM TASK t "
+ "LEFT JOIN OBJECT_REFERENCE o on t.ID=o.TASK_ID "
+ "<if test=\"joinWithAttachments\">"
+ "LEFT JOIN ATTACHMENT a ON t.ID = a.TASK_ID "
+ "</if>"
+ "<if test=\"joinWithClassifications\">"
+ "LEFT JOIN CLASSIFICATION c ON t.CLASSIFICATION_ID = c.ID "
+ "</if>"
+ "<if test=\"joinWithAttachmentClassifications\">"
+ "LEFT JOIN CLASSIFICATION ac ON a.CLASSIFICATION_ID = ac.ID "
+ "</if>"
+ "<if test=\"joinWithWorkbaskets\">"
+ "LEFT JOIN WORKBASKET w ON t.WORKBASKET_ID = w.ID "
+ "</if>"
+ "<if test=\"joinWithUserInfo\">"
+ "LEFT JOIN USER_INFO u ON t.owner = u.USER_ID "
+ "</if>"
+ OPENING_WHERE_TAG
+ checkForAuthorization()
+ commonTaskWhereStatement()
+ "AND o.TYPE=#{groupBySor} "
+ CLOSING_WHERE_TAG
+ "GROUP BY o.VALUE) AS tt ON t.SOR_VALUE=tt.VALUE "
+ "WHERE rn = 1"
+ "</if> ";
}
private static String commonTaskObjectReferenceWhereStatement() {
return "<if test='objectReferences != null'>"
+ "AND (<foreach item='item' collection='objectReferences' separator=' OR '> "

View File

@ -33,6 +33,7 @@ public class TaskImpl extends TaskSummaryImpl implements Task {
callbackInfo = new HashMap<>(copyFrom.callbackInfo);
callbackState = copyFrom.callbackState;
attachments = copyFrom.attachments.stream().map(Attachment::copy).collect(Collectors.toList());
groupByCount = copyFrom.groupByCount;
}
public Map<String, String> getCustomAttributes() {
@ -280,6 +281,7 @@ public class TaskImpl extends TaskSummaryImpl implements Task {
taskSummary.setNote(note);
taskSummary.setDescription(description);
taskSummary.setOwner(owner);
taskSummary.setOwnerLongName(ownerLongName);
taskSummary.setParentBusinessProcessId(parentBusinessProcessId);
taskSummary.setPlanned(planned);
taskSummary.setReceived(received);

View File

@ -38,6 +38,7 @@ public class TaskSummaryImpl implements TaskSummary {
protected int manualPriority = DEFAULT_MANUAL_PRIORITY;
protected TaskState state;
protected ClassificationSummary classificationSummary;
protected Integer groupByCount;
protected WorkbasketSummary workbasketSummary;
protected String businessProcessId;
protected String parentBusinessProcessId;
@ -105,6 +106,7 @@ public class TaskSummaryImpl implements TaskSummary {
copyFrom.secondaryObjectReferences.stream()
.map(ObjectReference::copy)
.collect(Collectors.toList());
groupByCount = copyFrom.groupByCount;
custom1 = copyFrom.custom1;
custom2 = copyFrom.custom2;
custom3 = copyFrom.custom3;
@ -212,6 +214,11 @@ public class TaskSummaryImpl implements TaskSummary {
this.received = received != null ? received.truncatedTo(ChronoUnit.MILLIS) : null;
}
@Override
public Integer getGroupByCount() {
return this.groupByCount;
}
@Override
public Instant getDue() {
return due != null ? due.truncatedTo(ChronoUnit.MILLIS) : null;
@ -486,6 +493,10 @@ public class TaskSummaryImpl implements TaskSummary {
setWorkbasketSummary(workbasketSummary);
}
public void setGroupByCount(Integer n) {
groupByCount = n;
}
public void addAttachmentSummary(AttachmentSummary attachmentSummary) {
if (this.attachmentSummaries == null) {
this.attachmentSummaries = new ArrayList<>();
@ -797,6 +808,7 @@ public class TaskSummaryImpl implements TaskSummary {
primaryObjRef,
isRead,
isTransferred,
groupByCount,
attachmentSummaries,
secondaryObjectReferences,
custom1,
@ -864,6 +876,7 @@ public class TaskSummaryImpl implements TaskSummary {
&& Objects.equals(primaryObjRef, other.primaryObjRef)
&& Objects.equals(attachmentSummaries, other.attachmentSummaries)
&& Objects.equals(secondaryObjectReferences, other.secondaryObjectReferences)
&& Objects.equals(groupByCount, other.groupByCount)
&& Objects.equals(custom1, other.custom1)
&& Objects.equals(custom2, other.custom2)
&& Objects.equals(custom3, other.custom3)
@ -942,6 +955,8 @@ public class TaskSummaryImpl implements TaskSummary {
+ isRead
+ ", isTransferred="
+ isTransferred
+ ", groupByCount="
+ groupByCount
+ ", attachmentSummaries="
+ attachmentSummaries
+ ", objectReferences="

View File

@ -132,6 +132,11 @@ public class TaskBuilder implements SummaryEntityBuilder<TaskSummary, Task, Task
return this;
}
public TaskBuilder ownerLongName(String ownerLongName) {
testTask.setOwnerLongName(ownerLongName);
return this;
}
public TaskBuilder primaryObjRef(ObjectReference primaryObjRef) {
testTask.setPrimaryObjRef(primaryObjRef);
return this;
@ -161,6 +166,11 @@ public class TaskBuilder implements SummaryEntityBuilder<TaskSummary, Task, Task
return this;
}
public TaskBuilder groupByCount(Integer count) {
testTask.setGroupByCount(count);
return this;
}
public TaskBuilder attachments(Attachment... attachments) {
testTask.setAttachments(Arrays.asList(attachments));
return this;

View File

@ -212,7 +212,7 @@ class TaskBuilderTest {
expectedTask.setCallbackState(CallbackState.CALLBACK_PROCESSING_COMPLETED);
assertThat(task)
.hasNoNullFieldsOrPropertiesExcept("ownerLongName")
.hasNoNullFieldsOrPropertiesExcept("ownerLongName", "groupByCount")
.usingRecursiveComparison()
.ignoringFields("id")
.isEqualTo(expectedTask);

View File

@ -123,7 +123,8 @@ public class TaskController {
* @param request the HTTP request
* @param filterParameter the filter parameters
* @param filterCustomFields the filter parameters regarding TaskCustomFields
* @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields
* @param filterCustomIntFields the filter parameters regarding TaskCustomIntFields * @param
* @param groupByParameter the group by parameters
* @param sortParameter the sort parameters
* @param pagingParameter the paging parameters
* @return the Tasks with the given filter, sort and paging options.
@ -135,6 +136,7 @@ public class TaskController {
TaskQueryFilterParameter filterParameter,
TaskQueryFilterCustomFields filterCustomFields,
TaskQueryFilterCustomIntFields filterCustomIntFields,
TaskQueryGroupByParameter groupByParameter,
TaskQuerySortParameter sortParameter,
QueryPagingParameter<TaskSummary, TaskQuery> pagingParameter) {
QueryParamsValidator.validateParams(
@ -142,6 +144,7 @@ public class TaskController {
TaskQueryFilterParameter.class,
TaskQueryFilterCustomFields.class,
TaskQueryFilterCustomIntFields.class,
TaskQueryGroupByParameter.class,
QuerySortParameter.class,
QueryPagingParameter.class);
TaskQuery query = taskService.createTaskQuery();
@ -149,6 +152,7 @@ public class TaskController {
filterParameter.apply(query);
filterCustomFields.apply(query);
filterCustomIntFields.apply(query);
groupByParameter.apply(query);
sortParameter.apply(query);
List<TaskSummary> taskSummaries = pagingParameter.apply(query);

View File

@ -0,0 +1,61 @@
package pro.taskana.task.rest;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.beans.ConstructorProperties;
import java.util.Optional;
import java.util.function.Consumer;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.rest.QueryParameter;
import pro.taskana.task.api.TaskQuery;
public class TaskQueryGroupByParameter implements QueryParameter<TaskQuery, Void> {
public enum TaskQueryGroupBy {
POR_VALUE(TaskQuery::groupByPor);
private final Consumer<TaskQuery> consumer;
TaskQueryGroupBy(Consumer<TaskQuery> consumer) {
this.consumer = consumer;
}
public void applyGroupByForQuery(TaskQuery query) {
consumer.accept(query);
}
}
// region groupBy
@JsonProperty("group-by")
private final TaskQueryGroupBy groupByPor;
@JsonProperty("group-by-sor")
private final String groupBySor;
// endregion
// region constructor
@ConstructorProperties({"group-by", "group-by-sor"})
public TaskQueryGroupByParameter(TaskQueryGroupBy groupBy, String groupBySor)
throws InvalidArgumentException {
this.groupByPor = groupBy;
this.groupBySor = groupBySor;
validateGroupByParameters();
}
// endregion
@Override
public Void apply(TaskQuery query) {
Optional.ofNullable(groupBySor).ifPresent(query::groupBySor);
Optional.ofNullable(groupByPor)
.ifPresent(taskQueryGroupBy -> taskQueryGroupBy.applyGroupByForQuery(query));
return null;
}
private void validateGroupByParameters() throws InvalidArgumentException {
if (groupByPor != null && groupBySor != null) {
throw new InvalidArgumentException(
"Only one of the following can be provided: Either group-by or group-by-sor");
}
}
}

View File

@ -82,6 +82,7 @@ public class TaskRepresentationModelAssembler
.collect(Collectors.toList()));
repModel.setRead(task.isRead());
repModel.setTransferred(task.isTransferred());
repModel.setGroupByCount(task.getGroupByCount());
repModel.setAttachments(
task.getAttachments().stream()
.map(attachmentAssembler::toModel)
@ -159,6 +160,7 @@ public class TaskRepresentationModelAssembler
task.setPrimaryObjRef(objectReferenceAssembler.toEntity(repModel.getPrimaryObjRef()));
task.setRead(repModel.isRead());
task.setTransferred(repModel.isTransferred());
task.setGroupByCount(repModel.getGroupByCount());
task.setCustomField(TaskCustomField.CUSTOM_1, repModel.getCustom1());
task.setCustomField(TaskCustomField.CUSTOM_2, repModel.getCustom2());
task.setCustomField(TaskCustomField.CUSTOM_3, repModel.getCustom3());

View File

@ -82,6 +82,7 @@ public class TaskSummaryRepresentationModelAssembler
.collect(Collectors.toList()));
repModel.setRead(taskSummary.isRead());
repModel.setTransferred(taskSummary.isTransferred());
repModel.setGroupByCount(taskSummary.getGroupByCount());
repModel.setAttachmentSummaries(
taskSummary.getAttachmentSummaries().stream()
.map(attachmentAssembler::toModel)
@ -148,6 +149,7 @@ public class TaskSummaryRepresentationModelAssembler
.collect(Collectors.toList()));
taskSummary.setRead(repModel.isRead());
taskSummary.setTransferred(repModel.isTransferred());
taskSummary.setGroupByCount(repModel.getGroupByCount());
taskSummary.setAttachmentSummaries(
repModel.getAttachmentSummaries().stream()
.map(attachmentAssembler::toEntityModel)

View File

@ -79,6 +79,8 @@ public class TaskSummaryRepresentationModel
protected boolean isRead;
/** Indicator if the task has been transferred. */
protected boolean isTransferred;
/** Number of Tasks that are grouped together with this Task during a groupBy. */
protected Integer groupByCount;
/** A custom property with name "1". */
protected String custom1;
/** A custom property with name "2". */
@ -351,6 +353,14 @@ public class TaskSummaryRepresentationModel
this.attachmentSummaries = attachmentSummaries;
}
public Integer getGroupByCount() {
return groupByCount;
}
public void setGroupByCount(Integer groupByCount) {
this.groupByCount = groupByCount;
}
public String getCustom1() {
return custom1;
}

View File

@ -7,6 +7,7 @@ import static pro.taskana.rest.test.RestHelper.TEMPLATE;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
@ -34,6 +35,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpStatusCodeException;
import pro.taskana.TaskanaConfiguration;
import pro.taskana.classification.rest.models.ClassificationSummaryRepresentationModel;
import pro.taskana.common.rest.RestEndpoints;
import pro.taskana.rest.test.RestHelper;
@ -54,6 +56,7 @@ import pro.taskana.workbasket.rest.models.WorkbasketSummaryRepresentationModel;
@TaskanaSpringBootTest
class TaskControllerIntTest {
@Autowired TaskanaConfiguration taskanaConfiguration;
private static final ParameterizedTypeReference<TaskSummaryPagedRepresentationModel>
TASK_SUMMARY_PAGE_MODEL_TYPE = new ParameterizedTypeReference<>() {};
@ -1204,6 +1207,57 @@ class TaskControllerIntTest {
assertThat((response.getBody()).getLink(IanaLinkRelations.SELF)).isNotNull();
}
@Test
void should_GroupByPor() throws Exception {
Field useSpecificDb2Taskquery =
taskanaConfiguration.getClass().getDeclaredField("useSpecificDb2Taskquery");
useSpecificDb2Taskquery.setAccessible(true);
useSpecificDb2Taskquery.setBoolean(taskanaConfiguration, true);
String url = restHelper.toUrl(RestEndpoints.URL_TASKS) + "?group-by=POR_VALUE";
HttpEntity<String> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("admin"));
ResponseEntity<TaskSummaryPagedRepresentationModel> response =
TEMPLATE.exchange(url, HttpMethod.GET, auth, TASK_SUMMARY_PAGE_MODEL_TYPE);
assertThat(response.getBody()).isNotNull();
assertThat((response.getBody()).getLink(IanaLinkRelations.SELF)).isNotNull();
assertThat(response.getBody().getContent()).hasSize(14);
assertThat(
response.getBody().getContent().stream()
.filter(task -> task.getPrimaryObjRef().getValue().equals("MyValue1"))
.map(TaskSummaryRepresentationModel::getGroupByCount)
.toArray())
.containsExactly(6);
useSpecificDb2Taskquery.setBoolean(taskanaConfiguration, false);
}
@Test
void should_GroupBySor() throws Exception {
Field useSpecificDb2Taskquery =
taskanaConfiguration.getClass().getDeclaredField("useSpecificDb2Taskquery");
useSpecificDb2Taskquery.setAccessible(true);
useSpecificDb2Taskquery.setBoolean(taskanaConfiguration, true);
String url = restHelper.toUrl(RestEndpoints.URL_TASKS) + "?group-by-sor=Type2";
HttpEntity<String> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("admin"));
ResponseEntity<TaskSummaryPagedRepresentationModel> response =
TEMPLATE.exchange(url, HttpMethod.GET, auth, TASK_SUMMARY_PAGE_MODEL_TYPE);
assertThat(response.getBody()).isNotNull();
assertThat((response.getBody()).getLink(IanaLinkRelations.SELF)).isNotNull();
assertThat(response.getBody().getContent()).hasSize(1);
assertThat(
response.getBody().getContent().stream()
.map(TaskSummaryRepresentationModel::getGroupByCount)
.toArray())
.containsExactly(2);
useSpecificDb2Taskquery.setBoolean(taskanaConfiguration, false);
}
@Test
void testGetLastPageSortedByDueWithHiddenTasksRemovedFromResult() {
resetDb();

View File

@ -96,6 +96,7 @@ class TaskRepresentationModelAssemblerTest {
repModel.setCustomAttributes(List.of(TaskRepresentationModel.CustomAttribute.of("abc", "def")));
repModel.setCallbackInfo(List.of(TaskRepresentationModel.CustomAttribute.of("ghi", "jkl")));
repModel.setAttachments(List.of(attachment));
repModel.setGroupByCount(0);
repModel.setCustom1("custom1");
repModel.setCustom2("custom2");
repModel.setCustom3("custom3");
@ -195,6 +196,7 @@ class TaskRepresentationModelAssemblerTest {
task.setPrimaryObjRef(primaryObjRef);
task.setRead(true);
task.setTransferred(true);
task.setGroupByCount(0);
task.setCustomAttributeMap(Map.of("abc", "def"));
task.setCallbackInfo(Map.of("ghi", "jkl"));
task.setAttachments(List.of(attachment));
@ -268,6 +270,7 @@ class TaskRepresentationModelAssemblerTest {
task.setPrimaryObjRef(primaryObjRef);
task.setRead(true);
task.setTransferred(true);
task.setGroupByCount(0);
task.setCustomAttributeMap(Map.of("abc", "def"));
task.setCallbackInfo(Map.of("ghi", "jkl"));
task.setAttachments(List.of(attachment));

View File

@ -104,6 +104,7 @@ class TaskSummaryRepresentationModelAssemblerTest {
task.setPrimaryObjRef(primaryObjRef);
task.setRead(true);
task.setTransferred(true);
task.setGroupByCount(0);
task.setCustom1("custom1");
task.setCustom2("custom2");
task.setCustom3("custom3");
@ -178,6 +179,7 @@ class TaskSummaryRepresentationModelAssemblerTest {
repModel.setOwnerLongName("ownerLongName");
repModel.setRead(true);
repModel.setTransferred(true);
repModel.setGroupByCount(0);
repModel.setCustom1("custom1");
repModel.setCustom2("custom2");
repModel.setCustom3("custom3");
@ -276,6 +278,7 @@ class TaskSummaryRepresentationModelAssemblerTest {
task.setPrimaryObjRef(primaryObjRef);
task.setRead(true);
task.setTransferred(true);
task.setGroupByCount(0);
task.setCustom1("custom1");
task.setCustom2("custom2");
task.setCustom3("custom3");
@ -340,6 +343,7 @@ class TaskSummaryRepresentationModelAssemblerTest {
taskSummary.getPrimaryObjRef(), repModel.getPrimaryObjRef());
assertThat(taskSummary.isRead()).isEqualTo(repModel.isRead());
assertThat(taskSummary.isTransferred()).isEqualTo(repModel.isTransferred());
assertThat(taskSummary.getGroupByCount()).isEqualTo(repModel.getGroupByCount());
assertThat(taskSummary.getCustomField(CUSTOM_1)).isEqualTo(repModel.getCustom1());
assertThat(taskSummary.getCustomField(CUSTOM_2)).isEqualTo(repModel.getCustom2());
assertThat(taskSummary.getCustomField(CUSTOM_3)).isEqualTo(repModel.getCustom3());