Skip to content

Commit

Permalink
chore(engine, engine-rest): Add Support for Sort By CreateTime
Browse files Browse the repository at this point in the history
- Fetch & Lock API
- Backend

Related-to: #3896
  • Loading branch information
psavidis authored Dec 13, 2023
1 parent b58307d commit 159d401
Show file tree
Hide file tree
Showing 28 changed files with 1,570 additions and 331 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,51 +16,39 @@
*/
package org.camunda.bpm.engine.rest.dto.externaltask;

import java.util.ArrayList;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.function.Consumer;
import javax.ws.rs.core.MultivaluedMap;

import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.externaltask.ExternalTaskQuery;
import org.camunda.bpm.engine.rest.dto.AbstractQueryDto;
import org.camunda.bpm.engine.rest.dto.CamundaQueryParam;
import org.camunda.bpm.engine.rest.dto.converter.BooleanConverter;
import org.camunda.bpm.engine.rest.dto.converter.DateConverter;
import org.camunda.bpm.engine.rest.dto.converter.LongConverter;
import org.camunda.bpm.engine.rest.dto.converter.StringListConverter;
import org.camunda.bpm.engine.rest.dto.converter.StringSetConverter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.camunda.bpm.engine.rest.dto.converter.LongConverter;

/**
* @author Thorben Lindhauer
*
*/
public class ExternalTaskQueryDto extends AbstractQueryDto<ExternalTaskQuery> {

public static final String SORT_BY_ID_VALUE = "id";
public static final String SORT_BY_LOCK_EXPIRATION_TIME = "lockExpirationTime";
public static final String SORT_BY_PROCESS_INSTANCE_ID = "processInstanceId";
public static final String SORT_BY_PROCESS_DEFINITION_ID = "processDefinitionId";
public static final String SORT_BY_PROCESS_DEFINITION_KEY = "processDefinitionKey";
public static final String SORT_BY_TENANT_ID = "tenantId";
public static final String SORT_BY_PRIORITY = "taskPriority";

public static final List<String> VALID_SORT_BY_VALUES;
static {
VALID_SORT_BY_VALUES = new ArrayList<>();
VALID_SORT_BY_VALUES.add(SORT_BY_ID_VALUE);
VALID_SORT_BY_VALUES.add(SORT_BY_LOCK_EXPIRATION_TIME);
VALID_SORT_BY_VALUES.add(SORT_BY_PROCESS_INSTANCE_ID);
VALID_SORT_BY_VALUES.add(SORT_BY_PROCESS_DEFINITION_ID);
VALID_SORT_BY_VALUES.add(SORT_BY_PROCESS_DEFINITION_KEY);
VALID_SORT_BY_VALUES.add(SORT_BY_TENANT_ID);
VALID_SORT_BY_VALUES.add(SORT_BY_PRIORITY);
}
public static final Map<String, Consumer<ExternalTaskQuery>> SORT_METHODS_BY_FIELD = Map.of(
"id", ExternalTaskQuery::orderById,
"lockExpirationTime", ExternalTaskQuery::orderByLockExpirationTime,
"processInstanceId", ExternalTaskQuery::orderByProcessInstanceId,
"processDefinitionId", ExternalTaskQuery::orderByProcessDefinitionId,
"processDefinitionKey", ExternalTaskQuery::orderByProcessDefinitionKey,
"tenantId", ExternalTaskQuery::orderByTenantId,
"taskPriority", ExternalTaskQuery::orderByPriority,
"createTime", ExternalTaskQuery::orderByCreateTime
);

protected String externalTaskId;
protected Set<String> externalTaskIds;
Expand Down Expand Up @@ -201,7 +189,7 @@ public void setPriorityLowerThanOrEquals(Long priorityLowerThanOrEquals) {

@Override
protected boolean isValidSortByValue(String value) {
return VALID_SORT_BY_VALUES.contains(value);
return SORT_METHODS_BY_FIELD.containsKey(value);
}

@Override
Expand Down Expand Up @@ -278,24 +266,10 @@ protected void applyFilters(ExternalTaskQuery query) {

@Override
protected void applySortBy(ExternalTaskQuery query, String sortBy, Map<String, Object> parameters, ProcessEngine engine) {
if (SORT_BY_ID_VALUE.equals(sortBy)) {
query.orderById();
}
else if (SORT_BY_LOCK_EXPIRATION_TIME.equals(sortBy)) {
query.orderByLockExpirationTime();
}
else if (SORT_BY_PROCESS_DEFINITION_ID.equals(sortBy)) {
query.orderByProcessDefinitionId();
}
else if (SORT_BY_PROCESS_DEFINITION_KEY.equals(sortBy)) {
query.orderByProcessDefinitionKey();
}
else if (SORT_BY_PROCESS_INSTANCE_ID.equals(sortBy)) {
query.orderByProcessInstanceId();
} else if (sortBy.equals(SORT_BY_TENANT_ID)) {
query.orderByTenantId();
} else if (sortBy.equals(SORT_BY_PRIORITY)) {
query.orderByPriority();
var sortByMethod = SORT_METHODS_BY_FIELD.get(sortBy);

if (sortByMethod != null) {
sortByMethod.accept(query);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@
*/
package org.camunda.bpm.engine.rest.dto.externaltask;

import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.externaltask.ExternalTaskQueryBuilder;
import org.camunda.bpm.engine.externaltask.ExternalTaskQueryTopicBuilder;

import static java.lang.Boolean.TRUE;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import org.camunda.bpm.engine.ExternalTaskService;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.externaltask.ExternalTaskQueryTopicBuilder;
import org.camunda.bpm.engine.externaltask.FetchAndLockBuilder;
import org.camunda.bpm.engine.impl.util.CollectionUtil;
import org.camunda.bpm.engine.rest.dto.SortingDto;

/**
* @author Thorben Lindhauer
Expand All @@ -37,21 +43,28 @@ public class FetchExternalTasksDto {
protected List<FetchExternalTaskTopicDto> topics;
protected boolean includeExtensionProperties = false;

protected List<SortingDto> sortings;

public int getMaxTasks() {
return maxTasks;
}

public void setMaxTasks(int maxTasks) {
this.maxTasks = maxTasks;
}

public String getWorkerId() {
return workerId;
}

public void setWorkerId(String workerId) {
this.workerId = workerId;
}

public List<FetchExternalTaskTopicDto> getTopics() {
return topics;
}

public void setTopics(List<FetchExternalTaskTopicDto> topics) {
this.topics = topics;
}
Expand All @@ -64,6 +77,14 @@ public void setUsePriority(boolean usePriority) {
this.usePriority = usePriority;
}

public void setSortings(List<SortingDto> sortings) {
this.sortings = sortings;
}

public List<SortingDto> getSortings() {
return this.sortings;
}

public boolean isIncludeExtensionProperties() {
return includeExtensionProperties;
}
Expand Down Expand Up @@ -182,73 +203,136 @@ public void setIncludeExtensionProperties(boolean includeExtensionProperties) {
}
}

public ExternalTaskQueryBuilder buildQuery(ProcessEngine processEngine) {
ExternalTaskQueryBuilder fetchBuilder = processEngine
.getExternalTaskService()
.fetchAndLock(getMaxTasks(), getWorkerId(), isUsePriority());
public ExternalTaskQueryTopicBuilder buildQuery(ProcessEngine processEngine) {
FetchAndLockBuilder fetchAndLockBuilder = getBuilder(processEngine);

if (getTopics() != null) {
for (FetchExternalTaskTopicDto topicDto : getTopics()) {
ExternalTaskQueryTopicBuilder topicFetchBuilder =
fetchBuilder.topic(topicDto.getTopicName(), topicDto.getLockDuration());
return configureTopics(fetchAndLockBuilder);
}

if (topicDto.getBusinessKey() != null) {
topicFetchBuilder = topicFetchBuilder.businessKey(topicDto.getBusinessKey());
}
protected ExternalTaskQueryTopicBuilder configureTopics(FetchAndLockBuilder builder) {
ExternalTaskQueryTopicBuilder topicBuilder = builder.subscribe();

if (topicDto.getProcessDefinitionId() != null) {
topicFetchBuilder.processDefinitionId(topicDto.getProcessDefinitionId());
}
if (CollectionUtil.isEmpty(topics)) {
return topicBuilder;
}

if (topicDto.getProcessDefinitionIdIn() != null) {
topicFetchBuilder.processDefinitionIdIn(topicDto.getProcessDefinitionIdIn());
}
topics.forEach(topic -> {
topicBuilder.topic(topic.getTopicName(), topic.getLockDuration());

if (topicDto.getProcessDefinitionKey() != null) {
topicFetchBuilder.processDefinitionKey(topicDto.getProcessDefinitionKey());
}
if (topic.getBusinessKey() != null) {
topicBuilder.businessKey(topic.getBusinessKey());
}

if (topicDto.getProcessDefinitionKeyIn() != null) {
topicFetchBuilder.processDefinitionKeyIn(topicDto.getProcessDefinitionKeyIn());
}
if (topic.getProcessDefinitionId() != null) {
topicBuilder.processDefinitionId(topic.getProcessDefinitionId());
}

if (topicDto.getVariables() != null) {
topicFetchBuilder = topicFetchBuilder.variables(topicDto.getVariables());
}
if (topic.getProcessDefinitionIdIn() != null) {
topicBuilder.processDefinitionIdIn(topic.getProcessDefinitionIdIn());
}

if (topicDto.getProcessVariables() != null) {
topicFetchBuilder = topicFetchBuilder.processInstanceVariableEquals(topicDto.getProcessVariables());
}
if (topic.getProcessDefinitionKey() != null) {
topicBuilder.processDefinitionKey(topic.getProcessDefinitionKey());
}

if (topicDto.isDeserializeValues()) {
topicFetchBuilder = topicFetchBuilder.enableCustomObjectDeserialization();
}
if (topic.getProcessDefinitionKeyIn() != null) {
topicBuilder.processDefinitionKeyIn(topic.getProcessDefinitionKeyIn());
}

if (topicDto.isLocalVariables()) {
topicFetchBuilder = topicFetchBuilder.localVariables();
}
if (topic.getVariables() != null) {
topicBuilder.variables(topic.getVariables());
}

if (TRUE.equals(topicDto.isWithoutTenantId())) {
topicFetchBuilder = topicFetchBuilder.withoutTenantId();
}
if (topic.getProcessVariables() != null) {
topicBuilder.processInstanceVariableEquals(topic.getProcessVariables());
}

if (topicDto.getTenantIdIn() != null) {
topicFetchBuilder = topicFetchBuilder.tenantIdIn(topicDto.getTenantIdIn());
}
if (topic.isDeserializeValues()) {
topicBuilder.enableCustomObjectDeserialization();
}

if(topicDto.getProcessDefinitionVersionTag() != null) {
topicFetchBuilder = topicFetchBuilder.processDefinitionVersionTag(topicDto.getProcessDefinitionVersionTag());
}
if (topic.isLocalVariables()) {
topicBuilder.localVariables();
}

if (TRUE.equals(topic.isWithoutTenantId())) {
topicBuilder.withoutTenantId();
}

if(topicDto.isIncludeExtensionProperties()) {
topicFetchBuilder = topicFetchBuilder.includeExtensionProperties();
}
if (topic.getTenantIdIn() != null) {
topicBuilder.tenantIdIn(topic.getTenantIdIn());
}

if(topic.getProcessDefinitionVersionTag() != null) {
topicBuilder.processDefinitionVersionTag(topic.getProcessDefinitionVersionTag());
}

fetchBuilder = topicFetchBuilder;
if(topic.isIncludeExtensionProperties()) {
topicBuilder.includeExtensionProperties();
}
});

return topicBuilder;
}

protected FetchAndLockBuilder getBuilder(ProcessEngine engine) {
ExternalTaskService service = engine.getExternalTaskService();

FetchAndLockBuilder builder = service.fetchAndLock()
.workerId(workerId)
.maxTasks(maxTasks)
.usePriority(usePriority);

SortMapper mapper = new SortMapper(sortings, builder);

return mapper.getBuilderWithSortConfigs();
}

/**
* Encapsulates the mapping of sorting configurations (field, order) to the respective methods builder config methods
* and applies them.
* <p>
* To achieve that, maps are used internally to map fields and orders to the corresponding builder method.
* It works with case-insensitive orders (e.g will work with "asc", "ASC").
*/
static class SortMapper {

protected static Map<String, Consumer<FetchAndLockBuilder>> FIELD_MAPPINGS = Map.of(
"createTime", FetchAndLockBuilder::orderByCreateTime
);

protected static Map<String, Consumer<FetchAndLockBuilder>> ORDER_MAPPINGS = Map.of(
"asc", FetchAndLockBuilder::asc,
"desc", FetchAndLockBuilder::desc
);

protected final List<SortingDto> sortings;
protected final FetchAndLockBuilder builder;

protected SortMapper(List<SortingDto> sortings, FetchAndLockBuilder builder) {
this.sortings = (sortings == null) ? Collections.emptyList() : sortings;
this.builder = builder;
}

return fetchBuilder;
/**
* Applies the sorting field mappings to the builder and returns it.
*/
protected FetchAndLockBuilder getBuilderWithSortConfigs() {
sortings.forEach(dto -> {
fieldMappingKey(dto).ifPresent(key -> FIELD_MAPPINGS.get(key).accept(builder));
orderMappingKey(dto).ifPresent(key -> ORDER_MAPPINGS.get(key).accept(builder));
});

return builder;
}

protected Optional<String> fieldMappingKey(SortingDto dto) {
return Optional.ofNullable(dto.getSortBy());
}

protected Optional<String> orderMappingKey(SortingDto dto) {
return Optional.ofNullable(dto.getSortOrder());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@
import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.core.Response.Status;

import org.camunda.bpm.engine.IdentityService;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.externaltask.ExternalTaskQueryBuilder;
import org.camunda.bpm.engine.externaltask.ExternalTaskQueryTopicBuilder;
import org.camunda.bpm.engine.externaltask.LockedExternalTask;
import org.camunda.bpm.engine.impl.ProcessEngineImpl;
import org.camunda.bpm.engine.impl.identity.Authentication;
Expand Down Expand Up @@ -264,8 +262,9 @@ protected FetchAndLockResult tryFetchAndLock(FetchAndLockRequest request) {
}

protected List<LockedExternalTaskDto> executeFetchAndLock(FetchExternalTasksExtendedDto fetchingDto, ProcessEngine processEngine) {
ExternalTaskQueryBuilder fetchBuilder = fetchingDto.buildQuery(processEngine);
ExternalTaskQueryTopicBuilder fetchBuilder = fetchingDto.buildQuery(processEngine);
List<LockedExternalTask> externalTasks = fetchBuilder.execute();

return LockedExternalTaskDto.fromLockedExternalTasks(externalTasks);
}

Expand Down
Loading

0 comments on commit 159d401

Please sign in to comment.