Skip to content

Commit

Permalink
[incubator-kie-issues-1517] Add Transactional Rest endpoints to UserT…
Browse files Browse the repository at this point in the history
…asks
  • Loading branch information
elguardian committed Oct 8, 2024
1 parent 30cf1bd commit 580c345
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -369,7 +369,7 @@ protected Collection<GeneratedFile> 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);
}
Expand Down Expand Up @@ -511,11 +511,6 @@ protected Collection<GeneratedFile> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@
*/
public class ProcessResourceGenerator {

/**
* Flag used to configure transaction enablement. Default to <code>true</code>
*/
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <code>true</code>
*/
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> T getProperty(Generator generator, KogitoBuildContext context, String propertyName, Function<String, T> 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"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -99,10 +101,11 @@ public class UserTaskCodegen extends AbstractGenerator {

public static final String SECTION_CLASS_NAME = "usertask";

TemplatedGenerator templateGenerator;
private TemplatedGenerator templateGenerator;
private List<Work> descriptors;
private TemplatedGenerator producerTemplateGenerator;
private TemplatedGenerator restTemplateGenerator;
boolean transactionEnabled;

public UserTaskCodegen(KogitoBuildContext context, List<Work> collectedResources) {
super(context, "usertasks");
Expand Down Expand Up @@ -158,16 +161,19 @@ protected Collection<GeneratedFile> 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);
Expand All @@ -176,7 +182,7 @@ private GeneratedFile generateProducer() {
return new GeneratedFile(GeneratedFileType.SOURCE, Path.of(urlBase).resolve(className + ".java"), compilationUnit.toString());
}

private List<GeneratedFile> generateUserTask() {
public List<GeneratedFile> generateUserTask() {
List<GeneratedFile> generatedFiles = new ArrayList<>();
for (Work info : descriptors) {
CompilationUnit unit = templateGenerator.compilationUnit().get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<MethodDeclaration> 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<MethodDeclaration> 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<MethodDeclaration> 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<MethodDeclaration> 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<MethodDeclaration> 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<AnnotationExpr> 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<MethodDeclaration> restEndpoints,
KogitoBuildContext kogitoBuildContext,
boolean enabled) {
Expand Down

0 comments on commit 580c345

Please sign in to comment.