From de552f22deee5a06623e0b0a71058267abe7160c Mon Sep 17 00:00:00 2001 From: nmirasch Date: Thu, 25 Jan 2024 18:39:05 +0100 Subject: [PATCH 1/3] incubator-kie-kogito-apps-issues#1964: Allow multiple execution of dataindex postgresql migration scripts (#1966) --- ....2__data_index_definitions_add_columns.sql} | 18 +++++++++++++----- .../src/main/resources/cache_entity_create.sql | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) rename data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/{V1.45.0.2__data_index_definitions_add_collums.sql => V1.45.0.2__data_index_definitions_add_columns.sql} (68%) diff --git a/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_collums.sql b/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_columns.sql similarity index 68% rename from data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_collums.sql rename to data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_columns.sql index c976a3ee0b..0506531e0c 100644 --- a/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_collums.sql +++ b/data-index/data-index-storage/data-index-storage-postgresql/src/main/resources/db/migration/V1.45.0.2__data_index_definitions_add_columns.sql @@ -1,19 +1,23 @@ -create table definitions_annotations +create table IF NOT EXISTS definitions_annotations ( value varchar(255) not null, process_id varchar(255) not null, process_version varchar(255) not null, primary key (value, process_id, process_version) -); + ); -create table definitions_metadata +create table IF NOT EXISTS definitions_metadata ( process_id varchar(255) not null, process_version varchar(255) not null, value varchar(255), key varchar(255) not null, primary key (process_id, process_version, key) -); + ); + +alter table if exists definitions_annotations +drop constraint if exists fk_definitions_annotations +cascade; alter table if exists definitions_annotations add constraint fk_definitions_annotations @@ -23,6 +27,10 @@ alter table if exists definitions_annotations delete cascade; +alter table if exists definitions_metadata +drop constraint if exists fk_definitions_metadata +cascade; + alter table if exists definitions_metadata add constraint fk_definitions_metadata foreign key (process_id, process_version) @@ -32,4 +40,4 @@ delete cascade; alter table if exists definitions - add column description varchar (255); \ No newline at end of file + add column IF NOT EXISTS description varchar (255); \ No newline at end of file diff --git a/persistence-commons/persistence-commons-postgresql/src/main/resources/cache_entity_create.sql b/persistence-commons/persistence-commons-postgresql/src/main/resources/cache_entity_create.sql index e24b58fdde..74c48518d6 100644 --- a/persistence-commons/persistence-commons-postgresql/src/main/resources/cache_entity_create.sql +++ b/persistence-commons/persistence-commons-postgresql/src/main/resources/cache_entity_create.sql @@ -1,5 +1,5 @@ -create table kogito_data_cache ( +create table if not exists kogito_data_cache ( key varchar(255) not null, name varchar(255) not null, json_value jsonb, From 57c440b4ecb0b24fcbbcc4c0a30d7366a0bf6725 Mon Sep 17 00:00:00 2001 From: Enrique Date: Thu, 1 Feb 2024 09:03:29 +0100 Subject: [PATCH 2/3] [incubator-kie-issues-834] Support Dynamic GraphQL query registering in Data Audit (#1962) [incubator-kie-issues-834] Support Dynamic GraphQL query registering in Data Audit --- data-audit/README.md | 44 ++- .../kogito/app/audit/api/DataAuditQuery.java | 58 +++ .../app/audit/api/DataAuditQueryService.java | 12 +- .../audit/api/DataAuditStoreProxyService.java | 10 + .../app/audit/api/SubsystemConstants.java | 2 + .../app/audit/graphql/GraphQLSchemaBuild.java | 29 ++ .../audit/graphql/GraphQLSchemaManager.java | 78 +++- .../kogito/app/audit/spi/DataAuditStore.java | 7 + .../app/audit/spi/GraphQLSchemaQuery.java | 2 +- .../audit/spi/GraphQLSchemaQueryProvider.java | 4 +- .../app/audit/quarkus/DataAuditTestUtils.java | 0 .../app/audit/jpa/JPADataAuditStore.java | 28 ++ .../app/audit/jpa/model/AuditQuery.java | 64 ++++ .../audit/jpa/queries/JPAAbstractQuery.java | 19 +- .../jpa/queries/JPAComplexNamedQuery.java | 2 +- .../audit/jpa/queries/JPADynamicQuery.java | 122 ++++++ .../JPAGraphQLSchemaDynamicQueryProvider.java | 44 +++ .../JPAGraphQLSchemaJobsQueryProvider.java | 3 +- ...QLSchemaProcessInstancesQueryProvider.java | 5 +- ...LSchemaUserTaskInstancesQueryProvider.java | 3 +- .../jpa/queries/JPASimpleNamedQuery.java | 2 +- ...o.app.audit.spi.GraphQLSchemaQueryProvider | 3 +- ...0__Add Query audit dynamic registering.sql | 2 + ...0__Add Query audit dynamic registering.sql | 2 + .../QuarkusJPADataAuditContextFactory.java | 2 + .../kogito-addons-data-audit-quarkus/pom.xml | 5 + .../audit/quarkus/GraphQLDataAuditRouter.java | 131 +++++++ .../quarkus/GraphQLJPADataAuditRouter.java | 69 ---- ...va => QuarkusDataAuditEventPublisher.java} | 6 +- .../quarkus/QuarkusAuditJobServiceTest.java | 24 +- ...uarkusAuditProcessInstanceServiceTest.java | 18 +- .../QuarkusAuditQueryRegistryServiceTest.java | 340 +++++++++++++++++ ...arkusAuditUserTaskInstanceServiceTest.java | 12 +- .../QuarkusEmbeddedJPADataAuditTest.java | 2 +- .../src/test/resources/application.properties | 1 - .../pom.xml | 5 + .../GraphQLAuditDataRouteMapping.java | 100 +++++ .../GraphQLJPAAuditDataRouteMapping.java | 60 --- ...=> SpringbootDataAuditEventPublisher.java} | 6 +- ...ringbootAuditQueryRegistryServiceTest.java | 357 ++++++++++++++++++ .../SpringbootJPADataAuditTest.java | 2 +- kogito-apps-bom/pom.xml | 6 + 42 files changed, 1493 insertions(+), 198 deletions(-) create mode 100644 data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQuery.java create mode 100644 data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaBuild.java rename data-audit/{kogito-addons-data-audit-quarkus => data-audit-common}/src/test/java/org/kie/kogito/app/audit/quarkus/DataAuditTestUtils.java (100%) create mode 100644 data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/model/AuditQuery.java create mode 100644 data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPADynamicQuery.java create mode 100644 data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaDynamicQueryProvider.java create mode 100644 data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/h2/V1.1.0__Add Query audit dynamic registering.sql create mode 100644 data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/postgresql/V1.1.0__Add Query audit dynamic registering.sql create mode 100644 data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLDataAuditRouter.java delete mode 100644 data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLJPADataAuditRouter.java rename data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/{QuarkusJPADataAuditEventPublisher.java => QuarkusDataAuditEventPublisher.java} (94%) create mode 100644 data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditQueryRegistryServiceTest.java create mode 100644 data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLAuditDataRouteMapping.java delete mode 100644 data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLJPAAuditDataRouteMapping.java rename data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/{SpringbootJPADataAuditEventPublisher.java => SpringbootDataAuditEventPublisher.java} (94%) create mode 100644 data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootAuditQueryRegistryServiceTest.java diff --git a/data-audit/README.md b/data-audit/README.md index e567bba444..a137058a37 100644 --- a/data-audit/README.md +++ b/data-audit/README.md @@ -25,7 +25,6 @@ Data Audit «SpringBoot»: Provides the wiring to use Data Audit with SpringBoot Now we have and implementation examples - Data Audit JPA Common: Provides the common exension not depending on the runtime Data Audit JPA «Quarkus»: Provides the wiring between the specific implementation and Quarkus System Data Audit JPA «SpringBoot»: Provides the wiring between the specific implementation and Springboot colocated system @@ -35,9 +34,46 @@ Data Audit JPA «SpringBoot»: Provides the wiring between the specific implemen The way to retrieve information from the data audit is using GraphQL. This way we can abstract how the information is retrieved and allow different needs depending on the user. +The Path is `${HOST}/data-audit/q` for sending GraphQL queries. + +### Example + +Execute a registered query, eg. `GetAllProcessInstancesState` with a definition of data fields that should be returned: + +``` +curl -H "Content-Type: application/json" -H "Accept: application/json" -s -X POST http://${HOST}/data-audit/q/ -d ' +{ + "query": "{GetAllProcessInstancesState {eventId, processInstanceId, eventType, eventDate}}" +}'|jq +``` + +To retrieve the GraphQL schema definition including a list of all registered queries, run a GET command to the `${HOST}/data-audit/r` endpoint. This endpoint can also be used to register new queries. + +### Example + +Register a new query with a complex data type: + +``` +curl -H "Content-Type: application/json" -H "Accept: application/json" -s -X POST http://${HOST}/data-audit/r/ -d ' +{ + "identifier" : "tests", + "graphQLDefinition" : "type EventTest { jobId : String, processInstanceId: String} type Query { tests (pagination: Pagination) : [ EventTest ] } ", + "query" : "SELECT o.job_id, o.process_instance_id FROM job_execution_log o" +}' +``` + +Once registered, the new query can be executed similar to the pre-registered ones using the `${HOST}/data-audit/q` endpoint: + +``` +curl -H "Content-Type: application/json" -H "Accept: application/json" -s -X POST http://${HOST}/data-audit/q/ -d ' +{ + "query": "{tests {jobId, processInstanceId}}" +}'|jq +``` + ## JPA implementation -The jpa implementation allows you to store those events to be stored in a database. +The jpa implementation allows you to store those events to be stored in a database. The only thing required in this case is to setup the datasource. ## Extension Points @@ -54,7 +90,7 @@ org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider: this allow the subsyste ## How to use in with Quarkus/Springboot -You need to add two different dependencies to your project. +You need to add two different dependencies to your project (collocated service) org.kie.kogito @@ -68,8 +104,6 @@ You need to add two different dependencies to your project. - - The first dependency is related how to you want to deploy it. In this case as collocated/embedded service The second dependency is which implementation you want to use. diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQuery.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQuery.java new file mode 100644 index 0000000000..96b5f21d86 --- /dev/null +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQuery.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.api; + +public class DataAuditQuery { + + private String identifier; + + private String graphQLDefinition; + + private String query; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getGraphQLDefinition() { + return graphQLDefinition; + } + + public void setGraphQLDefinition(String graphQLDefinition) { + this.graphQLDefinition = graphQLDefinition; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + + @Override + public String toString() { + return "DataAuditQuery [identifier=" + identifier + ", graphQLDefinition=" + graphQLDefinition + ", query=" + query + "]"; + } + +} diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQueryService.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQueryService.java index fdcf3eaa66..37e3825440 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQueryService.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditQueryService.java @@ -33,23 +33,21 @@ public class DataAuditQueryService { private GraphQLSchemaManager graphQLManager; - private GraphQL graphQL; private DataAuditQueryService(GraphQLSchemaManager graphQLManager) { this.graphQLManager = graphQLManager; - this.graphQL = GraphQL.newGraphQL(graphQLManager.getGraphQLSchema()).build(); } public GraphQLSchema getGraphQLSchema() { return this.graphQLManager.getGraphQLSchema(); } - public ExecutionResult executeQuery(DataAuditContext context, String query) { - return executeQuery(context, query, emptyMap()); + public GraphQL getGraphQL() { + return this.graphQLManager.getGraphQL(); } - public ExecutionResult executeQuery(String query) { - return executeQuery(null, query, emptyMap()); + public ExecutionResult executeQuery(DataAuditContext context, String query) { + return executeQuery(context, query, emptyMap()); } public ExecutionResult executeQuery(DataAuditContext context, String query, Map variables) { @@ -59,7 +57,7 @@ public ExecutionResult executeQuery(DataAuditContext context, String query, Map< .variables(variables) .build(); - return graphQL.execute(executionInput); + return graphQLManager.execute(executionInput); } public static DataAuditQueryService newAuditQuerySerice() { diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditStoreProxyService.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditStoreProxyService.java index 6ab14c52d8..8d844d0a02 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditStoreProxyService.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/DataAuditStoreProxyService.java @@ -18,6 +18,7 @@ */ package org.kie.kogito.app.audit.api; +import java.util.List; import java.util.ServiceLoader; import org.kie.kogito.app.audit.spi.DataAuditStore; @@ -81,4 +82,13 @@ public static DataAuditStoreProxyService newAuditStoreService() { LOGGER.debug("Creating new Data Audit Store proxy service with {}", service); return new DataAuditStoreProxyService(service); } + + public void storeQuery(DataAuditContext newDataAuditContext, DataAuditQuery dataAuditQuery) { + LOGGER.info("Store query {}", dataAuditQuery); + auditStoreService.storeQuery(newDataAuditContext, dataAuditQuery); + } + + public List findQueries(DataAuditContext context) { + return auditStoreService.findQueries(context); + } } diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/SubsystemConstants.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/SubsystemConstants.java index 1f56dd2944..a4267d9d1d 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/SubsystemConstants.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/api/SubsystemConstants.java @@ -25,6 +25,8 @@ private SubsystemConstants() { } public static final String DATA_AUDIT_PATH = "/data-audit"; + public static final String DATA_AUDIT_QUERY_PATH = DATA_AUDIT_PATH + "/q"; + public static final String DATA_AUDIT_REGISTRY_PATH = DATA_AUDIT_PATH + "/r"; public static final String KOGITO_PROCESSINSTANCES_EVENTS = "kogito-processinstances-events"; public static final String KOGITO_USERTASKINSTANCES_EVENTS = "kogito-usertaskinstances-events"; diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaBuild.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaBuild.java new file mode 100644 index 0000000000..10c7b2f664 --- /dev/null +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaBuild.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.kie.kogito.app.audit.graphql; + +import java.util.Map; + +import graphql.GraphQL; +import graphql.schema.GraphQLSchema; + +public record GraphQLSchemaBuild(GraphQLSchema graphQLSchema, GraphQL graphQL, Map additionalDefinitions) { + +} diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaManager.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaManager.java index 48b31dfef6..b689d5f0eb 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaManager.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/graphql/GraphQLSchemaManager.java @@ -18,18 +18,26 @@ */ package org.kie.kogito.app.audit.graphql; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.ServiceLoader; +import org.kie.kogito.app.audit.api.DataAuditContext; +import org.kie.kogito.app.audit.api.DataAuditQuery; import org.kie.kogito.app.audit.spi.GraphQLSchemaQuery; import org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import graphql.ExecutionInput; +import graphql.ExecutionResult; +import graphql.GraphQL; import graphql.language.FieldDefinition; import graphql.language.ObjectTypeDefinition; import graphql.scalars.ExtendedScalars; @@ -37,6 +45,7 @@ import graphql.schema.idl.RuntimeWiring; import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; +import graphql.schema.idl.SchemaPrinter; import graphql.schema.idl.TypeDefinitionRegistry; import static graphql.schema.idl.RuntimeWiring.newRuntimeWiring; @@ -49,12 +58,20 @@ public class GraphQLSchemaManager { private GraphQLSchema graphQLSchema; + private GraphQL graphQL; + + private Map graphQLdefinitions; + public static GraphQLSchemaManager graphQLSchemaManagerInstance() { return INSTANCE; } private GraphQLSchemaManager() { + this.graphQLdefinitions = new HashMap<>(); + } + public GraphQLSchemaBuild rebuildDefinitions(DataAuditContext dataAuditContext, Map additionalDefinitions) { + LOGGER.debug("Rebuilding graphQL definitions"); RuntimeWiring.Builder runtimeWiringBuilder = newRuntimeWiring(); runtimeWiringBuilder.scalar(ExtendedScalars.GraphQLBigInteger); @@ -77,15 +94,19 @@ private GraphQLSchemaManager() { ServiceLoader.load(GraphQLSchemaQueryProvider.class, classLoader).forEach(queryProvider -> { graphqlSchemas.addAll(List.of(queryProvider.graphQLQueryExtension())); - for (GraphQLSchemaQuery query : queryProvider.queries()) { + for (GraphQLSchemaQuery query : queryProvider.queries(dataAuditContext)) { runtimeWiringBuilder.type("Query", builder -> builder.dataFetcher(query.name(), query::fetch)); } }); + List data = new ArrayList<>(); + data.addAll(graphqlSchemas.stream().map(this::toInputStream).toList()); + data.addAll(additionalDefinitions.values().stream().map(String::getBytes).map(ByteArrayInputStream::new).toList()); + // now we have all of definitions List queryDefinitions = new ArrayList<>(); - for (String graphQLSchema : graphqlSchemas) { - TypeDefinitionRegistry newTypes = readDefintionRegistry(graphQLSchema); + for (InputStream graphQLSchema : data) { + TypeDefinitionRegistry newTypes = readDefinitionRegistry(graphQLSchema); // for allowing extension of the schema we need to merge this object manually // we remove it from the new Types and aggregate in temporal list so we can add this at the end @@ -103,22 +124,61 @@ private GraphQLSchemaManager() { SchemaGenerator schemaGenerator = new SchemaGenerator(); // we merge the query object typeDefinitionRegistry.add(ObjectTypeDefinition.newObjectTypeDefinition().name("Query").fieldDefinitions(queryDefinitions).build()); - graphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); + GraphQLSchema newGraphQLSchema = schemaGenerator.makeExecutableSchema(typeDefinitionRegistry, runtimeWiring); + GraphQL newGraphQL = GraphQL.newGraphQL(newGraphQLSchema).build(); + LOGGER.debug("Succesfuly rebuilding graphQL definitions"); + return new GraphQLSchemaBuild(newGraphQLSchema, newGraphQL, additionalDefinitions); } - private TypeDefinitionRegistry readDefintionRegistry(String graphQLSchema) { - try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(graphQLSchema)) { - SchemaParser schemaParser = new SchemaParser(); - return schemaParser.parse(is); + private InputStream toInputStream(String classpathFile) { + try (InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(classpathFile)) { + return new ByteArrayInputStream(is.readAllBytes()); } catch (IOException e) { LOGGER.error("could not find or process {}", graphQLSchema, e); - return new TypeDefinitionRegistry(); + return new ByteArrayInputStream(new byte[0]); } } + private TypeDefinitionRegistry readDefinitionRegistry(InputStream inputStream) { + SchemaParser schemaParser = new SchemaParser(); + return schemaParser.parse(inputStream); + } + + public GraphQL getGraphQL() { + return graphQL; + } + public GraphQLSchema getGraphQLSchema() { return graphQLSchema; } + public ExecutionResult execute(ExecutionInput executionInput) { + return graphQL.execute(executionInput); + } + + public String getGraphQLSchemaDefinition() { + SchemaPrinter printer = new SchemaPrinter(); + return printer.print(graphQL.getGraphQLSchema()); + } + + public void init(DataAuditContext dataAuditContext, Map additionalQueries) { + setGraphQLSchemaBuild(rebuildDefinitions(dataAuditContext, additionalQueries)); + } + + public GraphQLSchemaBuild devireNewDataAuditQuerySchema(DataAuditContext dataAuditContext, DataAuditQuery dataAuditQuery) { + String graphQLDefinition = dataAuditQuery.getGraphQLDefinition(); + TypeDefinitionRegistry registry = readDefinitionRegistry(new ByteArrayInputStream(graphQLDefinition.getBytes())); + LOGGER.debug("Registering data audit query {} with definition {}", dataAuditQuery.getIdentifier(), registry.getType("Query")); + Map additionalDefinitions = new HashMap<>(this.graphQLdefinitions); + additionalDefinitions.put(dataAuditQuery.getIdentifier(), dataAuditQuery.getGraphQLDefinition()); + return rebuildDefinitions(dataAuditContext, additionalDefinitions); + } + + public void setGraphQLSchemaBuild(GraphQLSchemaBuild build) { + this.graphQL = build.graphQL(); + this.graphQLSchema = build.graphQLSchema(); + this.graphQLdefinitions = build.additionalDefinitions(); + } + } diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/DataAuditStore.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/DataAuditStore.java index 46944cad86..0d7a105664 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/DataAuditStore.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/DataAuditStore.java @@ -18,7 +18,10 @@ */ package org.kie.kogito.app.audit.spi; +import java.util.List; + import org.kie.kogito.app.audit.api.DataAuditContext; +import org.kie.kogito.app.audit.api.DataAuditQuery; import org.kie.kogito.event.job.JobInstanceDataEvent; import org.kie.kogito.event.process.ProcessInstanceErrorDataEvent; import org.kie.kogito.event.process.ProcessInstanceNodeDataEvent; @@ -58,4 +61,8 @@ public interface DataAuditStore { void storeJobDataEvent(DataAuditContext context, JobInstanceDataEvent event); + void storeQuery(DataAuditContext context, DataAuditQuery dataAuditQuery); + + List findQueries(DataAuditContext context); + } diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQuery.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQuery.java index c004a250af..59c26e4739 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQuery.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQuery.java @@ -20,7 +20,7 @@ import graphql.schema.DataFetchingEnvironment; -public interface GraphQLSchemaQuery { +public interface GraphQLSchemaQuery { String name(); diff --git a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQueryProvider.java b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQueryProvider.java index 7f04dcd6c4..96e57eeef5 100644 --- a/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQueryProvider.java +++ b/data-audit/data-audit-common/src/main/java/org/kie/kogito/app/audit/spi/GraphQLSchemaQueryProvider.java @@ -20,12 +20,14 @@ import java.util.List; +import org.kie.kogito.app.audit.api.DataAuditContext; + public interface GraphQLSchemaQueryProvider { default String[] graphQLQueryExtension() { return new String[0]; } - List> queries(); + List queries(DataAuditContext dataAuditContext); } diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/DataAuditTestUtils.java b/data-audit/data-audit-common/src/test/java/org/kie/kogito/app/audit/quarkus/DataAuditTestUtils.java similarity index 100% rename from data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/DataAuditTestUtils.java rename to data-audit/data-audit-common/src/test/java/org/kie/kogito/app/audit/quarkus/DataAuditTestUtils.java diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/JPADataAuditStore.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/JPADataAuditStore.java index 8d51e98583..0793672cf4 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/JPADataAuditStore.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/JPADataAuditStore.java @@ -24,12 +24,16 @@ import java.time.Instant; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.kie.kogito.app.audit.api.DataAuditContext; +import org.kie.kogito.app.audit.api.DataAuditQuery; import org.kie.kogito.app.audit.jpa.model.AbstractProcessInstanceLog; import org.kie.kogito.app.audit.jpa.model.AbstractUserTaskInstanceLog; +import org.kie.kogito.app.audit.jpa.model.AuditQuery; import org.kie.kogito.app.audit.jpa.model.JobExecutionLog; import org.kie.kogito.app.audit.jpa.model.ProcessInstanceErrorLog; import org.kie.kogito.app.audit.jpa.model.ProcessInstanceNodeLog; @@ -397,4 +401,28 @@ private String toJsonString(Object data) { } } + @Override + public void storeQuery(DataAuditContext context, DataAuditQuery dataAuditQuery) { + EntityManager entityManager = context.getContext(); + AuditQuery auditQuery = new AuditQuery(); + auditQuery.setIdentifier(dataAuditQuery.getIdentifier()); + auditQuery.setGraphQLDefinition(dataAuditQuery.getGraphQLDefinition()); + auditQuery.setQuery(dataAuditQuery.getQuery()); + entityManager.merge(auditQuery); + } + + @Override + public List findQueries(DataAuditContext context) { + EntityManager entityManager = context.getContext(); + List queries = entityManager.createQuery("SELECT o FROM AuditQuery o", AuditQuery.class).getResultList(); + return queries.stream().map(this::to).collect(Collectors.toList()); + } + + private DataAuditQuery to(AuditQuery auditQuery) { + DataAuditQuery dataAuditQuery = new DataAuditQuery(); + dataAuditQuery.setIdentifier(auditQuery.getIdentifier()); + dataAuditQuery.setGraphQLDefinition(auditQuery.getGraphQLDefinition()); + dataAuditQuery.setQuery(auditQuery.getQuery()); + return dataAuditQuery; + } } diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/model/AuditQuery.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/model/AuditQuery.java new file mode 100644 index 0000000000..dca40d27a1 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/model/AuditQuery.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.jpa.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; + +@Entity +@Table(name = "Audit_Query") +public class AuditQuery { + + @Id + @Column(name = "identifier", nullable = false) + private String identifier; + + @Column(name = "graph_ql_definition", nullable = false, length = 5000) + private String graphQLDefinition; + + @Column(name = "query", nullable = false, length = 5000) + private String query; + + public String getIdentifier() { + return identifier; + } + + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getGraphQLDefinition() { + return graphQLDefinition; + } + + public void setGraphQLDefinition(String graphQLDefinition) { + this.graphQLDefinition = graphQLDefinition; + } + + public String getQuery() { + return query; + } + + public void setQuery(String query) { + this.query = query; + } + +} diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAAbstractQuery.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAAbstractQuery.java index b10d95ae55..e6a744ff29 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAAbstractQuery.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAAbstractQuery.java @@ -23,15 +23,20 @@ import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import jakarta.persistence.EntityManager; import jakarta.persistence.Query; public abstract class JPAAbstractQuery { + private static final Logger LOGGER = LoggerFactory.getLogger(JPAAbstractQuery.class); protected List executeWithNamedQueryEntityManager(EntityManager entityManager, String queryName, Class clazz) { return executeWithNamedQueryEntityManager("META-INF/data-audit-orm.xml", entityManager, queryName); } + @SuppressWarnings("unchecked") protected List executeWithNamedQueryEntityManager(String file, EntityManager entityManager, String queryName) { String query = MappingFile.findInFile(file, entityManager, queryName); return entityManager.createNativeQuery(query).getResultList(); @@ -47,10 +52,19 @@ protected List executeWithNamedQueryEntityManagerAndArguments(EntityManag protected List executeWithNamedQueryEntityManagerAndArguments(String file, EntityManager entityManager, String queryName, Map arguments) { String query = MappingFile.findInFile(file, entityManager, queryName); + return executeWithQueryEntityManagerAndArguments(entityManager, query, arguments); + } - Map parameters = new HashMap<>(arguments); + protected List executeWithQueryEntityManagerAndArguments(EntityManager entityManager, String query) { + return executeWithQueryEntityManagerAndArguments(entityManager, query, Collections.emptyMap()); + } + + @SuppressWarnings("unchecked") + protected List executeWithQueryEntityManagerAndArguments(EntityManager entityManager, String query, Map arguments) { + LOGGER.debug("About to execute native query {} with arguments {}", query, arguments); Query jpaQuery = entityManager.createNativeQuery(query); - @SuppressWarnings("unchecked") + + Map parameters = new HashMap<>(arguments); Map pagination = (Map) parameters.remove("pagination"); parameters.forEach(jpaQuery::setParameter); if (pagination != null) { @@ -69,6 +83,5 @@ protected List executeWithNamedQueryEntityManagerAndArguments(String file } return jpaQuery.getResultList(); - } } diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAComplexNamedQuery.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAComplexNamedQuery.java index 4efdc9629c..aa0eb23d99 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAComplexNamedQuery.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAComplexNamedQuery.java @@ -27,7 +27,7 @@ import graphql.schema.DataFetchingEnvironment; import jakarta.persistence.EntityManager; -public class JPAComplexNamedQuery extends JPAAbstractQuery implements GraphQLSchemaQuery> { +public class JPAComplexNamedQuery extends JPAAbstractQuery implements GraphQLSchemaQuery { private String name; private String namedQuery; diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPADynamicQuery.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPADynamicQuery.java new file mode 100644 index 0000000000..f9edd34111 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPADynamicQuery.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.jpa.queries; + +import java.sql.Timestamp; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.kie.kogito.app.audit.api.DataAuditContext; +import org.kie.kogito.app.audit.spi.GraphQLSchemaQuery; + +import graphql.schema.DataFetchingEnvironment; +import graphql.schema.GraphQLFieldDefinition; +import graphql.schema.GraphQLList; +import graphql.schema.GraphQLObjectType; +import graphql.schema.GraphQLOutputType; +import graphql.schema.GraphQLScalarType; +import graphql.schema.GraphQLSchemaElement; +import jakarta.persistence.EntityManager; + +public class JPADynamicQuery extends JPAAbstractQuery implements GraphQLSchemaQuery { + + private String name; + private String query; + + public JPADynamicQuery(String name, String query) { + this.name = name; + this.query = query; + } + + @Override + public String name() { + return name; + } + + @Override + public Object fetch(DataFetchingEnvironment dataFetchingEnvironment) { + Map arguments = dataFetchingEnvironment.getArguments(); + DataAuditContext context = dataFetchingEnvironment.getLocalContext(); + EntityManager entityManager = context.getContext(); + + GraphQLSchemaElement element = dataFetchingEnvironment.getFieldType(); + if (arguments.isEmpty()) { + return toJson(element, executeWithQueryEntityManagerAndArguments(entityManager, query)); + } else { + return toJson(element, executeWithQueryEntityManagerAndArguments(entityManager, query, arguments)); + } + } + + private Object toJson(GraphQLSchemaElement element, List data) { + if (data.isEmpty()) { + return Collections.emptyList(); + } + + if (element instanceof GraphQLList) { + GraphQLSchemaElement child = element.getChildren().get(0); + List transformedData = new ArrayList<>(); + for (Object row : data) { + transformedData.add(toJsonObject(child, row)); + } + return transformedData; + } else if (element instanceof GraphQLSchemaElement) { + return toJsonObject(element, data); + } + return null; + } + + private Object toJsonObject(GraphQLSchemaElement element, Object data) { + if (element instanceof GraphQLScalarType) { + return transform((GraphQLScalarType) element, data); + } else if (element instanceof GraphQLObjectType) { + return toComplexObject((GraphQLObjectType) element, (Object[]) data); + } + return null; + } + + private Object toComplexObject(GraphQLObjectType type, Object[] data) { + Map newPojo = new HashMap<>(); + for (int i = 0; i < data.length; i++) { + GraphQLFieldDefinition definition = type.getFieldDefinitions().get(i); + newPojo.put(definition.getName(), transform(definition.getType(), data[i])); + } + return newPojo; + } + + private Object transform(GraphQLOutputType outputType, Object source) { + if (source == null) { + return null; + } + Object target = source; + if (outputType instanceof GraphQLScalarType) { + GraphQLScalarType scalarType = (GraphQLScalarType) outputType; + if ("DateTime".equals(scalarType.getName())) { + target = OffsetDateTime.ofInstant(((Timestamp) source).toInstant(), ZoneId.of("UTC")); + } + + } + return target; + } + +} diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaDynamicQueryProvider.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaDynamicQueryProvider.java new file mode 100644 index 0000000000..cd4c307d75 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaDynamicQueryProvider.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.jpa.queries; + +import java.util.ArrayList; +import java.util.List; + +import org.kie.kogito.app.audit.api.DataAuditContext; +import org.kie.kogito.app.audit.jpa.model.AuditQuery; +import org.kie.kogito.app.audit.spi.GraphQLSchemaQuery; +import org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider; + +import jakarta.persistence.EntityManager; + +public class JPAGraphQLSchemaDynamicQueryProvider implements GraphQLSchemaQueryProvider { + + @Override + public List queries(DataAuditContext dataAuditContext) { + EntityManager entityManager = dataAuditContext.getContext(); + List queriesRegistered = entityManager.createQuery("SELECT o FROM AuditQuery o", AuditQuery.class).getResultList(); + List queries = new ArrayList<>(); + for (AuditQuery auditQuery : queriesRegistered) { + queries.add(new JPADynamicQuery(auditQuery.getIdentifier(), auditQuery.getQuery())); + } + return queries; + + } +} diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaJobsQueryProvider.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaJobsQueryProvider.java index d01ab33acd..60b5e1a36b 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaJobsQueryProvider.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaJobsQueryProvider.java @@ -20,6 +20,7 @@ import java.util.List; +import org.kie.kogito.app.audit.api.DataAuditContext; import org.kie.kogito.app.audit.graphql.type.JobExecutionTO; import org.kie.kogito.app.audit.spi.GraphQLSchemaQuery; import org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider; @@ -27,7 +28,7 @@ public class JPAGraphQLSchemaJobsQueryProvider implements GraphQLSchemaQueryProvider { @Override - public List> queries() { + public List queries(DataAuditContext dataAuditContext) { return List.of( new JPASimpleNamedQuery("GetAllScheduledJobs", JobExecutionTO.class), new JPASimpleNamedQuery("GetJobById", JobExecutionTO.class), diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaProcessInstancesQueryProvider.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaProcessInstancesQueryProvider.java index 3dd41f105f..3e7c13100a 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaProcessInstancesQueryProvider.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaProcessInstancesQueryProvider.java @@ -20,6 +20,7 @@ import java.util.List; +import org.kie.kogito.app.audit.api.DataAuditContext; import org.kie.kogito.app.audit.graphql.type.ProcessInstanceErrorTO; import org.kie.kogito.app.audit.graphql.type.ProcessInstanceNodeTO; import org.kie.kogito.app.audit.graphql.type.ProcessInstanceStateTO; @@ -33,8 +34,8 @@ public class JPAGraphQLSchemaProcessInstancesQueryProvider implements GraphQLSchemaQueryProvider { @Override - public List> queries() { - return List.> of( + public List queries(DataAuditContext dataAuditContext) { + return List. of( new JPAComplexNamedQuery("GetAllProcessInstancesState", new ProcessInstanceStateTOMapper()), new JPAComplexNamedQuery("GetAllProcessInstancesStateByStatus", new ProcessInstanceStateTOMapper()), new JPAComplexNamedQuery("GetAllProcessInstancesStateByProcessId", new ProcessInstanceStateTOMapper()), diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaUserTaskInstancesQueryProvider.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaUserTaskInstancesQueryProvider.java index 9fa5a6f114..8d2fe8e080 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaUserTaskInstancesQueryProvider.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPAGraphQLSchemaUserTaskInstancesQueryProvider.java @@ -23,6 +23,7 @@ import java.util.Date; import java.util.List; +import org.kie.kogito.app.audit.api.DataAuditContext; import org.kie.kogito.app.audit.graphql.type.UserTaskInstanceAssignmentTO; import org.kie.kogito.app.audit.graphql.type.UserTaskInstanceAttachmentTO; import org.kie.kogito.app.audit.graphql.type.UserTaskInstanceCommentTO; @@ -37,7 +38,7 @@ public class JPAGraphQLSchemaUserTaskInstancesQueryProvider implements GraphQLSchemaQueryProvider { @Override - public List> queries() { + public List queries(DataAuditContext dataAuditContext) { return List.of( new JPASimpleNamedQuery("GetAllUserTaskInstanceState", UserTaskInstanceStateTO.class), new JPASimpleNamedQuery("GetAllUserTaskInstanceAttachments", UserTaskInstanceAttachmentTO.class), diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPASimpleNamedQuery.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPASimpleNamedQuery.java index 0624fda4de..114f16dfa9 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPASimpleNamedQuery.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/java/org/kie/kogito/app/audit/jpa/queries/JPASimpleNamedQuery.java @@ -28,7 +28,7 @@ import graphql.schema.DataFetchingEnvironment; import jakarta.persistence.EntityManager; -public class JPASimpleNamedQuery extends JPAAbstractQuery implements GraphQLSchemaQuery> { +public class JPASimpleNamedQuery extends JPAAbstractQuery implements GraphQLSchemaQuery { private String name; private String namedQuery; diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/META-INF/services/org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/META-INF/services/org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider index ac66f469b8..e9456694aa 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/META-INF/services/org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/META-INF/services/org.kie.kogito.app.audit.spi.GraphQLSchemaQueryProvider @@ -1,3 +1,4 @@ org.kie.kogito.app.audit.jpa.queries.JPAGraphQLSchemaJobsQueryProvider org.kie.kogito.app.audit.jpa.queries.JPAGraphQLSchemaProcessInstancesQueryProvider -org.kie.kogito.app.audit.jpa.queries.JPAGraphQLSchemaUserTaskInstancesQueryProvider \ No newline at end of file +org.kie.kogito.app.audit.jpa.queries.JPAGraphQLSchemaUserTaskInstancesQueryProvider +org.kie.kogito.app.audit.jpa.queries.JPAGraphQLSchemaDynamicQueryProvider diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/h2/V1.1.0__Add Query audit dynamic registering.sql b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/h2/V1.1.0__Add Query audit dynamic registering.sql new file mode 100644 index 0000000000..2c5e2da7e4 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/h2/V1.1.0__Add Query audit dynamic registering.sql @@ -0,0 +1,2 @@ +create table Audit_Query (identifier varchar(255) not null, graph_ql_definition varchar(5000), query varchar(5000), primary key (identifier)); + diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/postgresql/V1.1.0__Add Query audit dynamic registering.sql b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/postgresql/V1.1.0__Add Query audit dynamic registering.sql new file mode 100644 index 0000000000..2c5e2da7e4 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-common/src/main/resources/db/data-audit/postgresql/V1.1.0__Add Query audit dynamic registering.sql @@ -0,0 +1,2 @@ +create table Audit_Query (identifier varchar(255) not null, graph_ql_definition varchar(5000), query varchar(5000), primary key (identifier)); + diff --git a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditContextFactory.java b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditContextFactory.java index 40e35c1a18..6ecca9097b 100644 --- a/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditContextFactory.java +++ b/data-audit/kogito-addons-data-audit-jpa/kogito-addons-data-audit-jpa-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditContextFactory.java @@ -24,8 +24,10 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; +import jakarta.transaction.Transactional; @ApplicationScoped +@Transactional public class QuarkusJPADataAuditContextFactory implements DataAuditContextFactory { @PersistenceContext diff --git a/data-audit/kogito-addons-data-audit-quarkus/pom.xml b/data-audit/kogito-addons-data-audit-quarkus/pom.xml index 38304e58c0..aa46eb153d 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/pom.xml +++ b/data-audit/kogito-addons-data-audit-quarkus/pom.xml @@ -62,6 +62,11 @@ + + org.kie.kogito + data-audit-common + tests + org.hamcrest hamcrest-all diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLDataAuditRouter.java b/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLDataAuditRouter.java new file mode 100644 index 0000000000..13f84ef598 --- /dev/null +++ b/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLDataAuditRouter.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.quarkus; + +import java.util.Map; +import java.util.stream.Collectors; + +import org.kie.kogito.app.audit.api.DataAuditQuery; +import org.kie.kogito.app.audit.api.DataAuditQueryService; +import org.kie.kogito.app.audit.api.DataAuditStoreProxyService; +import org.kie.kogito.app.audit.graphql.GraphQLSchemaBuild; +import org.kie.kogito.app.audit.spi.DataAuditContextFactory; + +import io.quarkus.vertx.web.Route; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.graphql.ExecutionInputBuilderWithContext; +import io.vertx.ext.web.handler.graphql.GraphQLHandler; +import io.vertx.ext.web.handler.graphql.GraphQLHandlerOptions; + +import graphql.GraphQL; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Status; +import jakarta.transaction.Synchronization; +import jakarta.transaction.TransactionSynchronizationRegistry; +import jakarta.transaction.Transactional; + +import static io.quarkus.vertx.web.Route.HttpMethod.GET; +import static io.quarkus.vertx.web.Route.HttpMethod.POST; +import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_QUERY_PATH; +import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_REGISTRY_PATH; +import static org.kie.kogito.app.audit.graphql.GraphQLSchemaManager.graphQLSchemaManagerInstance; + +@ApplicationScoped +@Transactional +public class GraphQLDataAuditRouter { + + GraphQL graphQL; + + GraphQLHandler graphQLHandler; + + @Inject + DataAuditContextFactory dataAuditContextFactory; + + private DataAuditQueryService dataAuditQueryService; + + private DataAuditStoreProxyService dataAuditStoreProxyService; + + @Inject + TransactionSynchronizationRegistry registry; + + @PostConstruct + public void init() { + dataAuditStoreProxyService = DataAuditStoreProxyService.newAuditStoreService(); + dataAuditQueryService = DataAuditQueryService.newAuditQuerySerice(); + + Map queries = + dataAuditStoreProxyService.findQueries(dataAuditContextFactory.newDataAuditContext()).stream() + .collect(Collectors.toMap(DataAuditQuery::getIdentifier, DataAuditQuery::getGraphQLDefinition)); + graphQLSchemaManagerInstance().init(dataAuditContextFactory.newDataAuditContext(), queries); + graphQLHandler = GraphQLHandler.create(dataAuditQueryService.getGraphQL(), new GraphQLHandlerOptions()); + } + + @Route(path = DATA_AUDIT_QUERY_PATH, type = Route.HandlerType.BLOCKING, order = 2, methods = { POST }) + public void blockingGraphQLHandlerPost(RoutingContext rc) { + graphQLHandler.beforeExecute(this::beforeExecuteHTTP).handle(rc); + } + + @Route(path = DATA_AUDIT_REGISTRY_PATH, type = Route.HandlerType.BLOCKING, order = 2, methods = { POST }) + public void blockingRegistryHandlerPost(RoutingContext rc) { + try { + JsonObject jsonObject = rc.body().asJsonObject(); + DataAuditQuery dataAuditQuery = new DataAuditQuery(); + dataAuditQuery.setIdentifier(jsonObject.getString("identifier")); + dataAuditQuery.setGraphQLDefinition(jsonObject.getString("graphQLDefinition")); + dataAuditQuery.setQuery(jsonObject.getString("query")); + dataAuditStoreProxyService.storeQuery(dataAuditContextFactory.newDataAuditContext(), dataAuditQuery); + GraphQLSchemaBuild build = graphQLSchemaManagerInstance().devireNewDataAuditQuerySchema(dataAuditContextFactory.newDataAuditContext(), dataAuditQuery); + registry.registerInterposedSynchronization(new Synchronization() { + + @Override + public void beforeCompletion() { + // do nothing + } + + @Override + public void afterCompletion(int status) { + if (status != Status.STATUS_COMMITTED) { + return; + } + graphQLSchemaManagerInstance().setGraphQLSchemaBuild(build); + graphQLHandler = GraphQLHandler.create(dataAuditQueryService.getGraphQL(), new GraphQLHandlerOptions()); + } + }); + + rc.response().setStatusCode(200).end(); + } catch (Exception e) { + rc.response().setStatusCode(400).end(e.getLocalizedMessage()); + throw e; + } + + } + + @Route(path = DATA_AUDIT_REGISTRY_PATH, type = Route.HandlerType.BLOCKING, order = 2, methods = { GET }) + public void blockingRegistryHandlerGet(RoutingContext rc) { + rc.response().setStatusCode(200).end(graphQLSchemaManagerInstance().getGraphQLSchemaDefinition()); + } + + private void beforeExecuteHTTP(ExecutionInputBuilderWithContext config) { + config.builder().localContext(dataAuditContextFactory.newDataAuditContext()); + } + +} diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLJPADataAuditRouter.java b/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLJPADataAuditRouter.java deleted file mode 100644 index 28df810396..0000000000 --- a/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/GraphQLJPADataAuditRouter.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.app.audit.quarkus; - -import org.kie.kogito.app.audit.api.DataAuditQueryService; -import org.kie.kogito.app.audit.spi.DataAuditContextFactory; - -import io.quarkus.vertx.web.Route; -import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.graphql.ExecutionInputBuilderWithContext; -import io.vertx.ext.web.handler.graphql.GraphQLHandler; -import io.vertx.ext.web.handler.graphql.GraphQLHandlerOptions; - -import graphql.GraphQL; -import jakarta.annotation.PostConstruct; -import jakarta.enterprise.context.ApplicationScoped; -import jakarta.inject.Inject; - -import static io.quarkus.vertx.web.Route.HttpMethod.GET; -import static io.quarkus.vertx.web.Route.HttpMethod.POST; -import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_PATH; - -@ApplicationScoped -public class GraphQLJPADataAuditRouter { - - GraphQL graphQL; - - GraphQLHandler graphQLHandler; - - @Inject - DataAuditContextFactory dataAuditContextFactory; - - @PostConstruct - public void init() { - graphQL = GraphQL.newGraphQL(DataAuditQueryService.newAuditQuerySerice().getGraphQLSchema()).build(); - graphQLHandler = GraphQLHandler.create(graphQL, new GraphQLHandlerOptions()); - } - - @Route(path = DATA_AUDIT_PATH, type = Route.HandlerType.BLOCKING, order = 2, methods = { GET }) - public void blockingGraphQLHandlerGet(RoutingContext rc) { - graphQLHandler.beforeExecute(this::beforeExecuteHTTP).handle(rc); - } - - @Route(path = DATA_AUDIT_PATH, type = Route.HandlerType.BLOCKING, order = 2, methods = { POST }) - public void blockingGraphQLHandlerPost(RoutingContext rc) { - graphQLHandler.beforeExecute(this::beforeExecuteHTTP).handle(rc); - } - - private void beforeExecuteHTTP(ExecutionInputBuilderWithContext config) { - config.builder().localContext(dataAuditContextFactory.newDataAuditContext()); - } - -} diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditEventPublisher.java b/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusDataAuditEventPublisher.java similarity index 94% rename from data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditEventPublisher.java rename to data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusDataAuditEventPublisher.java index 21261a5a1d..cdd9bdcca3 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusJPADataAuditEventPublisher.java +++ b/data-audit/kogito-addons-data-audit-quarkus/src/main/java/org/kie/kogito/app/audit/quarkus/QuarkusDataAuditEventPublisher.java @@ -36,16 +36,16 @@ import jakarta.transaction.Transactional.TxType; @ApplicationScoped -public class QuarkusJPADataAuditEventPublisher implements EventPublisher { +public class QuarkusDataAuditEventPublisher implements EventPublisher { - private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusJPADataAuditEventPublisher.class); + private static final Logger LOGGER = LoggerFactory.getLogger(QuarkusDataAuditEventPublisher.class); private DataAuditStoreProxyService proxy; @Inject DataAuditContextFactory dataAuditContextFactory; - public QuarkusJPADataAuditEventPublisher() { + public QuarkusDataAuditEventPublisher() { proxy = DataAuditStoreProxyService.newAuditStoreService(); } diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditJobServiceTest.java b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditJobServiceTest.java index 152306e74d..81926a90cf 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditJobServiceTest.java +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditJobServiceTest.java @@ -93,7 +93,7 @@ public void testGetAllScheduledJobs() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .log() .body() @@ -118,7 +118,7 @@ public void testGetJobById() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .log() .body() @@ -140,7 +140,7 @@ public void testGetJobHistoryById() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .log() .body() @@ -166,7 +166,7 @@ public void testGetJobHistoryByProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -189,7 +189,7 @@ public void testGetAllPendingJobs() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -208,7 +208,7 @@ public void testGetAllEligibleJobsForExecution() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .log() .body() @@ -230,7 +230,7 @@ public void testGetAllEligibleJobsForRetry() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -248,7 +248,7 @@ public void testGetAllJobs() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -269,7 +269,7 @@ public void testGetAllCompletedJobs() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -292,7 +292,7 @@ public void testGetAllInErrorJobs() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -316,7 +316,7 @@ public void testGetAllJobsByStatus() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -339,7 +339,7 @@ public void testGetJobByProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditProcessInstanceServiceTest.java b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditProcessInstanceServiceTest.java index 83f9954039..801926b791 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditProcessInstanceServiceTest.java +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditProcessInstanceServiceTest.java @@ -119,7 +119,7 @@ public void testGetAllProcessInstancesState() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -145,7 +145,7 @@ public void testGetProcessInstancesStateHistory() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -170,7 +170,7 @@ public void testGetProcessInstancesStateHistoryByBusinessKey() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -198,7 +198,7 @@ public void testGetAllProcessInstancesStateByStatus() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -225,7 +225,7 @@ public void testGetAllProcessInstancesStateByProcessId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -250,7 +250,7 @@ public void testGetAllProcessInstancesNodeByProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -276,7 +276,7 @@ public void testGetAllProcessInstancesErrorByProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -302,7 +302,7 @@ public void testGetAllProcessInstancesVariablebyProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -327,7 +327,7 @@ public void testGetAllProcessInstancesVariableHistoryByProcessInstanceId() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditQueryRegistryServiceTest.java b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditQueryRegistryServiceTest.java new file mode 100644 index 0000000000..024436107d --- /dev/null +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditQueryRegistryServiceTest.java @@ -0,0 +1,340 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.quarkus; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.kie.kogito.app.audit.api.DataAuditStoreProxyService; +import org.kie.kogito.app.audit.api.SubsystemConstants; +import org.kie.kogito.app.audit.spi.DataAuditContextFactory; +import org.kie.kogito.event.EventPublisher; +import org.kie.kogito.event.job.JobInstanceDataEvent; +import org.kie.kogito.event.process.ProcessInstanceStateDataEvent; +import org.kie.kogito.event.process.ProcessInstanceStateEventBody; +import org.kie.kogito.jobs.service.model.JobStatus; +import org.kie.kogito.process.ProcessInstance; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; + +import jakarta.inject.Inject; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.deriveNewState; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.newJobEvent; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.newProcessInstanceStateEvent; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.wrapQuery; + +@QuarkusTest +@TestInstance(Lifecycle.PER_CLASS) +public class QuarkusAuditQueryRegistryServiceTest { + + @Inject + EventPublisher publisher; + + @Inject + DataAuditContextFactory contextFactory; + + @BeforeAll + public void init() throws Exception { + + ProcessInstanceStateDataEvent processInstanceEvent = newProcessInstanceStateEvent("processId1", "1", ProcessInstance.STATE_ACTIVE, "rootI1", "rootP1", "parent1", "identity", + ProcessInstanceStateEventBody.EVENT_TYPE_STARTED); + publisher.publish(processInstanceEvent); + + JobInstanceDataEvent jobEvent; + jobEvent = newJobEvent("job1", "nodeInstanceId1", 1, "processId1", "processInstanceId1", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.EXECUTED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job2", "nodeInstanceId1", 1, "processId1", "processInstanceId2", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job3", "nodeInstanceId1", 1, "processId1", "processInstanceId3", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.CANCELED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job4", "nodeInstanceId1", 1, "processId1", "processInstanceId4", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.RETRY); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 2, JobStatus.EXECUTED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job5", "nodeInstanceId1", 1, "processId1", "processInstanceI51", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.ERROR); + publisher.publish(jobEvent); + } + + @Test + public void testRegisterQueryComplexType() { + + String body = "{" + + "\"identifier\" : \"testsComplex\", " + + "\"graphQLDefinition\" : \"type EventTest { jobId : String, processInstanceId: String} type Query { testsComplex (pagination: Pagination) : [ EventTest ] } \"," + + "\"query\" : \" SELECT o.job_id, o.process_instance_id FROM job_execution_log o \"" + + "}"; + + given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ testsComplex { jobId, processInstanceId } }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.testsComplex"); + + assertThat(response) + .hasSize(10); + + } + + @Test + public void testRegisterQuerySimpleType() { + + String body = "{" + + "\"identifier\" : \"testSimple\", " + + "\"graphQLDefinition\" : \"type Query { testSimple (pagination: Pagination) : [ String ] } \"," + + "\"query\" : \" SELECT o.job_id FROM job_execution_log o \"" + + "}"; + + given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ testSimple }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.testSimple"); + + assertThat(response) + .hasSize(10); + + } + + @Test + public void testRegisterQueryWithDateTime() { + + String body = "{\n" + + " \"identifier\": \"GetWithTime\",\n" + + " \"graphQLDefinition\": \"type GetWithTimeType {eventId: String, processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetWithTime(pagination:Pagination) : [GetWithTimeType]}\",\n" + + " \"query\": \"SELECT log.event_id as eventId, log.process_instance_id as processInstanceId, log.process_id as processId, log.state as state, log.event_type as eventType, log.event_date as eventDate FROM Process_Instance_State_Log log group by log.event_id, log.event_type, log.event_date, log.process_id, log.process_instance_id, log.state order by processInstanceId, eventDate\" }"; + + given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ GetWithTime {eventId, eventDate, processInstanceId, state} }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.GetWithTime"); + + assertThat(response) + .extracting("eventDate") + .allSatisfy(OffsetDateTime.class::isInstance); + } + + @Test + public void testQuerySQLRegistrationFailure() { + + String q = " ".repeat(6000); + + String body = "{\n" + + " \"identifier\": \"ErrorSQLQuery\",\n" + + " \"graphQLDefinition\": \"type ErrorSQLQuery {eventId: String, processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetAllStates(pagination:Pagination) : [AllStates]}\",\n" + + " \"query\": \"" + q + "\" }"; + + String graphQLBefore = given() + .contentType(ContentType.JSON) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(400); + + String graphQLAfter = given() + .contentType(ContentType.JSON) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + assertEquals(graphQLAfter, graphQLBefore); + Assertions.assertThat(graphQLAfter).doesNotContain("ErrorSQLQuery"); + + Assertions.assertThat(DataAuditStoreProxyService.newAuditStoreService().findQueries(contextFactory.newDataAuditContext())) + .extracting(e -> e.getIdentifier()) + .doesNotContain("ErrorSQLQuery"); + + } + + @Test + public void testQueryGraphQLRegistrationFailure() { + + String q = " ".repeat(1000); + + String body = "{\n" + + " \"identifier\": \"ErrorGraphQLQuery\",\n" + + " \"graphQLDefinition\": \"type ErrorGraphQLQuery {eventId: processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetAllStates(pagination:Pagination) : [AllStates]}\",\n" + + " \"query\": \"" + q + "\" }"; + + String graphQLBefore = given() + .contentType(ContentType.JSON) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + given() + .contentType(ContentType.JSON) + .body(body) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(400); + + String graphQLAfter = given() + .contentType(ContentType.JSON) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + assertEquals(graphQLAfter, graphQLBefore); + Assertions.assertThat(graphQLAfter).doesNotContain("ErrorGraphQLQuery"); + + Assertions.assertThat(DataAuditStoreProxyService.newAuditStoreService().findQueries(contextFactory.newDataAuditContext())) + .extracting(e -> e.getIdentifier()) + .doesNotContain("ErrorGraphQLQuery"); + } + + @Test + public void testPrintSchema() { + given() + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .body(Matchers.notNullValue()); + } +} diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditUserTaskInstanceServiceTest.java b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditUserTaskInstanceServiceTest.java index f1c7a52ab1..bf1c11f243 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditUserTaskInstanceServiceTest.java +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusAuditUserTaskInstanceServiceTest.java @@ -138,7 +138,7 @@ public void testGetAllUserTaskInstanceState() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -159,7 +159,7 @@ public void testGetAllUserTaskInstanceAssignments() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -179,7 +179,7 @@ public void testGetAllUserTaskInstanceAttachments() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -199,7 +199,7 @@ public void testGetAllUserTaskInstanceComment() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -219,7 +219,7 @@ public void testGetAllUserTaskInstanceDeadline() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) @@ -239,7 +239,7 @@ public void testGetAllUserTaskInstanceVariable() { .contentType(ContentType.JSON) .body(query) .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusEmbeddedJPADataAuditTest.java b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusEmbeddedJPADataAuditTest.java index 6edbcb0fb9..eb5bfcb711 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusEmbeddedJPADataAuditTest.java +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/java/org/kie/kogito/app/audit/quarkus/QuarkusEmbeddedJPADataAuditTest.java @@ -86,7 +86,7 @@ public void testQuarkusEventPublisher() { .contentType(ContentType.JSON) .body("{\"query\": \"{ GetAllProcessInstancesState { eventId, eventDate, processType, processId, processVersion, parentProcessInstanceId, rootProcessId, rootProcessInstanceId, processInstanceId, businessKey, eventType, outcome, state, slaDueDate } }\"}") .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) diff --git a/data-audit/kogito-addons-data-audit-quarkus/src/test/resources/application.properties b/data-audit/kogito-addons-data-audit-quarkus/src/test/resources/application.properties index 21c5f97026..24a1f9d8fb 100644 --- a/data-audit/kogito-addons-data-audit-quarkus/src/test/resources/application.properties +++ b/data-audit/kogito-addons-data-audit-quarkus/src/test/resources/application.properties @@ -7,4 +7,3 @@ quarkus.hibernate-orm.database.generation=create-drop - diff --git a/data-audit/kogito-addons-data-audit-springboot/pom.xml b/data-audit/kogito-addons-data-audit-springboot/pom.xml index c23ec627ca..cb1258b40b 100644 --- a/data-audit/kogito-addons-data-audit-springboot/pom.xml +++ b/data-audit/kogito-addons-data-audit-springboot/pom.xml @@ -45,6 +45,11 @@ + + org.kie.kogito + data-audit-common + tests + org.springframework.boot spring-boot-starter-test diff --git a/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLAuditDataRouteMapping.java b/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLAuditDataRouteMapping.java new file mode 100644 index 0000000000..82e5e2f8bc --- /dev/null +++ b/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLAuditDataRouteMapping.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.springboot; + +import java.util.Map; +import java.util.stream.Collectors; + +import org.kie.kogito.app.audit.api.DataAuditQuery; +import org.kie.kogito.app.audit.api.DataAuditQueryService; +import org.kie.kogito.app.audit.api.DataAuditStoreProxyService; +import org.kie.kogito.app.audit.graphql.GraphQLSchemaBuild; +import org.kie.kogito.app.audit.spi.DataAuditContextFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.fasterxml.jackson.databind.JsonNode; + +import graphql.ExecutionResult; +import jakarta.annotation.PostConstruct; + +import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_QUERY_PATH; +import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_REGISTRY_PATH; +import static org.kie.kogito.app.audit.graphql.GraphQLSchemaManager.graphQLSchemaManagerInstance; + +@RestController +@Transactional +public class GraphQLAuditDataRouteMapping { + + private DataAuditQueryService dataAuditQueryService; + + @Autowired + DataAuditContextFactory dataAuditContextFactory; + + private DataAuditStoreProxyService dataAuditStoreProxyService; + + @PostConstruct + public void init() { + dataAuditQueryService = DataAuditQueryService.newAuditQuerySerice(); + dataAuditStoreProxyService = DataAuditStoreProxyService.newAuditStoreService(); + + Map queries = + dataAuditStoreProxyService.findQueries(dataAuditContextFactory.newDataAuditContext()).stream() + .collect(Collectors.toMap(DataAuditQuery::getIdentifier, DataAuditQuery::getGraphQLDefinition)); + graphQLSchemaManagerInstance().init(dataAuditContextFactory.newDataAuditContext(), queries); + } + + @PostMapping(value = DATA_AUDIT_QUERY_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public Map executeQuery(@RequestBody JsonNode query) { + ExecutionResult executionResult = dataAuditQueryService.executeQuery(dataAuditContextFactory.newDataAuditContext(), query.get("query").asText()); + return executionResult.toSpecification(); + } + + @PostMapping(value = DATA_AUDIT_REGISTRY_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public void registerQuery(@RequestBody DataAuditQuery dataAuditQuery) { + dataAuditStoreProxyService.storeQuery(dataAuditContextFactory.newDataAuditContext(), dataAuditQuery); + GraphQLSchemaBuild build = graphQLSchemaManagerInstance().devireNewDataAuditQuerySchema(dataAuditContextFactory.newDataAuditContext(), dataAuditQuery); + + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + @Override + public void afterCommit() { + graphQLSchemaManagerInstance().setGraphQLSchemaBuild(build); + } + }); + } + + @GetMapping(path = DATA_AUDIT_REGISTRY_PATH) + public String blockingRegistryHandlerGet() { + return graphQLSchemaManagerInstance().getGraphQLSchemaDefinition(); + } + + @ExceptionHandler({ Throwable.class }) + public ResponseEntity handleException(Throwable th) { + return ResponseEntity.badRequest().body(th.getLocalizedMessage()); + } +} diff --git a/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLJPAAuditDataRouteMapping.java b/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLJPAAuditDataRouteMapping.java deleted file mode 100644 index 4d99ceff1a..0000000000 --- a/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/GraphQLJPAAuditDataRouteMapping.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.kie.kogito.app.audit.springboot; - -import java.util.Map; - -import org.kie.kogito.app.audit.api.DataAuditQueryService; -import org.kie.kogito.app.audit.spi.DataAuditContextFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.MediaType; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import com.fasterxml.jackson.databind.JsonNode; - -import graphql.ExecutionResult; -import jakarta.annotation.PostConstruct; - -import static org.kie.kogito.app.audit.api.SubsystemConstants.DATA_AUDIT_PATH; - -@RestController -@Transactional -public class GraphQLJPAAuditDataRouteMapping { - - private DataAuditQueryService dataAuditQueryService; - - @Autowired - DataAuditContextFactory dataAuditContextFactory; - - @PostConstruct - public void init() { - dataAuditQueryService = DataAuditQueryService.newAuditQuerySerice(); - - } - - @PostMapping(value = DATA_AUDIT_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) - public Map executeQuery(@RequestBody JsonNode query) { - ExecutionResult executionResult = dataAuditQueryService.executeQuery(dataAuditContextFactory.newDataAuditContext(), query.get("query").asText()); - return executionResult.toSpecification(); - } - -} diff --git a/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditEventPublisher.java b/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootDataAuditEventPublisher.java similarity index 94% rename from data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditEventPublisher.java rename to data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootDataAuditEventPublisher.java index 9f011e3394..3081d87b08 100644 --- a/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditEventPublisher.java +++ b/data-audit/kogito-addons-data-audit-springboot/src/main/java/org/kie/kogito/app/audit/springboot/SpringbootDataAuditEventPublisher.java @@ -35,16 +35,16 @@ @Component @Transactional -public class SpringbootJPADataAuditEventPublisher implements EventPublisher { +public class SpringbootDataAuditEventPublisher implements EventPublisher { - private static final Logger LOGGER = LoggerFactory.getLogger(SpringbootJPADataAuditEventPublisher.class); + private static final Logger LOGGER = LoggerFactory.getLogger(SpringbootDataAuditEventPublisher.class); private DataAuditStoreProxyService proxy; @Autowired DataAuditContextFactory dataAuditContextFactory; - public SpringbootJPADataAuditEventPublisher() { + public SpringbootDataAuditEventPublisher() { proxy = DataAuditStoreProxyService.newAuditStoreService(); } diff --git a/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootAuditQueryRegistryServiceTest.java b/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootAuditQueryRegistryServiceTest.java new file mode 100644 index 0000000000..4e873373ef --- /dev/null +++ b/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootAuditQueryRegistryServiceTest.java @@ -0,0 +1,357 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.app.audit.springboot; + +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.kie.kogito.app.audit.api.DataAuditStoreProxyService; +import org.kie.kogito.app.audit.api.SubsystemConstants; +import org.kie.kogito.app.audit.spi.DataAuditContextFactory; +import org.kie.kogito.event.EventPublisher; +import org.kie.kogito.event.job.JobInstanceDataEvent; +import org.kie.kogito.event.process.ProcessInstanceStateDataEvent; +import org.kie.kogito.event.process.ProcessInstanceStateEventBody; +import org.kie.kogito.jobs.service.model.JobStatus; +import org.kie.kogito.process.ProcessInstance; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; + +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.deriveNewState; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.newJobEvent; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.newProcessInstanceStateEvent; +import static org.kie.kogito.app.audit.quarkus.DataAuditTestUtils.wrapQuery; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = "server_port=0") +@TestInstance(Lifecycle.PER_CLASS) +public class SpringbootAuditQueryRegistryServiceTest { + + @LocalServerPort + private Integer port; + + @Autowired + EventPublisher publisher; + + @Autowired + DataAuditContextFactory contextFactory; + + @BeforeAll + public void init() throws Exception { + + ProcessInstanceStateDataEvent processInstanceEvent = newProcessInstanceStateEvent("processId1", "1", ProcessInstance.STATE_ACTIVE, "rootI1", "rootP1", "parent1", "identity", + ProcessInstanceStateEventBody.EVENT_TYPE_STARTED); + publisher.publish(processInstanceEvent); + + JobInstanceDataEvent jobEvent; + jobEvent = newJobEvent("job1", "nodeInstanceId1", 1, "processId1", "processInstanceId1", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.EXECUTED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job2", "nodeInstanceId1", 1, "processId1", "processInstanceId2", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job3", "nodeInstanceId1", 1, "processId1", "processInstanceId3", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.CANCELED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job4", "nodeInstanceId1", 1, "processId1", "processInstanceId4", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.RETRY); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 2, JobStatus.EXECUTED); + publisher.publish(jobEvent); + + jobEvent = newJobEvent("job5", "nodeInstanceId1", 1, "processId1", "processInstanceI51", 100L, 10, "rootProcessId1", "rootProcessInstanceId1", JobStatus.SCHEDULED, 0); + publisher.publish(jobEvent); + + jobEvent = deriveNewState(jobEvent, 1, JobStatus.ERROR); + publisher.publish(jobEvent); + } + + @Test + public void testRegisterQueryComplexType() { + + String body = "{" + + "\"identifier\" : \"testsComplex\", " + + "\"graphQLDefinition\" : \"type EventTest { jobId : String, processInstanceId: String} type Query { testsComplex (pagination: Pagination) : [ EventTest ] } \"," + + "\"query\" : \" SELECT o.job_id, o.process_instance_id FROM job_execution_log o \"" + + "}"; + + given() + .contentType(ContentType.JSON) + .body(body) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ testsComplex { jobId, processInstanceId } }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.testsComplex"); + + assertThat(response) + .hasSize(10); + + } + + @Test + public void testRegisterQuerySimpleType() { + + String body = "{" + + "\"identifier\" : \"testSimple\", " + + "\"graphQLDefinition\" : \"type Query { testSimple (pagination: Pagination) : [ String ] } \"," + + "\"query\" : \" SELECT o.job_id FROM job_execution_log o \"" + + "}"; + + given() + .contentType(ContentType.JSON) + .body(body) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ testSimple }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.testSimple"); + + assertThat(response) + .hasSize(10); + + } + + @Test + public void testRegisterQueryWithDateTime() { + + String body = "{\n" + + " \"identifier\": \"GetWithTime\",\n" + + " \"graphQLDefinition\": \"type GetWithTimeType {eventId: String, processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetWithTime(pagination:Pagination) : [GetWithTimeType]}\",\n" + + " \"query\": \"SELECT log.event_id as eventId, log.process_instance_id as processInstanceId, log.process_id as processId, log.state as state, log.event_type as eventType, log.event_date as eventDate FROM Process_Instance_State_Log log group by log.event_id, log.event_type, log.event_date, log.process_id, log.process_instance_id, log.state order by processInstanceId, eventDate\" }"; + + given() + .contentType(ContentType.JSON) + .body(body) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200); + + String query = "{ GetWithTime {eventId, eventDate, processInstanceId, state} }"; + query = wrapQuery(query); + + List> response = given() + .contentType(ContentType.JSON) + .body(query) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .and() + .extract().path("data.GetWithTime"); + + assertThat(response) + .extracting("eventDate") + .allSatisfy(OffsetDateTime.class::isInstance); + + } + + @Test + public void testQuerySQLRegistrationFailure() { + + String q = " ".repeat(6000); + + String body = "{\n" + + " \"identifier\": \"ErrorSQLQuery\",\n" + + " \"graphQLDefinition\": \"type ErrorSQLQuery {eventId: String, processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetAllStates(pagination:Pagination) : [AllStates]}\",\n" + + " \"query\": \"" + q + "\" }"; + + String graphQLBefore = given() + .contentType(ContentType.JSON) + .port(port) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + given() + .contentType(ContentType.JSON) + .body(body) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(400); + + String graphQLAfter = given() + .contentType(ContentType.JSON) + .port(port) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + assertEquals(graphQLAfter, graphQLBefore); + Assertions.assertThat(graphQLAfter).doesNotContain("ErrorSQLQuery"); + + Assertions.assertThat(DataAuditStoreProxyService.newAuditStoreService().findQueries(contextFactory.newDataAuditContext())) + .extracting(e -> e.getIdentifier()) + .doesNotContain("ErrorSQLQuery"); + + } + + @Test + public void testQueryGraphQLRegistrationFailure() { + + String q = " ".repeat(1000); + + String body = "{\n" + + " \"identifier\": \"ErrorGraphQLQuery\",\n" + + " \"graphQLDefinition\": \"type ErrorGraphQLQuery {eventId: processInstanceId: String, processId: String, state: String, eventType: String, eventDate: DateTime } type Query {GetAllStates(pagination:Pagination) : [AllStates]}\",\n" + + " \"query\": \"" + q + "\" }"; + + String graphQLBefore = given() + .contentType(ContentType.JSON) + .port(port) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + given() + .contentType(ContentType.JSON) + .body(body) + .port(port) + .when() + .post(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(400); + + String graphQLAfter = given() + .contentType(ContentType.JSON) + .port(port) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .extract().asString(); + + assertEquals(graphQLAfter, graphQLBefore); + Assertions.assertThat(graphQLAfter).doesNotContain("ErrorGraphQLQuery"); + + Assertions.assertThat(DataAuditStoreProxyService.newAuditStoreService().findQueries(contextFactory.newDataAuditContext())) + .extracting(e -> e.getIdentifier()) + .doesNotContain("ErrorGraphQLQuery"); + } + + @Test + public void testPrintSchema() { + given() + .port(port) + .when() + .get(SubsystemConstants.DATA_AUDIT_REGISTRY_PATH) + .then() + .log() + .body() + .assertThat() + .statusCode(200) + .body(Matchers.notNullValue()); + } +} diff --git a/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditTest.java b/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditTest.java index 0ffc2136a3..c8e63821e2 100644 --- a/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditTest.java +++ b/data-audit/kogito-addons-data-audit-springboot/src/test/java/org/kie/kogito/app/audit/springboot/SpringbootJPADataAuditTest.java @@ -90,7 +90,7 @@ public void testSpringbootEventPublisher() { .contentType(ContentType.JSON) .body("{\"query\": \"{ GetAllProcessInstancesState { eventId, eventDate, processType, processId, processVersion, parentProcessInstanceId, rootProcessId, rootProcessInstanceId, processInstanceId, businessKey, eventType, outcome, state, slaDueDate } }\"}") .when() - .post(SubsystemConstants.DATA_AUDIT_PATH) + .post(SubsystemConstants.DATA_AUDIT_QUERY_PATH) .then() .assertThat() .statusCode(200) diff --git a/kogito-apps-bom/pom.xml b/kogito-apps-bom/pom.xml index 9f60c13fe4..a25c91ac82 100644 --- a/kogito-apps-bom/pom.xml +++ b/kogito-apps-bom/pom.xml @@ -142,6 +142,12 @@ data-audit-common ${project.version} + + org.kie.kogito + data-audit-common + ${project.version} + tests + org.kie.kogito data-audit-common-service From fe2095b100bca307810edd4337af49f0a6acbb98 Mon Sep 17 00:00:00 2001 From: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:26:29 +0100 Subject: [PATCH 3/3] [Fix_#3383] Setting metadata when using binary (#1974) Since quarkus-http now uses binary as default the test condition for knative message has changed --- .../java/org/kie/kogito/it/jobs/SwitchStateTimeoutsIT.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps-integration-tests/integration-tests-jobs-service/integration-tests-jobs-service-quarkus/integration-tests-jobs-service-quarkus-knative-eventing/src/test/java/org/kie/kogito/it/jobs/SwitchStateTimeoutsIT.java b/apps-integration-tests/integration-tests-jobs-service/integration-tests-jobs-service-quarkus/integration-tests-jobs-service-quarkus-knative-eventing/src/test/java/org/kie/kogito/it/jobs/SwitchStateTimeoutsIT.java index 8d04406264..62ab32d779 100644 --- a/apps-integration-tests/integration-tests-jobs-service/integration-tests-jobs-service-quarkus/integration-tests-jobs-service-quarkus-knative-eventing/src/test/java/org/kie/kogito/it/jobs/SwitchStateTimeoutsIT.java +++ b/apps-integration-tests/integration-tests-jobs-service/integration-tests-jobs-service-quarkus/integration-tests-jobs-service-quarkus-knative-eventing/src/test/java/org/kie/kogito/it/jobs/SwitchStateTimeoutsIT.java @@ -49,9 +49,9 @@ protected void verifyNoDecisionEventWasProduced(String processInstanceId) throws .with().pollInterval(1, SECONDS) .untilAsserted(() -> sink.verify(1, postRequestedFor(urlEqualTo("/")) - .withRequestBody(matchingJsonPath("kogitoprocinstanceid", equalTo(processInstanceId))) - .withRequestBody(matchingJsonPath("type", equalTo(PROCESS_RESULT_EVENT_TYPE))) - .withRequestBody(matchingJsonPath("data.decision", equalTo(DECISION_NO_DECISION))))); + .withHeader("ce-kogitoprocinstanceid", equalTo(processInstanceId)) + .withHeader("ce-type", equalTo(PROCESS_RESULT_EVENT_TYPE)) + .withRequestBody(matchingJsonPath("decision", equalTo(DECISION_NO_DECISION))))); }