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 (#3701)
  • Loading branch information
elguardian authored Oct 14, 2024
1 parent 42d3a4e commit 9d37dc2
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 20 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,105 @@
/*
* 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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class CodegenUtil {

private static final Logger LOG = LoggerFactory.getLogger(CodegenUtil.class);
/**
* Flag used to configure transaction enabling. Default to <code>true</code>
*/
public static final String TRANSACTION_ENABLED = "transactionEnabled";

private CodegenUtil() {
// do nothing
}

/**
* Creates the property for a certain generator.
*
* @param generator
* @param propertyName
* @return returns the property for certain generator
*/
public static String generatorProperty(Generator generator, String propertyName) {
return String.format("kogito.%s.%s", generator.name(), propertyName);
}

/**
* Creates the property for global application
*
* @param propertyName
* @return
*/
public static String globalProperty(String propertyName) {
return String.format("kogito.%s", propertyName);
}

/**
* This computes the boolean value of the transaction being enabled based on the logic specified
* the property. Default value it is true
*
* @see CodegenUtil#getProperty
*/
public static boolean isTransactionEnabled(Generator generator, KogitoBuildContext context) {
boolean propertyValue = getProperty(generator, context, TRANSACTION_ENABLED, Boolean::parseBoolean, true);
LOG.info("trying to compute property {} for generator {} property with value {}", TRANSACTION_ENABLED, generator.name(), propertyValue);
return propertyValue;
}

/**
* This method is a generic method to compute certain property of the given type.
* 1. we compute the global property applicable for all the application.
* 2. we compute the property only applicable for certain generator.
*
* @see CodegenUtil#getApplicationProperty
* @see CodegenUtil#globalProperty
* @see CodegenUtil#generatorProperty
*/
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 @@ -97,9 +99,9 @@ public class UserTaskCodegen extends AbstractGenerator {
BPMN_SEMANTIC_MODULES.addSemanticModule(new BPMNDISemanticModule());
}

public static final String SECTION_CLASS_NAME = "usertask";
public static final String SECTION_CLASS_NAME = "usertasks";

TemplatedGenerator templateGenerator;
private TemplatedGenerator templateGenerator;
private List<Work> descriptors;
private TemplatedGenerator producerTemplateGenerator;
private TemplatedGenerator restTemplateGenerator;
Expand Down Expand Up @@ -158,25 +160,28 @@ 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 compilationUnit = restTemplateGenerator.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());
return new GeneratedFile(GeneratedFileType.REST, 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 compilationUnit = producerTemplateGenerator.compilationUnitOrThrow("No producer template found for user tasks");
compilationUnit.setPackageDeclaration(packageName);
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 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 9d37dc2

Please sign in to comment.