Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix #2175]Adding count process intance and count user task queries #2196

Merged
merged 4 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .rat-excludes
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider
application.properties
# data-index/data-index-graphql/src/main/resources/basic.schema.graphqls
basic.schema.graphqls
# data-index/data-index-graphql/src/main/resources/count.schema.graphqls
count.schema.graphqls
# data-index/data-index-graphql/src/main/resources/json.schema.graphqls
json.schema.graphqls
# data-index/data-index-service/data-index-service-common/src/main/resources/domain.schema.graphqls
domain.schema.graphqls
# /data-index/data-index-mutations/data-index-shared-output-mutation/src/main/resources/mutation.schema.graphqls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.ServiceLoader.Provider;
import java.util.concurrent.CompletableFuture;
Expand All @@ -31,6 +30,7 @@
import org.kie.kogito.index.CommonUtils;
import org.kie.kogito.index.api.KogitoRuntimeClient;
import org.kie.kogito.index.graphql.query.GraphQLQueryOrderByParser;
import org.kie.kogito.index.graphql.query.GraphQLQueryParser;
import org.kie.kogito.index.graphql.query.GraphQLQueryParserRegistry;
import org.kie.kogito.index.model.Job;
import org.kie.kogito.index.model.Node;
Expand All @@ -41,13 +41,16 @@
import org.kie.kogito.index.service.DataIndexServiceException;
import org.kie.kogito.index.storage.DataIndexStorageService;
import org.kie.kogito.persistence.api.StorageFetcher;
import org.kie.kogito.persistence.api.StorageServiceCapability;
import org.kie.kogito.persistence.api.query.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import graphql.schema.GraphQLArgument;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLInputType;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLSchema;
Expand Down Expand Up @@ -109,6 +112,29 @@ protected final void loadAdditionalMutations(TypeDefinitionRegistry typeRegistry
mutations.stream().map(GraphQLMutationsProvider::registry).forEach(typeRegistry::merge);
}

protected final void addCountQueries(TypeDefinitionRegistry typeRegistry) {
if (supportsCount()) {
typeRegistry.merge(loadSchemaDefinitionFile("count.schema.graphqls"));
}
}

protected final void addJsonQueries(TypeDefinitionRegistry typeRegistry) {
if (cacheService.capabilities().contains(StorageServiceCapability.JSON_QUERY)) {
typeRegistry.merge(loadSchemaDefinitionFile("json.schema.graphqls"));
}
}

protected final void addCountQueries(Builder builder) {
if (supportsCount()) {
builder.dataFetcher("CountProcessInstances", this::countProcessInstances);
builder.dataFetcher("CountUserTaskInstances", this::countUserTaskInstances);
}
}

private boolean supportsCount() {
return cacheService.capabilities().contains(StorageServiceCapability.COUNT);
}

protected TypeDefinitionRegistry loadSchemaDefinitionFile(String fileName) {
return CommonUtils.loadSchemaDefinitionFile(fileName);
}
Expand Down Expand Up @@ -182,18 +208,17 @@ protected Collection<ProcessInstance> getProcessInstancesValues(DataFetchingEnvi
return executeAdvancedQueryForCache(cacheService.getProcessInstanceStorage(), env);
}

