From 580c345cd163ab51dfec7f9dee8805cd5581cb44 Mon Sep 17 00:00:00 2001 From: Enrique Gonzalez Martinez Date: Tue, 8 Oct 2024 10:26:11 +0200 Subject: [PATCH] [incubator-kie-issues-1517] Add Transactional Rest endpoints to UserTasks --- .../codegen/process/ProcessCodegen.java | 9 +-- .../process/ProcessResourceGenerator.java | 5 -- .../codegen/process/util/CodegenUtil.java | 78 ++++++++++++++++++ .../codegen/usertask/UserTaskCodegen.java | 14 +++- .../process/ProcessResourceGeneratorTest.java | 81 +++++++++++++++++++ 5 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/util/CodegenUtil.java diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java index 86e6bd80531..528a9e081a0 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessCodegen.java @@ -62,6 +62,7 @@ import org.kie.kogito.codegen.process.config.ProcessConfigGenerator; import org.kie.kogito.codegen.process.events.ProcessCloudEventMeta; import org.kie.kogito.codegen.process.events.ProcessCloudEventMetaFactoryGenerator; +import org.kie.kogito.codegen.process.util.CodegenUtil; import org.kie.kogito.internal.SupportedExtensions; import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess; import org.kie.kogito.process.validation.ValidationException; @@ -76,7 +77,6 @@ import static java.lang.String.format; import static java.util.stream.Collectors.toList; -import static org.kie.kogito.codegen.process.ProcessResourceGenerator.TRANSACTION_ENABLED; import static org.kie.kogito.grafana.GrafanaConfigurationWriter.buildDashboardName; import static org.kie.kogito.grafana.GrafanaConfigurationWriter.generateOperationalDashboard; import static org.kie.kogito.internal.utils.ConversionUtils.sanitizeClassName; @@ -369,7 +369,7 @@ protected Collection internalGenerate() { .withWorkItems(processIdToWorkItemModel.get(workFlowProcess.getId())) .withSignals(metaData.getSignals()) .withTriggers(metaData.isStartable(), metaData.isDynamic(), metaData.getTriggers()) - .withTransaction(isTransactionEnabled()); + .withTransaction(CodegenUtil.isTransactionEnabled(this, context())); rgs.add(processResourceGenerator); } @@ -511,11 +511,6 @@ protected Collection internalGenerate() { return generatedFiles; } - protected boolean isTransactionEnabled() { - String processTransactionProperty = String.format("kogito.%s.%s", GENERATOR_NAME, TRANSACTION_ENABLED); - return "true".equalsIgnoreCase(context().getApplicationProperty(processTransactionProperty).orElse("true")); - } - private void storeFile(GeneratedFileType type, String path, String source) { if (generatedFiles.stream().anyMatch(f -> path.equals(f.relativePath()))) { LOGGER.warn("There's already a generated file named {} to be compiled. Ignoring.", path); diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java index 3e2f8765a40..207af9e6369 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/ProcessResourceGenerator.java @@ -77,11 +77,6 @@ */ public class ProcessResourceGenerator { - /** - * Flag used to configure transaction enablement. Default to true - */ - public static final String TRANSACTION_ENABLED = "transactionEnabled"; - static final String INVALID_CONTEXT_TEMPLATE = "ProcessResourceGenerator can't be used for context without Rest %s"; private static final Logger LOG = LoggerFactory.getLogger(ProcessResourceGenerator.class); diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/util/CodegenUtil.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/util/CodegenUtil.java new file mode 100644 index 00000000000..d23d541aa96 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/process/util/CodegenUtil.java @@ -0,0 +1,78 @@ +/* + * 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.codegen.process.util; + +import java.util.function.Function; + +import org.kie.kogito.codegen.api.Generator; +import org.kie.kogito.codegen.api.context.KogitoBuildContext; + +public final class CodegenUtil { + /** + * Flag used to configure transaction enablement. Default to true + */ + public static final String TRANSACTION_ENABLED = "transactionEnabled"; + + private CodegenUtil() { + // do nothing + } + + public static String generatorProperty(Generator generator, String propertyName) { + return String.format("kogito.%s.%s", generator.name(), propertyName); + } + + public static String globalProperty(String propertyName) { + return String.format("kogito.%s", propertyName); + } + + /** + * There is a preference order about how to compute transaction enabling. + * 1. We check the property for the custom generator and + */ + public static boolean isTransactionEnabled(Generator generator, KogitoBuildContext context) { + return getProperty(generator, context, TRANSACTION_ENABLED, Boolean::parseBoolean, true); + } + + /** + * There is a preference order about how to compute transaction enabling. + * 1. We check the property for the custom generator and + */ + public static T getProperty(Generator generator, KogitoBuildContext context, String propertyName, Function converter, T defaultValue) { + + String generatorProperty = generatorProperty(generator, propertyName); + if (isApplicationPropertyDefined(context, generatorProperty)) { + return converter.apply(getApplicationProperty(context, generatorProperty)); + } + + String globalProperty = globalProperty(propertyName); + if (isApplicationPropertyDefined(context, globalProperty)) { + return converter.apply(getApplicationProperty(context, globalProperty)); + } + + return defaultValue; + } + + private static boolean isApplicationPropertyDefined(KogitoBuildContext context, String property) { + return context.getApplicationProperty(property).isPresent(); + } + + private static String getApplicationProperty(KogitoBuildContext context, String property) { + return context.getApplicationProperty(property).orElseThrow(() -> new IllegalArgumentException("Property " + property + " defined but does not contain proper value")); + } +} diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java index bd10069d470..2e468a07792 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/main/java/org/kie/kogito/codegen/usertask/UserTaskCodegen.java @@ -51,6 +51,7 @@ import org.kie.kogito.codegen.core.AbstractGenerator; import org.kie.kogito.codegen.process.ProcessCodegenException; import org.kie.kogito.codegen.process.ProcessParsingException; +import org.kie.kogito.codegen.process.util.CodegenUtil; import org.kie.kogito.internal.SupportedExtensions; import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess; import org.kie.kogito.process.validation.ValidationException; @@ -62,6 +63,7 @@ import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; import com.github.javaparser.ast.body.ConstructorDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; import com.github.javaparser.ast.expr.CastExpr; import com.github.javaparser.ast.expr.Expression; import com.github.javaparser.ast.expr.IntegerLiteralExpr; @@ -99,10 +101,11 @@ public class UserTaskCodegen extends AbstractGenerator { public static final String SECTION_CLASS_NAME = "usertask"; - TemplatedGenerator templateGenerator; + private TemplatedGenerator templateGenerator; private List descriptors; private TemplatedGenerator producerTemplateGenerator; private TemplatedGenerator restTemplateGenerator; + boolean transactionEnabled; public UserTaskCodegen(KogitoBuildContext context, List collectedResources) { super(context, "usertasks"); @@ -158,16 +161,19 @@ protected Collection internalGenerate() { return generatedFiles; } - private GeneratedFile generateRestEndpiont() { + public GeneratedFile generateRestEndpiont() { String packageName = context().getPackageName(); CompilationUnit compilationUnit = producerTemplateGenerator.compilationUnitOrThrow("Not rest endpoints template found for user tasks"); compilationUnit.setPackageDeclaration(packageName); + if (CodegenUtil.isTransactionEnabled(this, context())) { + compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).forEach(context().getDependencyInjectionAnnotator()::withTransactional); + } String className = compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString(); String urlBase = packageName.replaceAll("\\.", File.separator); return new GeneratedFile(GeneratedFileType.SOURCE, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); } - private GeneratedFile generateProducer() { + public GeneratedFile generateProducer() { String packageName = context().getPackageName(); CompilationUnit compilationUnit = restTemplateGenerator.compilationUnitOrThrow("No producer template found for user tasks"); compilationUnit.setPackageDeclaration(packageName); @@ -176,7 +182,7 @@ private GeneratedFile generateProducer() { return new GeneratedFile(GeneratedFileType.SOURCE, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString()); } - private List generateUserTask() { + public List generateUserTask() { List generatedFiles = new ArrayList<>(); for (Work info : descriptors) { CompilationUnit unit = templateGenerator.compilationUnit().get(); diff --git a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java index 52d1665166a..551d5b48b5d 100644 --- a/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java +++ b/kogito-codegen-modules/kogito-codegen-processes/src/test/java/org/kie/kogito/codegen/process/ProcessResourceGeneratorTest.java @@ -20,6 +20,7 @@ import java.io.File; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Predicate; @@ -35,6 +36,8 @@ import org.kie.kogito.codegen.api.AddonsConfig; import org.kie.kogito.codegen.api.context.KogitoBuildContext; import org.kie.kogito.codegen.api.context.impl.JavaKogitoBuildContext; +import org.kie.kogito.codegen.process.util.CodegenUtil; +import org.kie.kogito.codegen.usertask.UserTaskCodegen; import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess; import com.github.javaparser.StaticJavaParser; @@ -209,6 +212,84 @@ void testManageTransactionalDisabled(KogitoBuildContext.Builder contextBuilder) testTransaction(restEndpoints, kogitoBuildContext, transactionEnabled); } + @ParameterizedTest + @MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders") + void testUserTaskManageTransactionalEnabledByDefault(KogitoBuildContext.Builder contextBuilder) { + KogitoBuildContext context = contextBuilder.build(); + UserTaskCodegen userTaskCodegen = new UserTaskCodegen(context, Collections.emptyList()); + CompilationUnit compilationUnit = StaticJavaParser.parse(new String(userTaskCodegen.generateRestEndpiont().contents())); + List restEndpoints = compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).toList(); + assertThat(restEndpoints).isNotEmpty(); + for (MethodDeclaration method : restEndpoints) { + assertThat(findAnnotationExpr(method, context.getDependencyInjectionAnnotator().getTransactionalAnnotation())).isPresent(); + } + } + + @ParameterizedTest + @MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders") + void testUserTaskManageTransactionalDisabled(KogitoBuildContext.Builder contextBuilder) { + KogitoBuildContext context = contextBuilder.build(); + UserTaskCodegen userTaskCodegen = new UserTaskCodegen(context, Collections.emptyList()); + context.setApplicationProperty(CodegenUtil.generatorProperty(userTaskCodegen, CodegenUtil.TRANSACTION_ENABLED), "false"); + CompilationUnit compilationUnit = StaticJavaParser.parse(new String(userTaskCodegen.generateRestEndpiont().contents())); + List restEndpoints = compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).toList(); + assertThat(restEndpoints).isNotEmpty(); + for (MethodDeclaration method : restEndpoints) { + assertThat(findAnnotationExpr(method, context.getDependencyInjectionAnnotator().getTransactionalAnnotation())).isEmpty(); + } + } + + @ParameterizedTest + @MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders") + void testUserTaskManageTransactionalGeneratorDisabled(KogitoBuildContext.Builder contextBuilder) { + KogitoBuildContext context = contextBuilder.build(); + UserTaskCodegen userTaskCodegen = new UserTaskCodegen(context, Collections.emptyList()); + context.setApplicationProperty(CodegenUtil.generatorProperty(userTaskCodegen, CodegenUtil.TRANSACTION_ENABLED), "false"); + CompilationUnit compilationUnit = StaticJavaParser.parse(new String(userTaskCodegen.generateRestEndpiont().contents())); + List restEndpoints = compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).toList(); + assertThat(restEndpoints).isNotEmpty(); + for (MethodDeclaration method : restEndpoints) { + assertThat(findAnnotationExpr(method, context.getDependencyInjectionAnnotator().getTransactionalAnnotation())).isEmpty(); + } + } + + @ParameterizedTest + @MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders") + void testUserTaskManageTransactionalEnabled(KogitoBuildContext.Builder contextBuilder) { + KogitoBuildContext context = contextBuilder.build(); + UserTaskCodegen userTaskCodegen = new UserTaskCodegen(context, Collections.emptyList()); + context.setApplicationProperty(CodegenUtil.generatorProperty(userTaskCodegen, CodegenUtil.TRANSACTION_ENABLED), "true"); + CompilationUnit compilationUnit = StaticJavaParser.parse(new String(userTaskCodegen.generateRestEndpiont().contents())); + List restEndpoints = compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).toList(); + assertThat(restEndpoints).isNotEmpty(); + for (MethodDeclaration method : restEndpoints) { + assertThat(findAnnotationExpr(method, context.getDependencyInjectionAnnotator().getTransactionalAnnotation())).isPresent(); + } + } + + @ParameterizedTest + @MethodSource("org.kie.kogito.codegen.api.utils.KogitoContextTestUtils#restContextBuilders") + void testUserTaskManageTransactionalGeneratorEnabled(KogitoBuildContext.Builder contextBuilder) { + KogitoBuildContext context = contextBuilder.build(); + UserTaskCodegen userTaskCodegen = new UserTaskCodegen(context, Collections.emptyList()); + context.setApplicationProperty(CodegenUtil.generatorProperty(userTaskCodegen, CodegenUtil.TRANSACTION_ENABLED), "true"); + CompilationUnit compilationUnit = StaticJavaParser.parse(new String(userTaskCodegen.generateRestEndpiont().contents())); + List restEndpoints = compilationUnit.findAll(MethodDeclaration.class).stream().filter(MethodDeclaration::isPublic).toList(); + assertThat(restEndpoints).isNotEmpty(); + for (MethodDeclaration method : restEndpoints) { + assertThat(findAnnotationExpr(method, context.getDependencyInjectionAnnotator().getTransactionalAnnotation())).isPresent(); + } + } + + Optional findAnnotationExpr(MethodDeclaration method, String fqn) { + for (AnnotationExpr expr : method.getAnnotations()) { + if (expr.getNameAsString().equals(fqn)) { + return Optional.of(expr); + } + } + return Optional.empty(); + } + void testTransaction(Collection restEndpoints, KogitoBuildContext kogitoBuildContext, boolean enabled) {