protected <K, T> List<T> executeAdvancedQueryForCache(StorageFetcher<K, T> cache, DataFetchingEnvironment env) {
Objects.requireNonNull(cache, "Cache not found");

String inputTypeName = ((GraphQLNamedType) env.getFieldDefinition().getArgument("where").getType()).getName();

Query<T> query = cache.query();
protected long countProcessInstances(DataFetchingEnvironment env) {
return executeCount(cacheService.getProcessInstanceStorage(), env);
}

Map<String, Object> where = env.getArgument("where");
query.filter(GraphQLQueryParserRegistry.get().getParser(inputTypeName).apply(where));
protected long countUserTaskInstances(DataFetchingEnvironment env) {
return executeCount(cacheService.getUserTaskInstanceStorage(), env);
}

protected <K, T> List<T> executeAdvancedQueryForCache(StorageFetcher<K, T> cache, DataFetchingEnvironment env) {
Query<T> query = buildQuery(cache, env);
query.sort(new GraphQLQueryOrderByParser().apply(env));

Map<String, Integer> pagination = env.getArgument("pagination");
if (pagination != null) {
Integer limit = pagination.get("limit");
Expand All @@ -205,10 +230,29 @@ protected <K, T> List<T> executeAdvancedQueryForCache(StorageFetcher<K, T> cache
query.offset(offset);
}
}

return query.execute();
}

protected <K, T> long executeCount(StorageFetcher<K, T> cache, DataFetchingEnvironment env) {
return buildQuery(cache, env).count();
}

private <K, T> Query<T> buildQuery(StorageFetcher<K, T> cache, DataFetchingEnvironment env) {
assert cache != null;
Query<T> query = cache.query();
GraphQLArgument arg = env.getFieldDefinition().getArgument("where");
if (arg != null) {
GraphQLInputType inputType = arg.getType();
if (inputType instanceof GraphQLNamedType) {
GraphQLQueryParser parser = GraphQLQueryParserRegistry.get().getParser(((GraphQLNamedType) inputType).getName());
if (parser != null) {
query.filter(parser.apply(env.getArgument("where")));
}
}
}
return query;
}

protected Collection<UserTaskInstance> getUserTaskInstancesValues(DataFetchingEnvironment env) {
return executeAdvancedQueryForCache(cacheService.getUserTaskInstanceStorage(), env);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ input ProcessDefinitionArgument {
serviceUrl: StringArgument
description: StringArgument
type: StringArgument
metadata: JSON
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are preventing json properties to appear int he contextual graphql help in case the datastore does not support it

}

type ProcessInstance {
Expand Down Expand Up @@ -183,7 +182,6 @@ input ProcessInstanceArgument {
id: IdArgument
processId: StringArgument
processName: StringArgument
variables: JSON
parentProcessInstanceId: IdArgument
rootProcessInstanceId: IdArgument
rootProcessId: StringArgument
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
extend type Query {
CountProcessInstances(where: ProcessInstanceArgument): Int
CountUserTaskInstances(where: UserTaskInstanceArgument): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extend input ProcessInstanceArgument {
variables: JSON
}

extend input ProcessDefinitionArgument {
metadata: JSON
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public GraphQLSchema createSchema() {
TypeDefinitionRegistry typeDefinitionRegistry = new TypeDefinitionRegistry();
typeDefinitionRegistry.merge(loadSchemaDefinitionFile("basic.schema.graphqls"));
typeDefinitionRegistry.merge(loadSchemaDefinitionFile("domain.schema.graphqls"));
addCountQueries(typeDefinitionRegistry);
addJsonQueries(typeDefinitionRegistry);
loadAdditionalMutations(typeDefinitionRegistry);

RuntimeWiring runtimeWiring = RuntimeWiring.newRuntimeWiring()
Expand All @@ -83,6 +85,7 @@ public GraphQLSchema createSchema() {
builder.dataFetcher("ProcessInstances", this::getProcessInstancesValues);
builder.dataFetcher("UserTaskInstances", this::getUserTaskInstancesValues);
builder.dataFetcher("Jobs", this::getJobsValues);
addCountQueries(builder);
return builder;
})
.type("Mutation", builder -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@
*/
package org.kie.kogito.index.storage;

import java.util.EnumSet;
import java.util.Set;

import org.kie.kogito.index.model.Job;
import org.kie.kogito.index.model.ProcessDefinition;
import org.kie.kogito.index.model.ProcessDefinitionKey;
import org.kie.kogito.persistence.api.Storage;
import org.kie.kogito.persistence.api.StorageServiceCapability;

import com.fasterxml.jackson.databind.node.ObjectNode;

public interface DataIndexStorageService {

Storage<ProcessDefinitionKey, ProcessDefinition> getProcessDefinitionStorage();

ProcessInstanceStorage getProcessInstanceStorage();
Expand All @@ -40,4 +43,8 @@ public interface DataIndexStorageService {
String getDomainModelCacheName(String processId);

Storage<String, String> getProcessIdModelCache();

default Set<StorageServiceCapability> capabilities() {
return EnumSet.noneOf(StorageServiceCapability.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
*/
package org.kie.kogito.index.jpa.storage;

import java.util.Set;

import org.kie.kogito.index.model.Job;
import org.kie.kogito.index.model.ProcessDefinition;
import org.kie.kogito.index.model.ProcessDefinitionKey;
import org.kie.kogito.index.storage.DataIndexStorageService;
import org.kie.kogito.index.storage.ProcessInstanceStorage;
import org.kie.kogito.index.storage.UserTaskInstanceStorage;
import org.kie.kogito.persistence.api.Storage;
import org.kie.kogito.persistence.api.StorageServiceCapability;

import com.fasterxml.jackson.databind.node.ObjectNode;

Expand Down Expand Up @@ -80,4 +83,9 @@ public String getDomainModelCacheName(String processId) {
public Storage<String, String> getProcessIdModelCache() {
throw new UnsupportedOperationException("Generic String cache not available in JPA");
}

@Override
public Set<StorageServiceCapability> capabilities() {
return processInstanceStorage.capabilities();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,20 +86,15 @@ public List<T> execute() {
CriteriaBuilder builder = repository.getEntityManager().getCriteriaBuilder();
CriteriaQuery<E> criteriaQuery = builder.createQuery(entityClass);
Root<E> root = criteriaQuery.from(entityClass);
if (filters != null && !filters.isEmpty()) {
List<Predicate> predicates = getPredicates(builder, root);
criteriaQuery.where(predicates.toArray(new Predicate[] {}));
}
addWhere(builder, criteriaQuery, root);
if (sortBy != null && !sortBy.isEmpty()) {
List<Order> orderBy = sortBy.stream().map(f -> {
Path attributePath = getAttributePath(root, f.getAttribute());
return f.getSort() == SortDirection.ASC ? builder.asc(attributePath) : builder.desc(attributePath);
}).collect(toList());
criteriaQuery.orderBy(orderBy);
}

jakarta.persistence.Query query = repository.getEntityManager().createQuery(criteriaQuery);

if (limit != null) {
query.setMaxResults(limit);
}
Expand All @@ -109,10 +104,6 @@ public List<T> execute() {
return (List<T>) query.getResultList().stream().map(mapper).collect(toList());
}

protected List<Predicate> getPredicates(CriteriaBuilder builder, Root<E> root) {
return filters.stream().map(filterPredicateFunction(root, builder)).collect(toList());
}

protected Function<AttributeFilter<?>, Predicate> filterPredicateFunction(Root<E> root, CriteriaBuilder builder) {
return filter -> buildPredicateFunction(filter, root, builder);
}
Expand Down Expand Up @@ -195,4 +186,19 @@ private List<Predicate> getRecursivePredicate(AttributeFilter<?> filter, Root<E>
.collect(toList());
}

@Override
public long count() {
CriteriaBuilder builder = repository.getEntityManager().getCriteriaBuilder();
CriteriaQuery<Long> criteriaQuery = builder.createQuery(Long.class);
Root<E> root = criteriaQuery.from(entityClass);
criteriaQuery.select(builder.count(root));
addWhere(builder, criteriaQuery, root);
return repository.getEntityManager().createQuery(criteriaQuery).getSingleResult();
}

private <V> void addWhere(CriteriaBuilder builder, CriteriaQuery<V> criteriaQuery, Root<E> root) {
if (filters != null && !filters.isEmpty()) {
criteriaQuery.where(filters.stream().map(filterPredicateFunction(root, builder)).toArray(Predicate[]::new));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -47,6 +48,7 @@
import org.kie.kogito.index.model.MilestoneStatus;
import org.kie.kogito.index.model.ProcessInstance;
import org.kie.kogito.index.storage.ProcessInstanceStorage;
import org.kie.kogito.persistence.api.StorageServiceCapability;

import io.quarkus.arc.DefaultBean;

Expand Down Expand Up @@ -234,4 +236,8 @@ private void indexVariable(ProcessInstanceEntity pi, ProcessInstanceVariableEven
private void indexSla(ProcessInstanceEntity orInit, ProcessInstanceSLAEventBody data) {
// SLA does nothing for now
}

public Set<StorageServiceCapability> capabilities() {
return EnumSet.of(StorageServiceCapability.COUNT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,21 @@
*/
package org.kie.kogito.index.jpa.query;

import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.Test;
import org.kie.kogito.event.process.ProcessInstanceStateDataEvent;
import org.kie.kogito.index.jpa.storage.ProcessInstanceEntityStorage;
import org.kie.kogito.index.test.TestUtils;
import org.kie.kogito.index.test.query.AbstractProcessInstanceQueryIT;

import jakarta.inject.Inject;

import static org.assertj.core.api.Assertions.assertThat;
import static org.kie.kogito.index.model.ProcessInstanceState.COMPLETED;
import static org.kie.kogito.persistence.api.query.QueryFilterFactory.in;

public abstract class AbstractProcessInstanceEntityQueryIT extends AbstractProcessInstanceQueryIT {

@Inject
Expand All @@ -32,4 +42,12 @@ public abstract class AbstractProcessInstanceEntityQueryIT extends AbstractProce
public ProcessInstanceEntityStorage getStorage() {
return storage;
}

@Test
void testCount() {
ProcessInstanceStateDataEvent processInstanceEvent = TestUtils.createProcessInstanceEvent(UUID.randomUUID().toString(), "counting", null, null, COMPLETED.ordinal());
storage.indexState(processInstanceEvent);
assertThat(storage.query().count()).isNotZero();
assertThat(storage.query().filter(List.of(in("state", List.of(34)))).count()).isZero();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,20 @@
*/
package org.kie.kogito.index.jpa.query;

import java.util.List;
import java.util.UUID;

import org.junit.jupiter.api.Test;
import org.kie.kogito.event.usertask.UserTaskInstanceStateDataEvent;
import org.kie.kogito.event.usertask.UserTaskInstanceStateEventBody;
import org.kie.kogito.index.jpa.storage.UserTaskInstanceEntityStorage;
import org.kie.kogito.index.test.query.AbstractUserTaskInstanceQueryIT;

import jakarta.inject.Inject;

import static org.assertj.core.api.Assertions.assertThat;
import static org.kie.kogito.persistence.api.query.QueryFilterFactory.in;

public abstract class AbstractUserTaskInstanceEntityQueryIT extends AbstractUserTaskInstanceQueryIT {

@Inject
Expand All @@ -37,4 +46,17 @@ public UserTaskInstanceEntityStorage getStorage() {
protected Boolean isDateTimeAsLong() {
return false;
}

@Test
void testCount() {
String taskId = UUID.randomUUID().toString();
String processInstanceId = UUID.randomUUID().toString();
UserTaskInstanceStateDataEvent event = new UserTaskInstanceStateDataEvent();
event.setKogitoProcessInstanceId(processInstanceId);
event.setKogitoUserTaskInstanceId(taskId);
event.setData(UserTaskInstanceStateEventBody.create().processInstanceId(processInstanceId).state("InProgress").userTaskInstanceId(taskId).build());
storage.indexState(event);
assertThat(storage.query().count()).isNotZero();
assertThat(storage.query().filter(List.of(in("state", List.of("Javierito")))).count()).isZero();
}
}
Loading