diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/README.md b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/README.md new file mode 100644 index 0000000000..ed882a3883 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/README.md @@ -0,0 +1,38 @@ + + +# KOGITO-4499 Hot reload false negative instrumentation for DecisionModels + +The DecisionModels is meant to be reloaded, but it would seems that for some reasons the quarkus dev mode hotreload instrumentation thinks the class is equivalent hence not reloaded. + +The problem can be reproduced only using DevMojo maven invoker style, not with ShrinkWrap. + +This is a simple module which would test maven invoker style per the linked DevMojoIT, as suggested here https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/Failed.20to.20replace.20classes.20via.20instrumentation/near/225045416 + +The second change of the .dmn resource fails, and I believe this might be a good demonstration of how the Kogito hot reload is currently broken. + +The test prepares a basic Kogito-on-Quarkus maven app with a simple DMN, containing a decision which prefix "Hello, " the name given in input. +The first http call works as expected, the returned value is "Hello, v1". +The test then mutates the content of the dmn, now the decision should prefix "Ciao, ". +This second http call works again as expected, the returned value is "Ciao, v1". +The test then mutates again the content of the dmn, now it is expecting to prefix "Bonjour, ". +As can be seen in the log, attached, this fails, since the Ciao-prefix is still occuring. +build-project-intrumentation-reload-dmn.log + +This module and test demonstrate the fix for KOGITO-4499. diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/pom.xml b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/pom.xml new file mode 100644 index 0000000000..9896674ae9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/pom.xml @@ -0,0 +1,140 @@ + + + + 4.0.0 + + + org.kie.kogito + apps-integration-tests + 999-SNAPSHOT + + + jbpm-sith-drools-quarkus-integration-test-maven-devmode + jBPM with Drools :: Quarkus Extension :: Integration Tests (Maven devmode) + + + org.kie.kogito/data-index-service-inmemory:${project.version} + + false + io.quarkus.it.kogito.devmode + + + + + + org.kie.kogito + kogito-quarkus-bom + ${project.version} + pom + import + + + + + + + + + org.jbpm + jbpm-with-drools-quarkus-deployment + ${project.version} + pom + test + + + * + * + + + + + + org.jbpm + jbpm-with-drools-quarkus + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + org.kie.kogito + kogito-test-utils + test + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-test-maven + ${version.io.quarkus.quarkus-test} + test + + + com.github.stephenc.jcip + jcip-annotations + ${version.com.github.stephenc.jcip} + test + + + + + + + src/test/resources + true + + + + + maven-failsafe-plugin + + + ${maven.home} + + + + + + integration-test + verify + + + + + + + + diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java new file mode 100644 index 0000000000..c8edef6f27 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java @@ -0,0 +1,456 @@ +/* + * 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 io.quarkus.it.kogito.devmode; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.quarkus.maven.it.RunAndCheckMojoTestBase; +import io.quarkus.maven.it.verifier.MavenProcessInvoker; +import io.quarkus.maven.it.verifier.RunningInvoker; +import io.quarkus.test.devmode.util.DevModeTestUtils; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import net.jcip.annotations.NotThreadSafe; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; + +/* + * Test inspired by https://github.com/quarkusio/quarkus/blob/c8919cfb8abbc3df49dd1febd74b998417b0367e/integration-tests/maven/src/test/java/io/quarkus/maven/it/DevMojoIT.java#L218 + */ +//@DisableForNative: it is not yet available as of 1.11, and I doubt is ever needed for this module +@NotThreadSafe +public class DevMojoIT extends RunAndCheckMojoTestBase { + + private static final String PROPERTY_MAVEN_REPO_LOCAL = "maven.repo.local"; + private static final String PROPERTY_MAVEN_SETTINGS = "maven.settings"; + private static final String MAVEN_REPO_LOCAL = System.getProperty(PROPERTY_MAVEN_REPO_LOCAL); + private static final String MAVEN_SETTINGS = System.getProperty(PROPERTY_MAVEN_SETTINGS); + + private static final long INIT_POLL_DELAY = 3; + private static final TimeUnit INIT_POLL_DELAY_UNIT = TimeUnit.SECONDS; + private static final long INIT_POLL_TIMEOUT = 2; + private static final TimeUnit INIT_POLL_TIMEOUT_UNIT = TimeUnit.MINUTES; + private static final long RELOAD_POLL_DELAY = INIT_POLL_DELAY; + private static final TimeUnit RELOAD_POLL_DELAY_UNIT = INIT_POLL_DELAY_UNIT; + private static final long RELOAD_POLL_TIMEOUT = 3; + private static final TimeUnit RELOAD_POLL_TIMEOUT_UNIT = TimeUnit.MINUTES; + private static final String QUARKUS_RANDOM_HTTP_PORT_PATTERN = "(?<=localhost:)[0-9]+"; + private static final String QUARKUS_APP_NAME_PROP_HANDLER = "-Dquarkus.application.name="; + + private final Pattern quarkusRandomHttpPortPattern = Pattern.compile(QUARKUS_RANDOM_HTTP_PORT_PATTERN); + private final Map randomHttpPorts = new HashMap<>(); + + private final Logger LOGGER = LoggerFactory.getLogger(DevMojoIT.class); + + static { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + private String getRestResponse(String port) { + AtomicReference resp = new AtomicReference<>(); + // retry on exceptions for connection refused, connection errors, etc. which will occur until the Kogito Quarkus maven project is fully built and running + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT).until(() -> { + try { + String content = DevModeTestUtils.get("http://localhost:" + port + "/control"); + resp.set(content); + return true; + } catch (Exception e) { + return false; + } + }); + return resp.get(); + } + + /* copy-paste from quarkus */ + @Override + protected void run(boolean performCompile, String... options) throws MavenInvocationException { + assertThat(testDir).isDirectory(); + running = new RunningInvoker(testDir, false); + String applicationName = ""; + final List args = new ArrayList<>(4 + options.length); + if (performCompile) { + args.add("compile"); + } + args.add("quarkus:dev"); + boolean hasDebugOptions = false; + for (String option : options) { + args.add(option); + if (option.trim().startsWith("-Ddebug=") || option.trim().startsWith("-Dsuspend=")) { + hasDebugOptions = true; + } + // We have to respect the run method interface, that's why we are using options instead of a proper parameter + // or even better, returning the actual port directly without a collector + if (option.trim().startsWith(QUARKUS_APP_NAME_PROP_HANDLER)) { + applicationName = option.split("=")[1]; + } + } + if (!hasDebugOptions) { + // if no explicit debug options have been specified, let's just disable debugging + args.add("-Ddebug=false"); + } + + // Since the Kogito extension split, this requires more memory, going for a default of 1GB, per surefire. + args.add("-Djvm.args=-Xmx1024m"); + // Disable devservices + args.add("-Dquarkus.kogito.devservices.enabled=false"); + // Let Quarkus figure a random port + args.add("-Dquarkus.http.port=0"); + args.addAll(getProvidedMavenProperties()); + + running.execute(args, Collections.emptyMap()); + + final File logFile = ensureLogFile(); + AtomicLong filePointer = new AtomicLong(); + String finalApplicationName = applicationName; + + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT) + .until(() -> { + filePointer.set(seekRandomHttpPort(logFile, finalApplicationName, filePointer.get())); + return randomHttpPorts.containsKey(finalApplicationName); + }); + } + + private String run(String appName) throws MavenInvocationException { + LOGGER.info("Running test for {}", appName); + run(true, QUARKUS_APP_NAME_PROP_HANDLER + appName); + return randomHttpPorts.get(appName); + } + + /** + * Randomly access the log information and tries to match the port listened by the maven process + * Since the file is frequently written by another process, this method can be called inside await method. + * + * @return the updated file pointer (last byte read in the file), so callers can call this method again and continue seek for the required port + * @see MavenProcessInvoker + */ + private long seekRandomHttpPort(File logFile, String appName, long filePointer) { + long len = logFile.length(); + if (len < filePointer) { + filePointer = len; + } else if (len > filePointer) { + try (RandomAccessFile raf = new RandomAccessFile(logFile, "r")) { + raf.seek(filePointer); + String line; + while ((line = raf.readLine()) != null) { + Matcher matcher = quarkusRandomHttpPortPattern.matcher(line); + if (matcher.find()) { + randomHttpPorts.computeIfAbsent(appName, k -> matcher.group()); + LOGGER.info("App {} has port {}", appName, randomHttpPorts.get(appName)); + return filePointer; + } + } + filePointer = raf.getFilePointer(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + return filePointer; + } + + private File ensureLogFile() { + final File workingDirName = running.getWorkingDirectory(); + if (workingDirName.isDirectory()) { + final File[] files = workingDirName.listFiles((dir, name) -> name.equals("build-" + dir.getName() + ".log")); + if (files != null && files.length > 0) { + return files[0]; + } + } + throw new IllegalStateException("Couldn't find log file for project in the directory: " + workingDirName); + } + + private List getProvidedMavenProperties() { + List additionalArguments = new ArrayList<>(); + if (MAVEN_REPO_LOCAL != null) { + additionalArguments.add(String.format("-D%s=%s", PROPERTY_MAVEN_REPO_LOCAL, MAVEN_REPO_LOCAL)); + } + if (MAVEN_SETTINGS != null) { + /* + * Invoker would fail if the received settings.xml file did not exist. + * That can happen when ${session.request.userSettingsFile.path} is passed as value for maven.settings + * property from the pom.xml and at the same time user does not have settings.xml in ~/.m2/ nor they provided + * specific settings.xml using -s argument. + */ + if (new File(MAVEN_SETTINGS).exists()) { + additionalArguments.add(String.format("-s %s", MAVEN_SETTINGS)); + } + } + return additionalArguments; + } + + @Test + public void testBPMN2HotReload() throws Exception { + testDir = initProject("projects/classic-inst", "projects/project-intrumentation-reload-bpmn"); + String httpPort = run("testBPMN2HotReload"); + assertThat(httpPort).isNotEmpty(); + + final File controlSource = new File(testDir, "src/main/java/control/RestControl.java"); + + // await Quarkus + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Hello, v1")); + + LOGGER.info("[testBPMN2HotReload] Starting bpmn process"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"s1\": \"v1\"," + + " \"s2\": \"v2\"" + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "simple") + .then() + .statusCode(201) + .body("s2", is("Hello, v1")); + + // --- Change #1 + LOGGER.info("[testBPMN2HotReload] Beginning Change #1"); + File source = new File(testDir, "src/main/resources/simple.bpmn2"); + filter(source, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + filter(controlSource, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Ciao, v1")); + + LOGGER.info("[testBPMN2HotReload] Starting bpmn process"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"s1\": \"v1\"," + + " \"s2\": \"v2\"" + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "simple") + .then() + .statusCode(201) + .body("s2", is("Ciao, v1")); + + // --- Change #2 + LOGGER.info("[testBPMN2HotReload] Beginning Change #2"); + filter(source, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + filter(controlSource, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Bonjour, v1")); + + LOGGER.info("[testBPMN2HotReload] Starting bpmn process"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"s1\": \"v1\"," + + " \"s2\": \"v2\"" + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "simple") + .then() + .statusCode(201) + .body("s2", is("Bonjour, v1")); + + LOGGER.info("[testBPMN2HotReload] done."); + } + + @Test + public void testDMNHotReload() throws Exception { + testDir = initProject("projects/classic-inst", "projects/project-intrumentation-reload-dmn"); + final String httpPort = run("testDMNHotReload"); + + final File controlSource = new File(testDir, "src/main/java/control/RestControl.java"); + + // await Quarkus + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Hello, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DMN"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"name\": \"v1\" " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "hello") + .then() + .statusCode(200) + .body("greeting", is("Hello, v1")); + + // --- Change #1 + LOGGER.info("[testDMNHotReload] Beginning Change #1"); + File source = new File(testDir, "src/main/resources/hello.dmn"); + filter(source, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + filter(controlSource, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Ciao, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DMN"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"name\": \"v1\" " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "hello") + .then() + .statusCode(200) + .body("greeting", is("Ciao, v1")); + + // --- Change #2 + LOGGER.info("[testDMNHotReload] Beginning Change #2"); + filter(source, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + filter(controlSource, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Bonjour, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DMN"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"name\": \"v1\" " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "hello") + .then() + .statusCode(200) + .body("greeting", is("Bonjour, v1")); + + LOGGER.info("[testDMNHotReload] done."); + } + + @Test + public void testDRLHotReload() throws Exception { + testDir = initProject("projects/classic-inst", "projects/project-intrumentation-reload-drl"); + final String httpPort = run("testDRLHotReload"); + + final File controlSource = new File(testDir, "src/main/java/control/RestControl.java"); + + // await Quarkus + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Hello, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DRL"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"strings\": [\"v1\"] " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "q1") + .then() + .statusCode(200) + .body(containsString("Hello, v1")); + + // --- Change #1 + LOGGER.info("[testDMNHotReload] Beginning Change #1"); + File source = new File(testDir, "src/main/resources/acme/rules.drl"); + filter(source, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + filter(controlSource, Collections.singletonMap("\"Hello, \"+", "\"Ciao, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Ciao, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DRL"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"strings\": [\"v1\"] " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "q1") + .then() + .statusCode(200) + .body(containsString("Ciao, v1")); + + // --- Change #2 + LOGGER.info("[testDMNHotReload] Beginning Change #2"); + filter(source, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + filter(controlSource, Collections.singletonMap("\"Ciao, \"+", "\"Bonjour, \"+")); + await().pollDelay(RELOAD_POLL_DELAY, RELOAD_POLL_DELAY_UNIT) + .atMost(RELOAD_POLL_TIMEOUT, RELOAD_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Bonjour, v1")); + + LOGGER.info("[testDMNHotReload] Evaluate DRL"); + given().baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\n" + + " \"strings\": [\"v1\"] " + + "}") + .contentType(ContentType.JSON) + .when() + .post("/" + "q1") + .then() + .statusCode(200) + .body(containsString("Bonjour, v1")); + + LOGGER.info("done."); + } + + @Test + public void testStaticResource() throws MavenInvocationException { + testDir = initProject("projects/simple-dmn", "projects/simple-dmn-static-resource"); + final String httpPort = run("testStaticResource"); + + // await Quarkus + await().pollDelay(INIT_POLL_DELAY, INIT_POLL_DELAY_UNIT) + .atMost(INIT_POLL_TIMEOUT, INIT_POLL_TIMEOUT_UNIT).until(() -> getRestResponse(httpPort).contains("Hello, v1")); + + // static resource + given().baseUri("http://localhost:" + httpPort) + .get("/hello.json") + .then() + .statusCode(200) + .body("definitions", aMapWithSize(greaterThan(0))); + } +} diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/pom.xml b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/pom.xml new file mode 100644 index 0000000000..27001ffe2f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/pom.xml @@ -0,0 +1,111 @@ + + + + 4.0.0 + com.company + sample-kogito + 1.0-SNAPSHOT + + Generated project sample-kogito + + + @version.io.quarkus@ + @project.version@ + 2.22.2 + + UTF-8 + + + + + + org.kie.kogito + kogito-quarkus-bom + ${kogito.version} + pom + import + + + + + + + org.jbpm + jbpm-with-drools-quarkus + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-arc + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @version.compiler.plugin@ + + 11 + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + build + + + + + + + + diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Hello.java b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Hello.java new file mode 100644 index 0000000000..f80d58f8ba --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Hello.java @@ -0,0 +1,36 @@ +/* + * 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 acme; + +import org.drools.ruleunits.api.DataSource; +import org.drools.ruleunits.api.DataStore; +import org.drools.ruleunits.api.RuleUnitData; + +public class Hello implements RuleUnitData { + DataStore strings = DataSource.createStore(); + DataStore messages = DataSource.createStore(); + + public DataStore getStrings() { + return strings; + } + + public DataStore getMessages() { + return messages; + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Message.java b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Message.java new file mode 100644 index 0000000000..3adec96661 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/acme/Message.java @@ -0,0 +1,31 @@ +/* + * 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 acme; + +public class Message { + private String text; + + public Message(String t) { + this.text = t; + } + + public String getText() { + return text; + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/control/RestControl.java b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/control/RestControl.java new file mode 100644 index 0000000000..2e0a9279f2 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/java/control/RestControl.java @@ -0,0 +1,36 @@ +/* + * 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 control; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.util.UUID; + +@Path("/control") +public class RestControl { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello, "+ "v1"; + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/META-INF/resources/index.html b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/META-INF/resources/index.html new file mode 100644 index 0000000000..67ff03c97b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,175 @@ + + + + + + + acme - 1.0-SNAPSHOT + + + + + + +
+
+

Congratulations, you have created a new Quarkus application.

+ +

Why do you see this?

+ +

This page is served by Quarkus. The source is in + src/main/resources/META-INF/resources/index.html.

+ +

What can I do from here?

+ +

If not already done, run the application in dev mode using: mvn compile quarkus:dev. +

+
    +
  • Add REST resources, Servlets, functions and other services in src/main/java.
  • +
  • Your static assets are located in src/main/resources/META-INF/resources.
  • +
  • Configure your application in src/main/resources/application.properties. +
  • +
+ +

Do you like Quarkus?

+

Go give it a star on GitHub.

+ +

How do I get rid of this page?

+

Just delete the src/main/resources/META-INF/resources/index.html file.

+
+
+
+

Application

+
    +
  • GroupId: org.acme
  • +
  • ArtifactId: acme
  • +
  • Version: 1.0-SNAPSHOT
  • +
  • Quarkus Version: 999-SNAPSHOT
  • +
+
+
+

Next steps

+ +
+
+
+ + + + diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/acme/rules.drl b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/acme/rules.drl new file mode 100644 index 0000000000..19840d2885 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/acme/rules.drl @@ -0,0 +1,32 @@ +/** + * 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 acme; +unit Hello; + +rule "r1" +when + $s: /strings +then + messages.add(new Message("Hello, "+ $s)); +end + +query "q1" + $s : /strings + $m : /messages +end \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/application.properties b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/application.properties new file mode 100644 index 0000000000..1c385db7f3 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +quarkus.kogito.devservices.enabled=false \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/hello.dmn b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/hello.dmn new file mode 100644 index 0000000000..5a4a8d55f4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/hello.dmn @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + "Hello, "+name + + + + + + + + 300 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/simple.bpmn2 b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/simple.bpmn2 new file mode 100644 index 0000000000..87dfaf5efc --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/classic-inst/src/main/resources/simple.bpmn2 @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _F761A3F1-1203-4FA2-97FE-5EC205688934 + + + + + + + + _AE824A19-D749-4F9F-9830-0A3DCF62C763 + _F761A3F1-1203-4FA2-97FE-5EC205688934 + System.out.println("At start, s1: "+kcontext.getVariable("s1")); +System.out.println("At start, s2: "+kcontext.getVariable("s2")); +System.out.println("Hello world BPMN2."); +kcontext.setVariable("s2","Hello, "+kcontext.getVariable("s1")); +System.out.println("Now, s1: "+kcontext.getVariable("s1")); +System.out.println("Now, s2: "+kcontext.getVariable("s2")); + + + _AE824A19-D749-4F9F-9830-0A3DCF62C763 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _BxmcoFRgEDmhHcOk7x80sA + _BxmcoFRgEDmhHcOk7x80sA + + \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/pom.xml b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/pom.xml new file mode 100644 index 0000000000..6bdc6cf1e0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/pom.xml @@ -0,0 +1,111 @@ + + + + 4.0.0 + com.company + simple-dmn + 1.0-SNAPSHOT + + Simple DMN project + + + @version.io.quarkus@ + @project.version@ + 2.22.2 + + UTF-8 + + + + + + org.kie.kogito + kogito-quarkus-bom + ${kogito.version} + pom + import + + + + + + + org.jbpm + jbpm-with-drools-quarkus + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-arc + + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + @version.compiler.plugin@ + + 11 + + + + io.quarkus + quarkus-maven-plugin + ${quarkus.version} + + + + build + + + + + + + + diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/java/control/RestControl.java b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/java/control/RestControl.java new file mode 100644 index 0000000000..2e0a9279f2 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/java/control/RestControl.java @@ -0,0 +1,36 @@ +/* + * 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 control; + +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import java.util.UUID; + +@Path("/control") +public class RestControl { + + @GET + @Produces(MediaType.TEXT_PLAIN) + public String hello() { + return "Hello, "+ "v1"; + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/application.properties b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/application.properties new file mode 100644 index 0000000000..1c385db7f3 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/application.properties @@ -0,0 +1,20 @@ +# +# 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. +# + +quarkus.kogito.devservices.enabled=false \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/hello.dmn b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/hello.dmn new file mode 100644 index 0000000000..5a4a8d55f4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-integration-test-maven-devmode/src/test/resources/projects/simple-dmn/src/main/resources/hello.dmn @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + "Hello, "+name + + + + + + + + 300 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/pom.xml b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/pom.xml new file mode 100644 index 0000000000..e2a2b5eb27 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/pom.xml @@ -0,0 +1,191 @@ + + + + 4.0.0 + + + org.kie.kogito + apps-integration-tests + 999-SNAPSHOT + + + jbpm-quarkus-integration-test-hot-reload + jBPM :: Quarkus Extension :: Integration Tests (Hot Reload) + + + org.kie.kogito/data-index-service-inmemory:${project.version} + + false + org.kie.kogito.quarkus.processes.hotreload.tests + + + + + + org.kie.kogito + kogito-kie-bom + ${project.version} + pom + import + + + + + + + org.jbpm + jbpm-quarkus + + + org.drools + drools-decisiontables + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + + org.jbpm + jbpm-quarkus-deployment + ${project.version} + pom + test + + + * + * + + + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + org.kie.kogito + kogito-test-utils + test + + + com.github.stephenc.jcip + jcip-annotations + ${version.com.github.stephenc.jcip} + test + + + + + + + + io.quarkus + quarkus-maven-plugin + + true + ${skipTests} + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + + + + + + + + + native + + + native + + + + native + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + + + diff --git a/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTest.java b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTest.java new file mode 100644 index 0000000000..23b67d52c7 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTest.java @@ -0,0 +1,279 @@ +/* + * 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 io.quarkus.it.kogito.process; + +import java.util.Map; + +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.kie.kogito.test.utils.SocketUtils; + +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import net.jcip.annotations.NotThreadSafe; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@NotThreadSafe +public class HotReloadTest { + + static { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + private static final String PACKAGE = "io.quarkus.it.kogito.jbpm"; + private static final String PACKAGE_FOLDER = PACKAGE.replace('.', '/'); + private static final String RESOURCE_FILE = PACKAGE_FOLDER + "/text-process.bpmn"; + + private static final String PROCESS_NAME = "text_process"; + + final static int httpPort = SocketUtils.findAvailablePort(); + + @RegisterExtension + final static QuarkusDevModeTest test = new QuarkusDevModeTest() + .setArchiveProducer( + () -> ShrinkWrap + .create(JavaArchive.class) + .addClass(HotReloadTestHelper.class) + .addAsResource(new StringAsset("quarkus.kogito.devservices.enabled=false\nquarkus.http.port=" + httpPort), "application.properties") + .addAsResource("text-process.bpmn", RESOURCE_FILE)); + + @Test + @SuppressWarnings("unchecked") + public void testJavaFileChange() { + String payload = "{\"mytext\": \"HeLlO\"}"; + + String id = + given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body(payload) + .when() + .post("/" + PROCESS_NAME) + .then() + .statusCode(201) + .header("Location", notNullValue()) + .body("id", notNullValue()) + .extract() + .path("id"); + + Map result = given() + .baseUri("http://localhost:" + httpPort) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/{id}", id) + .then() + .statusCode(200) + .extract() + .as(Map.class); + + assertEquals(2, result.size()); + assertEquals("HELLO", result.get("mytext")); + + test.modifySourceFile(HotReloadTestHelper.class, s -> s.replace("toUpperCase", "toLowerCase")); + + id = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body(payload) + .when() + .post("/" + PROCESS_NAME) + .then() + .statusCode(201) + .body("id", notNullValue()) + .header("Location", notNullValue()) + .extract() + .path("id"); + + result = given() + .baseUri("http://localhost:" + httpPort) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/{id}", id) + .then() + .statusCode(200) + .extract() + .as(Map.class); + + assertEquals(2, result.size()); + assertEquals("hello", result.get("mytext")); + } + + @Test + @SuppressWarnings("unchecked") + public void testRenameProcess() { + String payload = "{\"mytext\": \"HeLlO\"}"; + + String id = + given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body(payload) + .when() + .post("/" + PROCESS_NAME) + .then() + .statusCode(201) + .header("Location", notNullValue()) + .body("id", notNullValue()) + .extract() + .path("id"); + + Map result = given() + .baseUri("http://localhost:" + httpPort) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/{id}", id) + .then() + .statusCode(200) + .extract() + .as(Map.class); + + assertEquals(2, result.size()); + assertEquals("HELLO", result.get("mytext")); + + test.modifyResourceFile(RESOURCE_FILE, s -> s.replaceAll(PROCESS_NAME, "new_" + PROCESS_NAME)); + + id = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body(payload) + .when() + .post("/new_" + PROCESS_NAME) + .then() + .statusCode(201) + .body("id", notNullValue()) + .header("Location", notNullValue()) + .extract() + .path("id"); + + result = given() + .baseUri("http://localhost:" + httpPort) + .accept(ContentType.JSON) + .when() + .get("/new_" + PROCESS_NAME + "/{id}", id) + .then() + .statusCode(200) + .extract() + .as(Map.class); + + assertEquals(2, result.size()); + assertEquals("HELLO", result.get("mytext")); + } + + @Test + public void testProcessJsonSchema() { + String jsonSchema = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/schema") + .then() + .statusCode(200) + .extract().body().asString(); + + assertNotNull(jsonSchema); + assertFalse(jsonSchema.isEmpty()); + + test.modifyResourceFile(RESOURCE_FILE, s -> s.replaceAll(PROCESS_NAME, "new_" + PROCESS_NAME)); + + // old endpoint should not work anymore + given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/schema") + .then() + .statusCode(404); + + String newJsonSchema = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/new_" + PROCESS_NAME + "/schema") + .then() + .statusCode(200) + .extract().body().asString(); + + assertNotNull(newJsonSchema); + assertFalse(newJsonSchema.isEmpty()); + + assertEquals(jsonSchema, newJsonSchema); + } + + @Test + public void testUserTaskJsonSchema() { + String jsonSchema = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/Task/schema") + .then() + .statusCode(200) + .extract().body().asString(); + + assertNotNull(jsonSchema); + assertFalse(jsonSchema.isEmpty()); + + // rename Task name () + test.modifyResourceFile(RESOURCE_FILE, s -> s.replaceAll("\\[Task]", "[Task1]")); + + // old endpoint should not work anymore + given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/Task/schema") + .then() + .statusCode(404); + + String newJsonSchema = given() + .baseUri("http://localhost:" + httpPort) + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/" + PROCESS_NAME + "/Task1/schema") + .then() + .statusCode(200) + .extract().body().asString(); + + assertNotNull(newJsonSchema); + assertFalse(newJsonSchema.isEmpty()); + + assertEquals(jsonSchema, newJsonSchema); + } + +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTestHelper.java b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTestHelper.java new file mode 100644 index 0000000000..4b3aad3e64 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/java/io/quarkus/it/kogito/process/HotReloadTestHelper.java @@ -0,0 +1,36 @@ +/* + * 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 io.quarkus.it.kogito.process; + +import jakarta.enterprise.context.ApplicationScoped; + +/** + * HotReloadTestHelper + */ +@ApplicationScoped +public class HotReloadTestHelper { + + public String toUpper(String text) { + return text.toUpperCase(); + } + + public String toLower(String text) { + return text.toLowerCase(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/resources/text-process.bpmn b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/resources/text-process.bpmn new file mode 100644 index 0000000000..f5c54e7b6b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-processes-integration-test-hot-reload/src/test/resources/text-process.bpmn @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _76FA3693-DC6C-49FB-9C00-7DF5A70634E5 + + + + + + + + _169465F7-5A54-454D-A355-EBB7AE4C3532 + _76FA3693-DC6C-49FB-9C00-7DF5A70634E5 + + + + + _6B621BB3-56F3-43DF-B6C5-D61599190E18_TaskNameInputX + _6B621BB3-56F3-43DF-B6C5-D61599190E18_SkippableInputX + + + + _6B621BB3-56F3-43DF-B6C5-D61599190E18_TaskNameInputX + + + + + + + _6B621BB3-56F3-43DF-B6C5-D61599190E18_SkippableInputX + + + + + + + + + + + + + _00AB4A77-D70F-4086-8BA6-57DD017A5323 + _169465F7-5A54-454D-A355-EBB7AE4C3532 + + + + + _3CDC6E61-DCC5-4831-8BBB-417CFF517CB0_textInputX + + + _3CDC6E61-DCC5-4831-8BBB-417CFF517CB0_resultOutputX + + + + mytext + _3CDC6E61-DCC5-4831-8BBB-417CFF517CB0_textInputX + + + _3CDC6E61-DCC5-4831-8BBB-417CFF517CB0_resultOutputX + mytext + + + + _00AB4A77-D70F-4086-8BA6-57DD017A5323 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _qsJkULktEDiq6fqGoeQh5Q + _qsJkULktEDiq6fqGoeQh5Q + + \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/.gitignore b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/.gitignore new file mode 100644 index 0000000000..203c7bec29 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/.gitignore @@ -0,0 +1,20 @@ +### +# 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. +### + +src/main/resources/shared \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/pom.xml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/pom.xml new file mode 100644 index 0000000000..dd4f0e9059 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/pom.xml @@ -0,0 +1,379 @@ + + + + + + org.kie.kogito + apps-integration-tests + 999-SNAPSHOT + + 4.0.0 + + sonataflow-quarkus-integration-test + SonataFlow :: Quarkus Serverless Workflow Extension :: Integration Tests + + + org.kie.kogito/data-index-service-inmemory:${project.version} + true + + ${project.root.dir}/kogito-serverless-workflow/kogito-serverless-workflow-builder/src/test/resources + ${project.root.dir}/kogito-serverless-workflow/kogito-serverless-workflow-openapi-parser/src/test/resources + org.kie.kogito.quarkus.workflows.tests + + + + + + org.apache.kie.sonataflow + sonataflow-quarkus + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-oidc-client-filter + + + io.quarkus + quarkus-smallrye-reactive-messaging-kafka + + + + org.kie + kie-addons-quarkus-persistence-jdbc + + + io.quarkus + quarkus-agroal + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-grpc-codegen + + + + org.kie + kie-addons-quarkus-events-process + + + io.quarkus + quarkus-elytron-security-properties-file + + + org.kie + kie-addons-quarkus-process-management + + + + org.kie.kogito + kogito-quarkus-test-utils + test + + + org.kie.kogito + kogito-test-utils + test + + + io.quarkus + quarkus-devtools-testing + test + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + com.github.tomakehurst + wiremock-jre8 + test + + + org.assertj + assertj-core + test + + + org.awaitility + awaitility + test + + + io.swagger.parser.v3 + swagger-parser + test + + + + + org.apache.kie.sonataflow + sonataflow-quarkus-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie + kie-addons-quarkus-persistence-jdbc-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie + kie-addons-quarkus-events-process-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie + kie-addons-quarkus-process-management-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie.kogito + kogito-serverless-workflow-builder + + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + grpc-java + io.grpc:protoc-gen-grpc-java:${version.io.grpc}:exe:${os.detected.classifier} + ${project.build.directory}/generated-sources/grpc + + + quarkus-grpc-protoc-plugin + io.quarkus + quarkus-grpc-protoc-plugin + ${version.io.quarkus} + io.quarkus.grpc.protoc.plugin.MutinyGrpcGenerator + + + + + + generate-sources + + compile + compile-custom + + + + + + + org.codehaus.mojo + exec-maven-plugin + + + post-process-grpc + generate-sources + + java + + + false + true + + ${project.build.directory}/generated-sources/grpc + + io.quarkus.grpc.deployment.GrpcPostProcessing + + + + + + io.quarkus + quarkus-maven-plugin + ${version.io.quarkus} + true + + ${skipTests} + + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.jboss.logmanager.LogManager + ${container.image.kafka} + ${project.version} + + + + + + maven-resources-plugin + + + generate-resources + + copy-resources + + + ${project.basedir}/src/main/resources/shared + + + ${shared.test.resources.dir}/exec + + switch-state-data-condition-transition.sw.json + switch-state-data-condition-end.sw.json + switch-state-event-condition-timeouts-transition.sw.json + switch-state-event-condition-timeouts-transition2.sw.json + switch-state-event-condition-timeouts-end.sw.json + multiple-timer-instances-event-state-timeouts.sw.json + + + + ${callback.test.resources.dir}/exec + + callback-state.sw.json + callback-state-timeouts.sw.json + + + + + + + + + maven-clean-plugin + + + + ${project.basedir}/src/main/resources/shared + + + + + + + + + + native + + + native + + + + native + + + + productized + + + productized + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + **/ServerlessWorkflowCodestartTest.java + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/ServerlessWorkflowCodestartTest.java + + + + + + + + + diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePerson.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePerson.java new file mode 100644 index 0000000000..e32b402fb6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePerson.java @@ -0,0 +1,79 @@ +/* + * 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.workflows.services; + +import java.util.Date; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class AgePerson extends Person { + + private int age; + private double income; + private Date creationDate; + + private BasicDataPerson basicDataPerson; + + public AgePerson() { + } + + public AgePerson(String name, int age, double income) { + this(name, age, income, null); + } + + public AgePerson(String name, int age, double income, Date creationDate) { + super(name); + this.age = age; + this.income = income; + this.creationDate = creationDate; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public double getIncome() { + return income; + } + + public void setIncome(double income) { + this.income = income; + } + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public BasicDataPerson getBasicDataPerson() { + return basicDataPerson; + } + + public void setBasicDataPerson(BasicDataPerson basicDataPerson) { + this.basicDataPerson = basicDataPerson; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePersonService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePersonService.java new file mode 100644 index 0000000000..91a4f9e910 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/AgePersonService.java @@ -0,0 +1,40 @@ +/* + * 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.workflows.services; + +import java.util.Date; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class AgePersonService { + + public AgePerson from(String name, int age, double income) { + return new AgePerson(name, age, income); + } + + public AgePerson createFrom(String name, int intValue, double income, long dateValue, + String cardId, double discount, Boolean enabled) { + Date receivedCreation = new Date(dateValue); + Date receivedBirth = new Date(dateValue); + AgePerson agePerson = new AgePerson(name, intValue, income, receivedCreation); + agePerson.setBasicDataPerson(new BasicDataPerson(cardId, discount, intValue, enabled, receivedBirth)); + return agePerson; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/BasicDataPerson.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/BasicDataPerson.java new file mode 100644 index 0000000000..f778c64ded --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/BasicDataPerson.java @@ -0,0 +1,84 @@ +/* + * 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.workflows.services; + +import java.util.Date; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class BasicDataPerson { + + private String cardId; + private double discount; + private int count; + private Boolean enabled; + private Date birthDate; + + public BasicDataPerson() { + } + + public BasicDataPerson(String cardId, double discount, int count, Boolean enabled, Date birthDate) { + this.cardId = cardId; + this.discount = discount; + this.count = count; + this.enabled = enabled; + this.birthDate = birthDate; + } + + public String getCardId() { + return cardId; + } + + public void setCardId(String cardId) { + this.cardId = cardId; + } + + public double getDiscount() { + return discount; + } + + public void setDiscount(double discount) { + this.discount = discount; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Date getBirthDate() { + return birthDate; + } + + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomFunction.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomFunction.java new file mode 100644 index 0000000000..339a8e5a7e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomFunction.java @@ -0,0 +1,45 @@ +/* + * 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.workflows.services; + +import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; +import org.jbpm.ruleflow.core.factory.ActionNodeFactory; +import org.kie.kogito.jackson.utils.ObjectMapperFactory; +import org.kie.kogito.serverless.workflow.functions.ActionFunctionNamespace; +import org.kie.kogito.serverless.workflow.parser.VariableInfo; +import org.kie.kogito.serverless.workflow.suppliers.InjectActionSupplier; + +import io.serverlessworkflow.api.Workflow; +import io.serverlessworkflow.api.functions.FunctionRef; + +public class DummyAnsibleCustomFunction extends ActionFunctionNamespace { + + @Override + public String namespace() { + return "ansible"; + } + + @Override + protected > ActionNodeFactory fillAction(Workflow workflow, + ActionNodeFactory node, + FunctionRef functionRef, + VariableInfo varInfo) { + return node.action(new InjectActionSupplier(ObjectMapperFactory.get().createObjectNode().put("name", "John"))); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomType.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomType.java new file mode 100644 index 0000000000..4f304ef5e2 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyAnsibleCustomType.java @@ -0,0 +1,47 @@ +/* + * 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.workflows.services; + +import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; +import org.jbpm.ruleflow.core.factory.ActionNodeFactory; +import org.kie.kogito.jackson.utils.ObjectMapperFactory; +import org.kie.kogito.serverless.workflow.parser.VariableInfo; +import org.kie.kogito.serverless.workflow.parser.types.ActionTypeHandler; +import org.kie.kogito.serverless.workflow.suppliers.InjectActionSupplier; + +import io.serverlessworkflow.api.Workflow; +import io.serverlessworkflow.api.functions.FunctionDefinition; +import io.serverlessworkflow.api.functions.FunctionRef; + +public class DummyAnsibleCustomType extends ActionTypeHandler { + + @Override + public String type() { + return "ansible"; + } + + @Override + protected > ActionNodeFactory fillAction(Workflow workflow, + ActionNodeFactory node, + FunctionDefinition functionDef, + FunctionRef functionRef, + VariableInfo varInfo) { + return node.action(new InjectActionSupplier(ObjectMapperFactory.get().createObjectNode().put("name", "John"))); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyRPCCustomType.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyRPCCustomType.java new file mode 100644 index 0000000000..e8bc7bdaff --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/DummyRPCCustomType.java @@ -0,0 +1,48 @@ +/* + * 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.workflows.services; + +import org.jbpm.ruleflow.core.RuleFlowNodeContainerFactory; +import org.jbpm.ruleflow.core.factory.WorkItemNodeFactory; +import org.kie.kogito.serverless.workflow.parser.ParserContext; +import org.kie.kogito.serverless.workflow.parser.types.WorkItemTypeHandler; + +import io.serverlessworkflow.api.Workflow; +import io.serverlessworkflow.api.functions.FunctionDefinition; + +import static org.kie.kogito.serverless.workflow.parser.FunctionTypeHandlerFactory.trimCustomOperation; +import static org.kie.kogito.workflows.services.RPCCustomWorkItemHandler.NAME; +import static org.kie.kogito.workflows.services.RPCCustomWorkItemHandler.OPERATION; + +public class DummyRPCCustomType extends WorkItemTypeHandler { + + @Override + public String type() { + return "rpc"; + } + + @Override + protected > WorkItemNodeFactory fillWorkItemHandler(Workflow workflow, + ParserContext context, + WorkItemNodeFactory node, + FunctionDefinition functionDef) { + return node.workName(NAME).metaData(OPERATION, trimCustomOperation(functionDef)); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/EvenService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/EvenService.java new file mode 100644 index 0000000000..13f31b359c --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/EvenService.java @@ -0,0 +1,31 @@ +/* + * 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.workflows.services; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class EvenService { + + public void isEven(int number) { + if (number % 2 != 0) { + throw new IllegalArgumentException("Odd situation"); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreetResource.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreetResource.java new file mode 100644 index 0000000000..9f97d68aea --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreetResource.java @@ -0,0 +1,47 @@ +/* + * 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.workflows.services; + +import java.util.Map; + +import org.kie.kogito.Model; +import org.kie.kogito.process.Process; + +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +@Path("/greetdetails") +public class GreetResource { + + @Inject + @Named("greet") + Process process; + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getWorkflowType() { + return Response.ok().entity(Map.of("type", process.type(), "version", process.version())).build(); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreeterService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreeterService.java new file mode 100644 index 0000000000..5c1536bc1a --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/GreeterService.java @@ -0,0 +1,74 @@ +/* + * 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.workflows.services; + +import org.kie.kogito.examples.sw.greeting.Greeter; +import org.kie.kogito.examples.sw.greeting.HelloArrayReply; +import org.kie.kogito.examples.sw.greeting.HelloArrayReply.Builder; +import org.kie.kogito.examples.sw.greeting.HelloArrayRequest; +import org.kie.kogito.examples.sw.greeting.HelloReply; +import org.kie.kogito.examples.sw.greeting.HelloRequest; +import org.kie.kogito.examples.sw.greeting.InnerMessage; +import org.kie.kogito.examples.sw.greeting.State; + +import com.google.protobuf.Empty; + +import io.quarkus.grpc.GrpcService; +import io.smallrye.mutiny.Uni; + +@GrpcService +public class GreeterService implements Greeter { + @Override + public Uni sayHello(HelloRequest request) { + return Uni.createFrom().item(() -> buildReply(request)); + } + + @Override + public Uni doNothing(Empty request) { + return Uni.createFrom().item(Empty.getDefaultInstance()); + } + + @Override + public Uni sayHelloArray(HelloArrayRequest request) { + Builder reply = HelloArrayReply.newBuilder(); + request.getRequestsList().forEach(r -> reply.addReplies(buildReply(r))); + return Uni.createFrom().item(reply.build()); + + } + + private HelloReply buildReply(HelloRequest request) { + String message; + switch (request.getLanguage().toLowerCase()) { + case "spanish": + message = "Saludos desde gRPC service " + request.getName(); + break; + case "italian": + message = "Boungiorno " + request.getName(); + break; + case "catalan": + message = "Bon dia" + request.getName(); + break; + case "english": + default: + message = "Hello from gRPC service " + request.getName(); + } + return HelloReply.newBuilder().setMessage(message).setState(request.getInnerHello().getUnknown() ? State.UNKNOWN : State.SUCCESS) + .setInnerMessage(InnerMessage.newBuilder().setNumber(23).build()).build(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataConverter.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataConverter.java new file mode 100644 index 0000000000..de21ca4cf4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataConverter.java @@ -0,0 +1,37 @@ +/* + * 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.workflows.services; + +import java.io.IOException; + +import org.kie.kogito.event.impl.AbstractCloudEventDataConverter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JavaSerializationCloudEventDataConverter extends AbstractCloudEventDataConverter { + + protected JavaSerializationCloudEventDataConverter(ObjectMapper objectMapper, Class targetClass) { + super(objectMapper, targetClass); + } + + @Override + protected O toValue(byte[] bytes) throws IOException { + return JavaSerializationUtils.fromBytes(bytes, targetClass, objectMapper); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataFactory.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataFactory.java new file mode 100644 index 0000000000..d048dbfd7f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationCloudEventDataFactory.java @@ -0,0 +1,41 @@ +/* + * 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.workflows.services; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import org.kie.kogito.event.impl.AbstractCloudEventDataFactory; + +public class JavaSerializationCloudEventDataFactory extends AbstractCloudEventDataFactory { + + @Override + protected byte[] toBytes(T object) throws IOException { + return object instanceof byte[] ? (byte[]) object : convert(object); + } + + protected static byte[] convert(T object) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (ObjectOutputStream out = new ObjectOutputStream(bytes)) { + out.writeObject(object); + } + return bytes.toByteArray(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationMarshaller.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationMarshaller.java new file mode 100644 index 0000000000..89cea83a09 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationMarshaller.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.workflows.services; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Set; +import java.util.function.Function; + +import org.kie.kogito.event.CloudEventMarshaller; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; + +public class JavaSerializationMarshaller implements CloudEventMarshaller { + + @Override + public byte[] marshall(CloudEvent dataEvent) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + try (DataOutputStream out = new DataOutputStream(bytes)) { + out.writeUTF(dataEvent.getSpecVersion().toString()); + out.writeUTF(dataEvent.getId()); + out.writeUTF(dataEvent.getType()); + out.writeUTF(dataEvent.getSource().toString()); + writeOptional(out, dataEvent.getTime()); + writeOptional(out, dataEvent.getSubject()); + writeOptional(out, dataEvent.getDataSchema()); + writeOptional(out, dataEvent.getDataContentType()); + + Set extensionNames = dataEvent.getExtensionNames(); + out.writeShort(extensionNames.size()); + for (String extensionName : extensionNames) { + out.writeUTF(extensionName); + writeOptional(out, dataEvent.getExtension(extensionName)); + } + CloudEventData data = dataEvent.getData(); + if (data != null) { + out.writeBoolean(true); + byte[] dataBytes = data.toBytes(); + out.write(dataBytes, 0, dataBytes.length); + } else { + out.writeBoolean(false); + } + } + return bytes.toByteArray(); + } + + private void writeOptional(DataOutputStream out, Object object) throws IOException { + if (object != null) { + out.writeBoolean(true); + out.writeUTF(object.toString()); + } else { + out.writeBoolean(false); + } + } + + @Override + public Function cloudEventDataFactory() { + return new JavaSerializationCloudEventDataFactory<>(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationProducer.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationProducer.java new file mode 100644 index 0000000000..0331552e79 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationProducer.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.workflows.services; + +import org.kie.kogito.addon.quarkus.messaging.common.ChannelFormat; +import org.kie.kogito.event.CloudEventUnmarshallerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +@ApplicationScoped +public class JavaSerializationProducer { + + @Inject + ObjectMapper objectMapper; + + @Produces + @Named("java") + @ChannelFormat + public CloudEventUnmarshallerFactory getJavaCloudEventUnmarshallerFactory() { + return new JavaSerializationUnmarshallerFactory(objectMapper); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshaller.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshaller.java new file mode 100644 index 0000000000..5bc6bbaf6a --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshaller.java @@ -0,0 +1,99 @@ +/* + * 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.workflows.services; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.net.URI; +import java.time.OffsetDateTime; + +import org.kie.kogito.event.CloudEventUnmarshaller; +import org.kie.kogito.event.Converter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.cloudevents.CloudEvent; +import io.cloudevents.CloudEventData; +import io.cloudevents.SpecVersion; +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.core.data.PojoCloudEventData; +import io.vertx.core.buffer.Buffer; + +public class JavaSerializationUnmarshaller implements CloudEventUnmarshaller { + + private final ObjectMapper objectMapper; + private final Class javaDataClass; + + public JavaSerializationUnmarshaller(ObjectMapper objectMapper, Class javaDataClass) { + this.objectMapper = objectMapper; + this.javaDataClass = javaDataClass; + } + + @Override + public Converter cloudEvent() { + return this::cloudEvent; + } + + private CloudEvent cloudEvent(Object object) throws IOException { + Buffer buffer = (Buffer) object; + try (DataInputStream is = new DataInputStream(new ByteArrayInputStream(buffer.getBytes()))) { + CloudEventBuilder builder = CloudEventBuilder.fromSpecVersion(SpecVersion.parse(is.readUTF())); + builder.withId(is.readUTF()); + builder.withType(is.readUTF()); + builder.withSource(URI.create(is.readUTF())); + if (is.readBoolean()) { + builder.withTime(OffsetDateTime.parse(is.readUTF())); + } + if (is.readBoolean()) { + builder.withSubject(is.readUTF()); + } + if (is.readBoolean()) { + builder.withDataSchema(URI.create(is.readUTF())); + } + if (is.readBoolean()) { + builder.withDataContentType(is.readUTF()); + } + + int numExtensions = is.readShort(); + + while (numExtensions-- > 0) { + String extName = is.readUTF(); + if (is.readBoolean()) { + builder.withExtension(extName, is.readUTF()); + } + } + if (is.readBoolean()) { + builder.withData(is.readAllBytes()); + } + return builder.build(); + } + } + + @Override + public Converter binaryCloudEvent() { + return bytes -> PojoCloudEventData.wrap(JavaSerializationUtils.fromBytes(((Buffer) bytes).getBytes(), javaDataClass, objectMapper), JavaSerializationCloudEventDataFactory::convert); + } + + @Override + public Converter data() { + return new JavaSerializationCloudEventDataConverter<>(objectMapper, javaDataClass); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshallerFactory.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshallerFactory.java new file mode 100644 index 0000000000..ae3539732f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUnmarshallerFactory.java @@ -0,0 +1,38 @@ +/* + * 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.workflows.services; + +import org.kie.kogito.event.CloudEventUnmarshaller; +import org.kie.kogito.event.CloudEventUnmarshallerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JavaSerializationUnmarshallerFactory implements CloudEventUnmarshallerFactory { + + private final ObjectMapper objectMapper; + + public JavaSerializationUnmarshallerFactory(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public CloudEventUnmarshaller unmarshaller(Class targetClass) { + return new JavaSerializationUnmarshaller<>(objectMapper, targetClass); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUtils.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUtils.java new file mode 100644 index 0000000000..2fb9e87152 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/JavaSerializationUtils.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.workflows.services; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +import com.fasterxml.jackson.databind.ObjectMapper; + +class JavaSerializationUtils { + + static T fromBytes(byte[] bytes, Class targetClass, ObjectMapper objectMapper) throws IOException { + try (ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(bytes))) { + Object value = is.readObject(); + if (targetClass.isInstance(value)) { + return targetClass.cast(value); + } else { + return objectMapper.convertValue(value, targetClass); + } + } catch (ClassNotFoundException e) { + throw new IOException(e); + } + } + + private JavaSerializationUtils() { + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/NodeInstanceTriggerEventListener.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/NodeInstanceTriggerEventListener.java new file mode 100644 index 0000000000..26e6c0e98d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/NodeInstanceTriggerEventListener.java @@ -0,0 +1,39 @@ +/* + * 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.workflows.services; + +import org.kie.api.event.process.ProcessNodeLeftEvent; +import org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener; +import org.kie.kogito.internal.process.runtime.KogitoNodeInstance; + +import jakarta.enterprise.context.ApplicationScoped; + +import static java.lang.String.format; + +@ApplicationScoped +public class NodeInstanceTriggerEventListener extends DefaultKogitoProcessEventListener { + + @Override + public void afterNodeLeft(ProcessNodeLeftEvent event) { + KogitoNodeInstance ni = (KogitoNodeInstance) event.getNodeInstance(); + if (ni.getTriggerTime() == null) { + throw new IllegalStateException(format("Node instance for node %s, contains a null trigger time", ni.getNodeName())); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/Person.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/Person.java new file mode 100644 index 0000000000..91eee08b7b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/Person.java @@ -0,0 +1,41 @@ +/* + * 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.workflows.services; + +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection +public class Person { + private String name; + + public Person() { + } + + public Person(String name) { + this.name = name + "Person"; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/PersonService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/PersonService.java new file mode 100644 index 0000000000..36197c0232 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/PersonService.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.workflows.services; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class PersonService { + + public Person from(String name) { + return new Person(name); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandler.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandler.java new file mode 100644 index 0000000000..b49a276508 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandler.java @@ -0,0 +1,47 @@ +/* + * 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.workflows.services; + +import java.util.Map; + +import org.kie.kogito.internal.process.runtime.KogitoWorkItem; +import org.kie.kogito.serverless.workflow.WorkflowWorkItemHandler; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class RPCCustomWorkItemHandler extends WorkflowWorkItemHandler { + + public static final String NAME = "RPCCustomWorkItemHandler"; + public static final String OPERATION = "operation"; + + @Override + protected Object internalExecute(KogitoWorkItem workItem, Map parameters) { + String operationId = (String) workItem.getNodeInstance().getNode().getMetaData().get(OPERATION); + if (!"division".equals(operationId)) { + throw new IllegalArgumentException("Operation " + operationId + " is not supported"); + } + return (Integer) parameters.get("dividend") / (Integer) parameters.get("divisor"); + } + + @Override + public String getName() { + return NAME; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandlerConfig.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandlerConfig.java new file mode 100644 index 0000000000..317cb16279 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/RPCCustomWorkItemHandlerConfig.java @@ -0,0 +1,37 @@ +/* + * 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.workflows.services; + +import org.kie.kogito.process.impl.CachedWorkItemHandlerConfig; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +@ApplicationScoped +public class RPCCustomWorkItemHandlerConfig extends CachedWorkItemHandlerConfig { + + @Inject + RPCCustomWorkItemHandler handler; + + @PostConstruct + void init() { + register(handler.getName(), handler); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/SquareService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/SquareService.java new file mode 100644 index 0000000000..f72348a510 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/java/org/kie/kogito/workflows/services/SquareService.java @@ -0,0 +1,36 @@ +/* + * 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.workflows.services; + +import java.util.Collection; +import java.util.stream.Collectors; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class SquareService { + + public Collection squareAll(Collection numbers) { + return numbers.stream().map(this::squareOne).collect(Collectors.toList()); + } + + public Integer squareOne(Integer x) { + return x * x; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/.greet-hidden.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/.greet-hidden.sw.json new file mode 100644 index 0000000000..ab4d758e68 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/.greet-hidden.sw.json @@ -0,0 +1,71 @@ +{ + "id": "greethidden", + "version": "1.0", + "name": "Greeting workflow", + "expressionLang": "jsonpath", + "description": "JSON based greeting workflow, but in a hidden file", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + }, + { + "name": "isEnglish", + "type": "expression", + "operation" : "$.[?(@.language == 'English')]" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "fn:isEnglish", + "transition": "GreetInEnglish" + }, + { + "condition": "${ $.[?(@.language == 'Spanish')] }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "GreetInEnglish" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": "$.greeting $.name" + } + } + } + ], + "end": "true" + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionNamespace b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionNamespace new file mode 100644 index 0000000000..5c85f4f7eb --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionNamespace @@ -0,0 +1,20 @@ +# +# 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. +# + +org.kie.kogito.workflows.services.DummyAnsibleCustomFunction \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler new file mode 100644 index 0000000000..6907fe95d0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/META-INF/services/org.kie.kogito.serverless.workflow.parser.FunctionTypeHandler @@ -0,0 +1,21 @@ +# +# 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. +# + +org.kie.kogito.workflows.services.DummyAnsibleCustomType +org.kie.kogito.workflows.services.DummyRPCCustomType \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties new file mode 100644 index 0000000000..bff2cf96b0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/application.properties @@ -0,0 +1,247 @@ +# +# 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. +# + +quarkus.kogito.logger.always-include=true +quarkus.swagger-ui.always-include=true + +kogito.persistence.type=jdbc +kogito.persistence.proto.marshaller=false +quarkus.datasource.db-kind=postgresql +quarkus.flyway.migrate-at-start=true +quarkus.flyway.clean-at-start=true + +quarkus.http.test-port=0 +quarkus.log.level=INFO +quarkus.log.category."org.kie.kogito.testcontainers".level=WARN +quarkus.log.category."org.apache.kafka".level=WARN + +# To include the greethidden workflow +kogito.codegen.ignoreHiddenFiles=false + +quarkus.kafka.devservices.enabled=false +quarkus.kubernetes-client.devservices.enabled=false + +# OpenApi client properties, see OperationsMockService, which is mocking these two services +quarkus.rest-client.multiplication.url=${multiplication-service-mock.url} +quarkus.rest-client.subtraction.url=${subtraction-service-mock.url} +quarkus.rest-client.array_yaml.url=${array-service-mock.url} + +# OpenApi client properties to access the general purpose external-service, which is mocked by the ExternalServiceMock +quarkus.rest-client.external_service_yaml.url=${external-service-mock.url} + +# OpenApi client properties to access the echo service, which is mocked by the EnumEchoServiceMock +quarkus.rest-client.enum-parameter_yaml.url=${enum-echo-service-mock.url} + +# Error handling properties +kogito.sw.functions.publishPerfectSquare.host=localhost + +mp.messaging.incoming.move.connector=quarkus-http +mp.messaging.incoming.move.path=/move + +mp.messaging.incoming.quiet.connector=quarkus-http +mp.messaging.incoming.quiet.path=/quiet +kogito.addon.messaging.unmarshaller.quiet=java + +mp.messaging.incoming.never.connector=quarkus-http +mp.messaging.incoming.never.path=/never + +mp.messaging.incoming.eventTimeout1.connector=quarkus-http +mp.messaging.incoming.eventTimeout1.path=/eventTimeout1 + +mp.messaging.incoming.eventTimeout2.connector=quarkus-http +mp.messaging.incoming.eventTimeout2.path=/eventTimeout2 + +mp.messaging.incoming.eventTimeout3.connector=quarkus-http +mp.messaging.incoming.eventTimeout3.path=/eventTimeout3 + +mp.messaging.incoming.eventTimeout1Exclusive.connector=quarkus-http +mp.messaging.incoming.eventTimeout1Exclusive.path=/eventTimeout1Exclusive + +mp.messaging.incoming.eventTimeout2Exclusive.connector=quarkus-http +mp.messaging.incoming.eventTimeout2Exclusive.path=/eventTimeout2Exclusive + +mp.messaging.incoming.eventTimeout3Exclusive.connector=quarkus-http +mp.messaging.incoming.eventTimeout3Exclusive.path=/eventTimeout3Exclusive + +mp.messaging.incoming.event1Exclusive.connector=quarkus-http +mp.messaging.incoming.event1Exclusive.path=/event1Exclusive + +mp.messaging.incoming.event2Exclusive.connector=quarkus-http +mp.messaging.incoming.event2Exclusive.path=/event2Exclusive + +mp.messaging.incoming.event3Exclusive.connector=quarkus-http +mp.messaging.incoming.event3Exclusive.path=/event3Exclusive + +expression.my_name=javierito + +# Kafka configuration for the sw tests that produce events +mp.messaging.outgoing.kogito_outgoing_stream.connector=smallrye-kafka +mp.messaging.outgoing.kogito_outgoing_stream.topic=kogito-sw-out-events +mp.messaging.outgoing.kogito_outgoing_stream.value.serializer=org.apache.kafka.common.serialization.StringSerializer + +# kafka configurations for the CallbackStateIT test. +mp.messaging.incoming.callback_state_event_type.connector=smallrye-kafka +mp.messaging.incoming.callback_state_event_type.topic=callback_state_event_type +mp.messaging.incoming.callback_state_event_type.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.callback_state_event_type.group.id=kogito-sw-it +mp.messaging.incoming.callback_state_event_type.auto.offset.reset=earliest + +# kafka configurations for the CallbackStateTimeoutsIT test. +mp.messaging.incoming.callback_state_timeouts_event_type.connector=smallrye-kafka +mp.messaging.incoming.callback_state_timeouts_event_type.topic=callback_state_timeouts_event_type +mp.messaging.incoming.callback_state_timeouts_event_type.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.callback_state_timeouts_event_type.group.id=kogito-sw-it +mp.messaging.incoming.callback_state_timeouts_event_type.auto.offset.reset=earliest + +# kafka configurations for the SwitchStateIT test variants: + +# kafka configurations for the switch_state_event_condition_timeouts_transition SW: +mp.messaging.incoming.visa_approved_in_transition.connector=smallrye-kafka +mp.messaging.incoming.visa_approved_in_transition.topic=visa_approved_topic_transition +mp.messaging.incoming.visa_approved_in_transition.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.visa_denied_in_transition.connector=smallrye-kafka +mp.messaging.incoming.visa_denied_in_transition.topic=visa_denied_topic_transition +mp.messaging.incoming.visa_denied_in_transition.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer + +# kafka configurations for the switch_state_event_condition_timeouts_transition2 SW: +mp.messaging.incoming.visa_approved_in_transition2.connector=smallrye-kafka +mp.messaging.incoming.visa_approved_in_transition2.topic=visa_approved_topic_transition2 +mp.messaging.incoming.visa_approved_in_transition2.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.visa_denied_in_transition2.connector=smallrye-kafka +mp.messaging.incoming.visa_denied_in_transition2.topic=visa_denied_topic_transition2 +mp.messaging.incoming.visa_denied_in_transition2.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer + +# kafka configurations for the switch-state-event-condition-timeouts-end SW: +mp.messaging.incoming.visa_approved_in_condition_end.connector=smallrye-kafka +mp.messaging.incoming.visa_approved_in_condition_end.topic=visa_approved_topic_condition_end +mp.messaging.incoming.visa_approved_in_condition_end.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.visa_denied_in_condition_end.connector=smallrye-kafka +mp.messaging.incoming.visa_denied_in_condition_end.topic=visa_denied_topic_condition_end +mp.messaging.incoming.visa_denied_in_condition_end.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer + +# End of kafka configurations for the SwitchStateIT test variants + +# kafka configurations for the CorrelationIT test. +mp.messaging.incoming.correlation_start_event_type.connector=smallrye-kafka +mp.messaging.incoming.correlation_start_event_type.topic=correlation_start_event_type +mp.messaging.incoming.correlation_start_event_type.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.correlation_start_event_type.group.id=kogito-sw-it +mp.messaging.incoming.correlation_start_event_type.auto.offset.reset=earliest + +mp.messaging.incoming.correlation_event_type.connector=smallrye-kafka +mp.messaging.incoming.correlation_event_type.topic=correlation_event_type +mp.messaging.incoming.correlation_event_type.value.deserializer=org.apache.kafka.common.serialization.StringDeserializer +mp.messaging.incoming.correlation_event_type.group.id=kogito-sw-it +mp.messaging.incoming.correlation_event_type.auto.offset.reset=earliest + +mp.messaging.incoming.kogito_incoming_stream.connector=quarkus-http +mp.messaging.incoming.kogito_incoming_stream.path=/ + + +quarkus.grpc.clients.Greeter.host=localhost + +# Token propagation support test properties, relates to the TokenPropagationIT and the token-propagation.sw.json +# 1) Configure the desired packages for the code generation, this information is basically source +quarkus.openapi-generator.codegen.spec.token_propagation_external_service1_yaml.base-package=org.acme.externalservice1 +quarkus.openapi-generator.codegen.spec.token_propagation_external_service2_yaml.base-package=org.acme.externalservice2 +quarkus.openapi-generator.codegen.spec.token_propagation_external_service3_yaml.base-package=org.acme.externalservice3 +quarkus.openapi-generator.codegen.spec.token_propagation_external_service4_yaml.base-package=org.acme.externalservice4 +quarkus.openapi-generator.codegen.spec.token_propagation_external_service5_yaml.base-package=org.acme.externalservice5 +# 2) Configure the access url for the four services. +quarkus.rest-client.token_propagation_external_service1_yaml.url=${propagation-external-service-mock.url} +quarkus.rest-client.token_propagation_external_service2_yaml.url=${propagation-external-service-mock.url} +quarkus.rest-client.token_propagation_external_service3_yaml.url=${propagation-external-service-mock.url} +quarkus.rest-client.token_propagation_external_service4_yaml.url=${propagation-external-service-mock.url} +quarkus.rest-client.token_propagation_external_service5_yaml.url=${propagation-external-service-mock.url} + +# 3) Configure the different propagation alternatives. +# default propagation for token_propagation_external_service1 invocation +quarkus.openapi-generator.token_propagation_external_service1_yaml.auth.service1_http_bearer.token-propagation=true +# default propagation for token_propagation_external_service2 invocation +quarkus.openapi-generator.token_propagation_external_service2_yaml.auth.service2_oauth2.token-propagation=true +# propagate the token coming in the header SERVICE3_HEADER_TO_PROPAGATE for token_propagation_external_service3 invocation +quarkus.openapi-generator.token_propagation_external_service3_yaml.auth.service3_http_bearer.token-propagation=true +quarkus.openapi-generator.token_propagation_external_service3_yaml.auth.service3_http_bearer.header-name=SERVICE3_HEADER_TO_PROPAGATE +# propagate the token coming in the header SERVICE4_HEADER_TO_PROPAGATE for token_propagation_external_service4 invocation +quarkus.openapi-generator.token_propagation_external_service4_yaml.auth.service4_oauth2.token-propagation=true +quarkus.openapi-generator.token_propagation_external_service4_yaml.auth.service4_oauth2.header-name=SERVICE4_HEADER_TO_PROPAGATE + +# 4) Oidc clients for the services that has oauth2 security. +# Oidc client used by the token_propagation_external_service2 +quarkus.oidc-client.service2_oauth2.auth-server-url=${keycloak.mock.service.url} +quarkus.oidc-client.service2_oauth2.token-path=${keycloak.mock.service.token-path} +quarkus.oidc-client.service2_oauth2.discovery-enabled=false +quarkus.oidc-client.service2_oauth2.client-id=kogito-app +quarkus.oidc-client.service2_oauth2.grant.type=client +quarkus.oidc-client.service2_oauth2.credentials.client-secret.method=basic +quarkus.oidc-client.service2_oauth2.credentials.client-secret.value=secret + +# Oidc client used by the token_propagation_external_service4 +quarkus.oidc-client.service4_oauth2.auth-server-url=${keycloak.mock.service.url} +quarkus.oidc-client.service4_oauth2.token-path=${keycloak.mock.service.token-path} +quarkus.oidc-client.service4_oauth2.discovery-enabled=false +quarkus.oidc-client.service4_oauth2.client-id=kogito-app +quarkus.oidc-client.service4_oauth2.grant.type=client +quarkus.oidc-client.service4_oauth2.credentials.client-secret.method=basic +quarkus.oidc-client.service4_oauth2.credentials.client-secret.value=secret + +# Oidc client used by the token_propagation_external_service5 +quarkus.oidc-client.service5_oauth2.auth-server-url=${keycloak.mock.service.url} +quarkus.oidc-client.service5_oauth2.token-path=${keycloak.mock.service.token-path} +quarkus.oidc-client.service5_oauth2.discovery-enabled=false +quarkus.oidc-client.service5_oauth2.client-id=kogito-app +quarkus.oidc-client.service5_oauth2.grant.type=client +quarkus.oidc-client.service5_oauth2.credentials.client-secret.method=basic +quarkus.oidc-client.service5_oauth2.credentials.client-secret.value=secret + +mp.messaging.outgoing.kogito-processinstances-events.connector=smallrye-kafka +mp.messaging.outgoing.kogito-processinstances-events.topic=kogito-processinstances-events +mp.messaging.outgoing.kogito-processinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.kogito-processinstances-events.group.id=kogito-data-index-it +mp.messaging.outgoing.kogito-processinstances-events.auto.offset.reset=latest + +mp.messaging.outgoing.kogito-usertaskinstances-events.connector=smallrye-kafka +mp.messaging.outgoing.kogito-usertaskinstances-events.topic=kogito-usertaskinstances-events +mp.messaging.outgoing.kogito-usertaskinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.kogito-usertaskinstances-events.group.id=kogito-data-index-it +mp.messaging.outgoing.kogito-usertaskinstances-events.auto.offset.reset=latest + +mp.messaging.outgoing.kogito-variables-events.connector=smallrye-kafka +mp.messaging.outgoing.kogito-variables-events.topic=kogito-variables-events +mp.messaging.outgoing.kogito-variables-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.kogito-variables-events.group.id=kogito-data-index-it +mp.messaging.outgoing.kogito-variables-events.auto.offset.reset=latest + +mp.messaging.outgoing.kogito-processdefinitions-events.connector=smallrye-kafka +mp.messaging.outgoing.kogito-processdefinitions-events.topic=kogito-processdefinitions-events +mp.messaging.outgoing.kogito-processdefinitions-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer +mp.messaging.outgoing.kogito-processdefinitions-events.group.id=kogito-data-index-it +mp.messaging.outgoing.kogito-processdefinitions-events.auto.offset.reset=latest + +quarkus.native.additional-build-args=-H:SerializationConfigurationResources=serialization-config.json +# Maximum Java heap to be used during the native image generation +quarkus.native.native-image-xmx=8g + +quarkus.http.auth.basic=true +quarkus.http.auth.permission.default.paths=/secure/* +quarkus.http.auth.permission.default.policy=authenticated + +quarkus.security.users.embedded.enabled=true +quarkus.security.users.embedded.plain-text=true +quarkus.security.users.embedded.users.buddy=buddy \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncConsumer.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncConsumer.sw.json new file mode 100644 index 0000000000..04b9562d44 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncConsumer.sw.json @@ -0,0 +1,60 @@ +{ + "id": "asyncEventConsumer", + "version": "1.0", + "name": "Workflow async consumer test", + "description": "An test that verifies an async api spec file with a consumer channel is really waiting", + "start": "printWaitMessage", + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + }, + { + "name": "waitForEvent", + "type": "asyncapi", + "operation": "specs/asyncAPI.yaml#wait" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "." + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "operation", + "actions": [ + { + "name": "waitForEvent", + "functionRef": "waitForEvent" + }, + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "." + } + } + } + ] + , + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncPublisher.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncPublisher.sw.json new file mode 100644 index 0000000000..92643e4145 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/asyncPublisher.sw.json @@ -0,0 +1,27 @@ +{ + "id": "asyncEventPublisher", + "version": "1.0", + "name": "Workflow async consumer test", + "description": "An test that verifies an async api spec file with a publish channel is really publishing", + "start": "publishEvent", + "functions": [ + { + "name": "publishEvent", + "type": "asyncapi", + "operation": "specs/asyncAPI.yaml#sendWait" + } + ], + "states": [ + { + "name": "publishEvent", + "type": "operation", + "actions": [ + { + "name": "publishEvent", + "functionRef": "publishEvent" + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json new file mode 100644 index 0000000000..de849da2d9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/callback-state-with-timeouts-error-handler.sw.json @@ -0,0 +1,135 @@ +{ + "id": "callback_state_with_timeouts_error_handler", + "version": "1.0", + "name": "Callback State With Timeouts Error Handler", + "expressionLang": "jsonpath", + "description": "Callback State With Timeouts Error Handler Test", + "start": "CallbackState", + "constants": {"duration":"PT5S"}, + "events": [ + { + "name": "callbackEvent", + "source": "", + "type": "callback_state_timeouts_event_type" + } + ], + "errors": [ + { + "name": "callbackError", + "code": "java.lang.Exception" + }, + { + "name": "timeoutError", + "code": "TimedOut" + } + ], + "functions": [ + { + "name": "callbackFunction", + "type": "rest", + "operation": "classpath:specs/external-service.yaml#sendRequest" + }, + { + "name": "publishSuccess", + "type": "asyncapi", + "operation": "specs/callbackResults.yaml#sendSuccess" + }, + { + "name": "publishTimeoutExpired", + "type": "asyncapi", + "operation": "specs/callbackResults.yaml#sendTimeoutExpiredForCallbackError" + }, + { + "name": "publishFailure", + "type": "asyncapi", + "operation": "specs/callbackResults.yaml#sendFailed" + } + ], + "states": [ + { + "name": "CallbackState", + "type": "callback", + "action": { + "name": "callbackAction", + "functionRef": { + "refName": "callbackFunction", + "arguments": { + "query": "$.query" + } + } + }, + "eventRef": "callbackEvent", + "transition": "PublishSuccess", + "onErrors": [ + { + "errorRef": "callbackError", + "transition": "PublishError" + }, + { + "errorRef": "timeoutError", + "transition": "PublishTimeout" + } + ], + "timeouts": { + "eventTimeout": "$CONST.duration" + } + }, + { + "name": "PublishSuccess", + "type": "operation", + "actions": [ + { + "name": "publishSuccess", + "functionRef": "publishSuccess" + } + ], + "transition": "FinalizeSuccessful" + }, + { + "name": "FinalizeSuccessful", + "type": "inject", + "data": { + "lastExecutedState": "FinalizeSuccessful" + }, + "end": true + }, + { + "name": "PublishTimeout", + "type": "operation", + "actions": [ + { + "name": "publishTimeoutExpired", + "functionRef": "publishTimeoutExpired" + } + ], + "transition": "FinalizeTimeout" + }, + { + "name": "FinalizeTimeout", + "type": "inject", + "data": { + "lastExecutedState": "FinalizeTimeout" + }, + "end": true + }, + { + "name": "PublishError", + "type": "operation", + "actions": [ + { + "name": "publishFailure", + "functionRef": "publishFailure" + } + ], + "transition": "FinalizeWithError" + }, + { + "name": "FinalizeWithError", + "type": "inject", + "data": { + "lastExecutedState": "FinalizeWithError" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/correlation.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/correlation.sw.json new file mode 100644 index 0000000000..66fc582914 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/correlation.sw.json @@ -0,0 +1,72 @@ +{ + "id": "correlation", + "version": "1.0", + "specVersion": "0.8", + "name": "Workflow with correlation", + "description": "An test of correlation", + "start": "printWaitMessage", + "events": [ + { + "name": "startEvent", + "source": "", + "type": "correlation_start_event_type", + "correlation": [ + { + "contextAttributeName": "userid" + } + ] + }, + { + "name": "moveEvent", + "source": "", + "type": "correlation_event_type", + "correlation": [ + { + "contextAttributeName": "userid" + } + ] + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type":"event", + "onEvents": [{ + "eventRefs": ["startEvent"] + }], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "moveEvent" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "." + } + } + } + ] + } + ], + "end": { + "terminate": "true" + } + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customFunction.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customFunction.sw.json new file mode 100644 index 0000000000..aa223f98d6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customFunction.sw.json @@ -0,0 +1,19 @@ +{ + "id": "customFunction", + "version": "1.0", + "name": "Test Custom function", + "description": "This test a custom function can be added in the classpath", + "start": "start", + "states": [ + { + "name": "start", + "type": "operation", + "actions": [ + { + "functionRef": "ansible:doSomething" + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customType.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customType.sw.json new file mode 100644 index 0000000000..63eb22d808 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/customType.sw.json @@ -0,0 +1,26 @@ +{ + "id": "customType", + "version": "1.0", + "name": "Test Custom type", + "description": "This test a custom type can be added as addon in the classpath", + "start": "start", + "functions": [ + { + "name": "doSomething", + "type": "custom", + "operation": "ansible:doSomething" + } + ], + "states": [ + { + "name": "start", + "type": "operation", + "actions": [ + { + "functionRef": "doSomething" + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/division.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/division.sw.json new file mode 100644 index 0000000000..e6be4fecad --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/division.sw.json @@ -0,0 +1,26 @@ +{ + "id": "division", + "version": "1.0", + "name": "Workflow Expression example", + "start": "divide", + "functions": [ + { + "name": "divide", + "type": "expression", + "operation": ".number1 / .number2" + } + ], + "states": [ + { + "name": "divide", + "type": "operation", + "actions": [ + { + "name": "divide", + "functionRef": "divide" + } + ], + "end": true + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/error.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/error.sw.json new file mode 100644 index 0000000000..cd0072acb3 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/error.sw.json @@ -0,0 +1,122 @@ +{ + "id": "error", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow Error example", + "description": "An example of how to handle an exception thrown by a service", + "start": "checkEven", + "errors": [ + { + "name": "odd number", + "code": "Odd situation" + }, + { + "name": "bad request", + "code": "HTTP:400" + } + ], + "functions": [ + { + "name": "isEven", + "type": "custom", + "operation": "service:java:org.kie.kogito.workflows.services.EvenService::isEven" + }, + { + "name": "publishPerfectSquare", + "type": "custom", + "operation": "rest:post:/publish/{type}/{number}" + }, + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "checkEven", + "type": "operation", + "actions": [ + { + "name": "checkEvenAction", + "functionRef": { + "refName": "isEven", + "arguments": { + "number": "$.number" + } + } + } + ], + "transition": "even", + "onErrors": [ + { + "errorRef": "odd number", + "transition": "odd" + } + ] + }, + { + "name": "even", + "type": "inject", + "data": { + "numberType": "even" + }, + "transition": "print" + }, + { + "name": "odd", + "type": "inject", + "data": { + "numberType": "odd" + }, + "transition": "print" + }, + { + "name": "print", + "type": "operation", + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$.numberType" + } + } + } + ], + "transition": "publish" + }, + { + "name": "publish", + "type": "operation", + "actions": [ + { + "name": "publishAction", + "functionRef" : { + "refName": "publishPerfectSquare", + "arguments": { + "type": "$.numberType", + "number": "$.number" + } + } + } + ], + "end": true, + "onErrors": [ + { + "errorRef": "bad request", + "transition": "setError" + } + ] + }, + { + "name": "setError", + "type": "inject", + "data": { + "perfect": "Error invoking publishPerfectSquare" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorGeneric.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorGeneric.sw.json new file mode 100644 index 0000000000..928bce4729 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorGeneric.sw.json @@ -0,0 +1,118 @@ +{ + "id": "errorGeneric", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow Error example", + "description": "An example of how to handle an exception thrown by a service", + "start": "checkEven", + "errors": [ + { + "name": "odd number", + "code": "java.lang.RuntimeException" + } + ], + "functions": [ + { + "name": "isEven", + "type": "custom", + "operation": "service:java:org.kie.kogito.workflows.services.EvenService::isEven" + }, + { + "name": "publishPerfectSquare", + "type": "custom", + "operation": "rest:post:/publish/{type}/{number}" + }, + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "checkEven", + "type": "operation", + "actions": [ + { + "name": "checkEvenAction", + "functionRef": { + "refName": "isEven", + "arguments": { + "number": "$.number" + } + } + } + ], + "transition": "even", + "onErrors": [ + { + "errorRef": "odd number", + "transition": "odd" + } + ] + }, + { + "name": "even", + "type": "inject", + "data": { + "numberType": "even" + }, + "transition": "print" + }, + { + "name": "odd", + "type": "inject", + "data": { + "numberType": "odd" + }, + "transition": "print" + }, + { + "name": "print", + "type": "operation", + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$.numberType" + } + } + } + ], + "transition": "publish" + }, + { + "name": "publish", + "type": "operation", + "actions": [ + { + "name": "publishAction", + "functionRef" : { + "refName": "publishPerfectSquare", + "arguments": { + "type": "$.numberType", + "number": "$.number" + } + } + } + ], + "end": true, + "onErrors": [ + { + "errorRef": "odd number", + "transition": "setError" + } + ] + }, + { + "name": "setError", + "type": "inject", + "data": { + "perfect": "Error invoking publishPerfectSquare" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorRepeated.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorRepeated.sw.json new file mode 100644 index 0000000000..bb8e880457 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorRepeated.sw.json @@ -0,0 +1,126 @@ +{ + "id": "errorRepeated", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow Error example", + "description": "An example of how to handle an exception thrown by a service", + "start": "checkEven", + "errors": [ + { + "name": "odd number", + "code": "Odd situation" + }, + { + "name": "bad request", + "code": "HTTP:400" + } + ], + "functions": [ + { + "name": "isEven", + "type": "custom", + "operation": "service:java:org.kie.kogito.workflows.services.EvenService::isEven" + }, + { + "name": "publishPerfectSquare", + "type": "custom", + "operation": "rest:post:/publish/{type}/{number}" + }, + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "checkEven", + "type": "operation", + "actions": [ + { + "name": "checkEvenAction", + "functionRef": { + "refName": "isEven", + "arguments": { + "number": "$.number" + } + } + } + ], + "transition": "even", + "onErrors": [ + { + "errorRef": "odd number", + "transition": "odd" + }, + { + "errorRef": "bad request", + "transition": "even" + } + ] + }, + { + "name": "even", + "type": "inject", + "data": { + "numberType": "even" + }, + "transition": "print" + }, + { + "name": "odd", + "type": "inject", + "data": { + "numberType": "odd" + }, + "transition": "print" + }, + { + "name": "print", + "type": "operation", + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$.numberType" + } + } + } + ], + "transition": "publish" + }, + { + "name": "publish", + "type": "operation", + "actions": [ + { + "name": "publishAction", + "functionRef" : { + "refName": "publishPerfectSquare", + "arguments": { + "type": "$.numberType", + "number": "$.number" + } + } + } + ], + "end": true, + "onErrors": [ + { + "errorRefs": ["bad request","odd number"], + "transition": "setError" + } + ] + }, + { + "name": "setError", + "type": "inject", + "data": { + "perfect": "Error invoking publishPerfectSquare" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json new file mode 100644 index 0000000000..7a7a9551e9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/errorWithMetadata.sw.json @@ -0,0 +1,18 @@ +{ + "id": "errorWithMetadata", + "version": "1.0", + "name": "Workflow Error example with metadata", + "description": "An example of how to abort a workflow with error given a condition", + "start": "checkEven", + "states": [ + { + "name": "checkEven", + "type": "operation", + "actions": [], + "end" : true, + "metadata": { + "errorMessage": "if .number % 2 != 0 then \"Is Odd number!!!!\" else null end" + } + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow.sw.yml new file mode 100644 index 0000000000..4caa9cd742 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow.sw.yml @@ -0,0 +1,50 @@ +# +# 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. +# + +id: EventFlow1 +name: event flow 1 +version: '1.0' +specVersion: '0.8' +description: TBD +functions: + - name: sout + type: custom + operation: sysout +events: + - name: Event1 + source: org.persistence + type: event1 +start: ShowPIDOfEvent1 +states: + - name: ShowPIDOfEvent1 + type: operation + actions: + - name: showPidOfEvent1Action + functionRef: + refName: sout + arguments: + message: "\"Event1: \" + $WORKFLOW.instanceId" + transition: WaitForCallbackEventFromEvent1 + - name: WaitForCallbackEventFromEvent1 + type: event + onEvents: + - eventRefs: + - Event1 + end: + terminate: true \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow2.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow2.sw.yml new file mode 100644 index 0000000000..61f8558244 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event-flow2.sw.yml @@ -0,0 +1,50 @@ +# +# 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. +# + +id: EventFlow2 +name: event flow 2 +version: '1.0' +specVersion: '0.8' +description: TBD +functions: + - name: sout + type: custom + operation: sysout +events: + - name: Event2 + source: org.persistence + type: event2 +start: ShowPID +states: + - name: ShowPID + type: operation + actions: + - name: showPidAction + functionRef: + refName: sout + arguments: + message: "\"Event2: \" + $WORKFLOW.instanceId" + transition: WaitForCallbackEvent + - name: WaitForCallbackEvent + type: event + onEvents: + - eventRefs: + - Event2 + end: + terminate: true \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event.sw.json new file mode 100644 index 0000000000..ee72a6856f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/event.sw.json @@ -0,0 +1,65 @@ +{ + "id": "nonStartEvent", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of a non starting event", + "start": "printWaitMessage", + "events": [ + { + "name": "moveEvent", + "source": "", + "type": "move" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "moveEvent" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultiple.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultiple.sw.json new file mode 100644 index 0000000000..94c91dc104 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultiple.sw.json @@ -0,0 +1,92 @@ +{ + "id": "nonStartMultipleEvent", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of multiple non starting event", + "start": "printWaitMessage", + "events": [ + { + "name": "quietEvent", + "source": "", + "type": "quiet", + "dataOnly" : "false" + }, + { + "name": "neverArrivingEvent", + "source": "", + "type": "never" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "quietEvent" + ], + "eventDataFilter": { + "data": ".data" + }, + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + }, + { + "eventRefs": [ + "neverArrivingEvent" + ], + "eventDataFilter" : { + "useData": false + }, + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "exclusive": false, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleExclusive.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleExclusive.sw.json new file mode 100644 index 0000000000..804bfd7a3a --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleExclusive.sw.json @@ -0,0 +1,90 @@ +{ + "id": "nonStartMultipleEventExclusive", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of multiple non starting exclusive event", + "start": "printWaitMessage", + "events": [ + { + "name": "event1", + "source": "", + "type": "event1Exclusive" + }, + { + "name": "event2", + "source": "", + "type": "event2Exclusive" + }, + { + "name": "event3", + "source": "", + "type": "event3Exclusive" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "event1", "event3" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + }, + { + "eventRefs": [ + "event2" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "exclusive": true, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeout.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeout.sw.json new file mode 100644 index 0000000000..d79fad7e3f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeout.sw.json @@ -0,0 +1,93 @@ +{ + "id": "nonStartMultipleEventTimeout", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of multiple non starting event with timeout", + "start": "printWaitMessage", + "events": [ + { + "name": "event1", + "source": "", + "type": "eventTimeout1" + }, + { + "name": "event2", + "source": "", + "type": "eventTimeout2" + }, + { + "name": "event3", + "source": "", + "type": "eventTimeout3" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "event1", "event3" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + }, + { + "eventRefs": [ + "event2" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "exclusive": false, + "timeouts": { + "eventTimeout": "PT5S" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeoutExclusive.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeoutExclusive.sw.json new file mode 100644 index 0000000000..e74b5e4ed0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleTimeoutExclusive.sw.json @@ -0,0 +1,93 @@ +{ + "id": "nonStartMultipleEventTimeoutExclusive", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of multiple non starting exclusive event with timeout", + "start": "printWaitMessage", + "events": [ + { + "name": "event1", + "source": "", + "type": "eventTimeout1Exclusive" + }, + { + "name": "event2", + "source": "", + "type": "eventTimeout2Exclusive" + }, + { + "name": "event3", + "source": "", + "type": "eventTimeout3Exclusive" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "event1", "event3" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + }, + { + "eventRefs": [ + "event2" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "exclusive": true, + "timeouts": { + "eventTimeout": "PT5S" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleWorkflowTimeout.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleWorkflowTimeout.sw.json new file mode 100644 index 0000000000..4a45ac679b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventMultipleWorkflowTimeout.sw.json @@ -0,0 +1,97 @@ +{ + "id": "nonStartMultipleEventWorkflowTimeout", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of multiple non starting event", + "start": "printWaitMessage", + "timeouts": { + "workflowExecTimeout": "PT2S" + }, + "events": [ + { + "name": "quietEvent", + "source": "", + "type": "quiet", + "dataOnly" : "false" + }, + { + "name": "neverArrivingEvent", + "source": "", + "type": "never" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "quietEvent" + ], + "eventDataFilter": { + "data": ".data" + }, + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + }, + { + "eventRefs": [ + "neverArrivingEvent" + ], + "eventDataFilter" : { + "useData": false + }, + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "exclusive": false, + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventWithError.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventWithError.sw.json new file mode 100644 index 0000000000..c854ebb8a4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/eventWithError.sw.json @@ -0,0 +1,95 @@ +{ + "id": "eventTimedout", + "version": "1.0", + "expressionLang": "jsonpath", + "name": "Workflow event test", + "description": "An test of a non starting event with timeout error", + "start": "printWaitMessage", + "events": [ + { + "name": "moveEvent", + "source": "", + "type": "move" + } + ], + "errors": [ + { + "name": "timeoutError", + "code": "TimedOut" + } + ], + "functions": [ + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + }, + { + "name": "publishTimeoutExpired", + "type": "asyncapi", + "operation": "specs/callbackResults.yaml#sendTimeoutExpiredError" + } + ] + , + "states": [ + { + "name": "printWaitMessage", + "type": "operation", + "actions": [ + { + "name": "printBeforeEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ], + "transition": "waitForEvent" + }, + { + "name": "waitForEvent", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "moveEvent" + ], + "actions": [ + { + "name": "printAfterEvent", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "$[*]" + } + } + } + ] + } + ], + "onErrors": [ + { + "errorRef": "timeoutError", + "transition": "PublishTimeout" + } + ], + "timeouts": { + "eventTimeout": "PT5S" + }, + "end":true + }, + { + "name": "PublishTimeout", + "type": "operation", + "actions": [ + { + "name": "publishTimeoutExpired", + "functionRef": "publishTimeoutExpired" + } + ], + "end": "true" + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/expression.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/expression.sw.json new file mode 100644 index 0000000000..9a0b96344e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/expression.sw.json @@ -0,0 +1,134 @@ +{ + "id": "expression", + "version": "1.0", + "name": "Workflow Expression example", + "description": "An example of how to use a JQ expression assignment", + "constants" : { + "dog" : { + "castellano" : "perro", + "leones": "perru", + "gallego" : "can", + "aragones" : "cocho", + "catalan" : "gos", + "vasco": "txakurra" + } + }, + "dataInputSchema" : "schema/expressionInput.json", + "extensions" : [ { + "extensionid": "workflow-output-schema", + "outputSchema": "schema/expressionOutput.json" + } + ], + "start": "squareState", + "functions": [ + { + "name": "max", + "type": "expression", + "operation": "{max: .numbers | max_by(.x), min: .numbers | min_by(.y)}" + }, + { + "name": "secretMessage", + "type": "expression", + "operation": ".message |= \"my name is \"+$SECRET.expression.my_name" + }, + { + "name": "constantMessage", + "type": "expression", + "operation": ".message |=.+\" and in my native language dog is translated to \"+$CONST.dog.castellano" + }, + { + "name": "userMessage", + "type": "expression", + "operation": ".user |= $WORKFLOW.identity" + }, + { + "name": "contextMessage", + "type": "expression", + "operation": ".message |=.+\" and the header pepe is \"+$WORKFLOW.headers.pepe" + }, + { + "name": "discardedResult", + "type": "expression", + "operation": ".discardedResult |=\"This string won't be added to the data\"" + }, + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "squareState", + "type": "operation", + "actions": [ + { + "name": "maxAction", + "functionRef": { + "refName": "max" + }, + "actionDataFilter": { + "results" : ".max.x", + "toStateData" : ".number" + } + }, + { + "name": "secretMessageAction", + "functionRef" : { + "refName" : "secretMessage" + } + } + ], + "transition": "finish" + }, + { + "name": "finish", + "type": "operation", + "stateDataFilter": { + "input": "{result: .number, message: .message}" + }, + "actions": [ + { + "name" : "skippedAction", + "functionRef" : "discardedResult", + "sleep" : { + "before" : "PT0.001S", + "after" : "PT0.001S" + }, + "condition": "false" + }, + { + "name": "costantMessageAction", + "functionRef" : "constantMessage" + }, + { + "name": "userMessageAction", + "functionRef" : "userMessage" + }, + { + "name": "contextMessageAction", + "functionRef" : "contextMessage" + }, + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": "\"Result is: \"+(.result|tostring)" + } + } + }, + { + "name": "discardedResultAction", + "functionRef" : "discardedResult", + "actionDataFilter" : { + "useResults" : false + } + } + ], + "end": { + "terminate": true + } + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/fahrenheit-to-celsius.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/fahrenheit-to-celsius.sw.json new file mode 100644 index 0000000000..0c33245fad --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/fahrenheit-to-celsius.sw.json @@ -0,0 +1,62 @@ +{ + "id": "fahrenheit_to_celsius", + "name": "Fahrenheit to Celsius Conversion Flow", + "version": "v1.0", + "start": "SetConstants", + "extensions" : [ { + "extensionid": "workflow-uri-definitions", + "definitions": { + "multiplication": "specs/multiplication.yaml", + "subtraction": "specs/subtraction.yaml" + } + } + ], + "functions": [ + { + "name": "multiplication", + "operation": "multiplication#Do Operation" + }, + { + "name": "subtraction", + "operation": "subtraction#Do Operation" + } + ], + "states": [ + { + "name": "SetConstants", + "type": "inject", + "data": { + "subtractValue": 32.0, + "multiplyValue": 0.5556 + }, + "transition": "Computation" + }, + { + "name": "Computation", + "actionMode": "sequential", + "type": "operation", + "stateDataFilter": { + "output": "${ .[\"celsius\"] = .product | del(.subtractValue, .product, .multiplyValue, .difference) }" + }, + "actions": [ + { + "name": "subtract", + "functionRef": { + "refName": "subtraction", + "arguments": "{leftElement: .fahrenheit, rightElement: .subtractValue}" + } + }, + { + "name": "multiply", + "functionRef": { + "refName": "multiplication", + "arguments": { "pepe":"pepa", "leftElement": ".difference", "rightElement": ".multiplyValue" } + } + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachCustomType.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachCustomType.sw.json new file mode 100644 index 0000000000..dd7cd214ad --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachCustomType.sw.json @@ -0,0 +1,37 @@ +{ + "id": "forEachCustomType", + "version": "1.0", + "name": "For each custom type", + "description": "Test for each state with workitem handler", + "start": "start", + "functions": [ + { + "name": "division", + "type": "custom", + "operation": "rpc:division" + } + ], + "states": [ { + "name": "start", + "type": "foreach", + "iterationParam" : "item", + "inputCollection": ".input", + "outputCollection": ".output", + "actions": [ + { + "functionRef": { + "refName": "division", + "arguments": { + "dividend": ".item", + "divisor" : ".divisor" + } + }, + "actionDataFilter" : { + "results" : ".+1" + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachRest.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachRest.sw.json new file mode 100644 index 0000000000..72cc34f32d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/forEachRest.sw.json @@ -0,0 +1,37 @@ +{ + "id": "forEachRest", + "version": "1.0", + "name": "For each with custom rest", + "description": "Test for each state with rest workitem handler", + "start": "start", + "functions": [ + { + "name": "division", + "type": "custom", + "operation": "rest:get:/division" + } + ], + "states": [ { + "name": "start", + "type": "foreach", + "iterationParam" : "item", + "inputCollection": ".input", + "outputCollection": ".output", + "actions": [ + { + "functionRef": { + "refName": "division", + "arguments": { + "QUERY_dividend": ".item", + "QUERY_divisor" : ".divisor" + } + }, + "actionDataFilter" : { + "results" : ".result+1" + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_child.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_child.sw.json new file mode 100644 index 0000000000..d1b25583f9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_child.sw.json @@ -0,0 +1,34 @@ +{ + "id": "foreach_child", + "version": "1.0", + "specVersion": "0.8", + "name": "Foreach child Workflow", + "description": "Foreach child Workflow Test", + "start": "multiply", + "functions": [ + { + "name": "multiply", + "type": "expression", + "operation": ".number*.constant" + } + ], + "states": [ + { + "name": "multiply", + "type": "operation", + "actions": [ + { + "name": "multiplyAction", + "functionRef": "multiply", + "actionDataFilter": { + "toStateData" : ".response" + } + } + ], + "stateDataFilter": { + "output": ".response" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_parent.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_parent.sw.json new file mode 100644 index 0000000000..403f8d10d8 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/foreach_parent.sw.json @@ -0,0 +1,24 @@ +{ + "id": "foreach_parent", + "version": "1.0", + "specVersion": "0.8", + "name": "For Each Parent", + "description": "For Each Parent Sample", + "start": "For Each", + "states": [ + { + "name": "For Each", + "type": "foreach", + "mode": "sequential", + "inputCollection": ".numbers", + "iterationParam": "number", + "outputCollection": ".products", + "actions": [ + { + "subFlowRef": "foreach_child" + } + ], + "end": true + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greet.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greet.sw.json new file mode 100644 index 0000000000..a849f440e1 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greet.sw.json @@ -0,0 +1,71 @@ +{ + "id": "greet", + "version": "1.0", + "name": "Greeting workflow", + "expressionLang": "jsonpath", + "description": "JSON based greeting workflow", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + }, + { + "name": "isEnglish", + "type": "expression", + "operation" : "$.[?(@.language == 'English')]" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "fn:isEnglish", + "transition": "GreetInEnglish" + }, + { + "condition": "${ $.[?(@.language == 'Spanish')] }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "GreetInEnglish" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": "$.greeting $.name" + } + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greetUnknown.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greetUnknown.sw.json new file mode 100644 index 0000000000..91597035c9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greetUnknown.sw.json @@ -0,0 +1,79 @@ +{ + "id": "greetUnknown", + "version": "1.0", + "name": "Greeting workflow", + "expressionLang": "jsonpath", + "description": "JSON based greeting workflow", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + }, + { + "name": "isEnglish", + "type": "expression", + "operation" : "$.[?(@.language == 'English')]" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "fn:isEnglish", + "transition": "GreetInEnglish" + }, + { + "condition": "${ $.[?(@.language == 'Spanish')] }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "NoGreet" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "NoGreet", + "type": "inject", + "data": { + "greeting": "I'm not familiar with your language," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": "$.greeting $.name" + } + } + } + ], + "end": "true" + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greeting.proto b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greeting.proto new file mode 100644 index 0000000000..1e17e595d6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/greeting.proto @@ -0,0 +1,38 @@ +/** + * 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. + */ + +syntax = "proto3"; + +option java_package="org.kie.kogito.examples.sw.greeting"; + +import "google/protobuf/empty.proto"; +import "message.proto"; + + + +// The greeter service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + + rpc DoNothing (google.protobuf.Empty) returns (google.protobuf.Empty) {} + + rpc SayHelloArray (HelloArrayRequest) returns (HelloArrayReply) {} +} + diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/helloworld.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/helloworld.sw.json new file mode 100644 index 0000000000..8fa30d4fb7 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/helloworld.sw.json @@ -0,0 +1,17 @@ +{ +"id": "helloworld", +"version": "1.0", +"name": "Hello World Workflow", +"description": "Inject Hello World", +"start": "Hello State", +"states":[ + { + "name":"Hello State", + "type":"inject", + "data": { + "result": "Hello World!" + }, + "end": true + } +] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/invalidOutputExpression.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/invalidOutputExpression.sw.json new file mode 100644 index 0000000000..fb1a95555d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/invalidOutputExpression.sw.json @@ -0,0 +1,39 @@ +{ + "id": "invalidOutputExpression", + "version": "1.0", + "name": "Workflow Expression example", + "description": "An example of how to use a JQ expression assignment with wrong output model, missing message", + "dataInputSchema" : "schema/expressionInput.json", + "extensions" : [ { + "extensionid": "workflow-output-schema", + "outputSchema": "schema/expressionOutput.json" + } + ], + "start": "squareState", + "functions": [ + { + "name": "max", + "type": "expression", + "operation": "{max: .numbers | max_by(.x), min: .numbers | min_by(.y)}" + } + ], + "states": [ + { + "name": "squareState", + "type": "operation", + "actions": [ + { + "name": "maxAction", + "functionRef": "max", + "actionDataFilter": { + "results" : ".max.x", + "toStateData" : ".result" + } + } + ], + "end": { + "terminate": true + } + } + ] +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/keepActive.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/keepActive.sw.json new file mode 100644 index 0000000000..dc03f22542 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/keepActive.sw.json @@ -0,0 +1,35 @@ +{ +"id": "keepActive", +"version": "1.0", +"name": "Keep active", +"description": "Keep active dummy test", +"start": "CheckFlag", +"keepActive": true, +"states":[ + { + "name":"CheckFlag", + "type": "switch", + "dataConditions": [ + { + "condition": ".keepActive == true", + "transition": "KeepActive" + } + ], + "defaultCondition": { + "transition": "EndIt" + } + }, + { + "name": "KeepActive", + "type": "inject", + "data": {"message": "this will never end"}, + "end": true + }, + { + "name": "EndIt", + "type": "inject", + "data": {"message": "this will end right now"}, + "end": {"terminate":true} + } +] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/main-flow.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/main-flow.sw.yml new file mode 100644 index 0000000000..5aaa7db82e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/main-flow.sw.yml @@ -0,0 +1,62 @@ +# +# 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. +# + +id: MainFlow +version: '1.0' +specVersion: '0.8' +name: Main flow for ClassCastException +description: TBD +functions: + - name: sout + type: custom + operation: sysout +start: ShowStartMainFlowMessage +states: + - name: ShowStartMainFlowMessage + type: operation + actions: + - name: showStartMainFlowMessageAction + functionRef: + refName: sout + arguments: + message: "\"MainFlow has started: \" + $WORKFLOW.instanceId" + transition: StartSubflow1FromMainFlow + - name: StartSubflow1FromMainFlow + type: operation + actions: + - name: startSubFlow1FromMainFlowAction + subFlowRef: SubFlow1 + transition: StartSubflow3FromMainFlow + - name: StartSubflow3FromMainFlow + type: operation + actions: + - name: startSubFlow3FromMainFlowAction + subFlowRef: SubFlow3 + transition: ShowEndMessageMainFlowMessage + - name: ShowEndMessageMainFlowMessage + type: operation + actions: + - name: showEndMainFlowMessageAction + functionRef: + refName: sout + arguments: + message: "\"MainFlow has ended: \" + $WORKFLOW.instanceId" + end: + terminate: true + diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/message.proto b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/message.proto new file mode 100644 index 0000000000..7164175bb6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/message.proto @@ -0,0 +1,61 @@ +/** + * 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. + */ + +syntax = "proto3"; + +option java_package="org.kie.kogito.examples.sw.greeting"; +option java_multiple_files = true; + +// The request message containing the user's name. +message HelloRequest { + string name = 1; + string language=2; + InnerHello innerHello= 3; +} + +message InnerHello { + bool unknown = 1; +} + +message HelloArrayRequest { + repeated HelloRequest requests = 1; +} + +message HelloArrayReply { + repeated HelloReply replies = 1; +} + +message InnerMessage { + int32 number = 2; + State state = 3; +} + +enum State { + UNKNOWN = 0; + SUCCESS = 1; + ERROR = 2; + } + +// The response message containing the greetings +message HelloReply { + + string message = 1; + State state = 2; + InnerMessage innerMessage = 3; +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openAPIEnumParameter.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openAPIEnumParameter.sw.json new file mode 100644 index 0000000000..589cab4e15 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openAPIEnumParameter.sw.json @@ -0,0 +1,38 @@ +{ + "id": "openapienumparameter", + "name": "OpenAPI Enum Parameter", + "description": "Workflow that sends an Enum parameter to an echo service", + "start": "Start", + "functions": [ + { + "name": "echoFunction", + "operation": "specs/enum-parameter.yaml#echo" + } + ], + "states": [ + { + "name": "Start", + "type": "inject", + "data": { + "msgType": "text" + }, + "transition": "Echo" + }, + { + "name": "Echo", + "type": "operation", + "actions": [ + { + "name": "echoAction", + "functionRef": { + "refName": "echoFunction", + "arguments": { + "msgType": "${ .msgType }" + } + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json new file mode 100644 index 0000000000..32645efec4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/openapiarray.sw.json @@ -0,0 +1,27 @@ +{ + "id": "openapiarray", + "name": "Open API Array Test", + "version": "v1.0", + "start": "DoIt", + "functions": [ + { + "name": "testArray", + "operation": "specs/array.yaml#testArray" + } + ], + "states": [ + { + "name": "DoIt", + "type": "operation", + "actions": [ + { + "name": "testArray", + "functionRef": { + "refName": "testArray", + "arguments": ".inputArray" + } + }], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/parallel.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/parallel.sw.json new file mode 100644 index 0000000000..fd6ce01c2e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/parallel.sw.json @@ -0,0 +1,36 @@ +{ +"id": "parallel", +"version": "1.0", +"name": "Welcome to the Parallel dimension", +"description": "Testing parallelism", +"start": "Parallel", +"functions": [ + { + "name": "concatA", + "type": "expression", + "operation": ".result|=.+\"A\"" + }, + { + "name": "concatB", + "type": "expression", + "operation": ".result|=.+\"B\"" + }, + { + "name": "concatC", + "type": "expression", + "operation": ".result|=.+\"C\"" + } + ], +"states":[ + { + "name":"Parallel", + "type":"parallel", + "branches": [ {"actions": [{"functionRef":"concatA"}] }, {"actions": [{"functionRef":"concatB"}] },{"actions": [{"functionRef":"concatC"}] }], + "numCompleted" : ".numCompleted", + "completionType" : "atLeast", + "end": { + "terminate": true + } + } +] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoService.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoService.sw.json new file mode 100644 index 0000000000..61efa14c63 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoService.sw.json @@ -0,0 +1,61 @@ +{ + "id": "pojoService", + "version": "1.0", + "name": "Workflow test", + "description": "Testing service returning pojo", + "start": "callService", + "functions": [ + { + "name": "getPerson", + "operation": "service:org.kie.kogito.workflows.services.PersonService::from", + "type": "custom" + }, + { + "name": "printMessage", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "callService", + "type": "operation", + + "actions": [ + { + "name": "callAction", + "actionDataFilter" : { + "fromStateData" : "{person : .name}" + }, + "functionRef": { + "refName": "getPerson", + "arguments": { + "name": "${.person}" + } + } + }], + "transition": "finish" + }, + { + "name": "finish", + "type": "operation", + "stateDataFilter" : { + "input" : "${{name : .name}}" + }, + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": ".name" + } + } + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceFilter.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceFilter.sw.json new file mode 100644 index 0000000000..b9be627c4d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceFilter.sw.json @@ -0,0 +1,62 @@ +{ + "id": "pojoServiceFilter", + "version": "1.0", + "name": "Workflow test", + "description": "Testing service returning pojo with filter", + "start": "callService", + "functions": [ + { + "name": "getPerson", + "operation": "service:org.kie.kogito.workflows.services.AgePersonService::from", + "type": "custom" + }, + { + "name": "printMessage", + "operation" : "sysout", + "type" : "custom" + } + ], + "states": [ + { + "name": "callService", + "type": "operation", + "actions": [ + { + "name": "callAction", + "functionRef": { + "refName": "getPerson", + "arguments": { + "name": ".name", + "age": ".age", + "income": 20000.50 + } + }, + "actionDataFilter": { + "results" : "{name: .name}" + } + }], + "stateDataFilter" : { + "output" : "{name: .name}" + }, + "transition": "finish" + }, + { + "name": "finish", + "type": "operation", + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": ".name" + } + } + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceTypes.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceTypes.sw.json new file mode 100644 index 0000000000..7890bc1025 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/pojoServiceTypes.sw.json @@ -0,0 +1,66 @@ +{ + "id": "pojoServiceTypes", + "version": "1.0", + "name": "Workflow test", + "description": "Testing service returning pojo with filter", + "start": "callService", + "functions": [ + { + "name": "getPerson", + "operation": "service:org.kie.kogito.workflows.services.AgePersonService::createFrom", + "type": "custom" + }, + { + "name": "printMessage", + "operation" : "sysout", + "type" : "custom" + } + ], + "states": [ + { + "name": "callService", + "type": "operation", + "actions": [ + { + "name": "callAction", + "functionRef": { + "refName": "getPerson", + "arguments": { + "name": ".name", + "intValue": 1, + "income": 20000.50, + "dateValue": ".creationDate", + "cardId": ".basicDataPerson.cardId", + "discount": ".basicDataPerson.discount", + "enabled": ".basicDataPerson.enabled" + } + }, + "actionDataFilter": { + "results" : "{name: .name, age: .age, income: .income, creationDate: .creationDate, cardId: .basicDataPerson.cardId, discount: .basicDataPerson.discount, count: .basicDataPerson.count, enabled: .basicDataPerson.enabled, birthDate: .basicDataPerson.birthDate}" + } + }], + "stateDataFilter" : { + "output" : "{name: .name, age: .age, income: .income, creationDate: .creationDate, cardId: .basicDataPerson.cardId, discount: .basicDataPerson.discount, count: .basicDataPerson.count, enabled: .basicDataPerson.enabled, birthDate: .basicDataPerson.birthDate}" + }, + "transition": "finish" + }, + { + "name": "finish", + "type": "operation", + "actions": [ + { + "name": "printAction", + "functionRef": { + "refName": "printMessage", + "arguments": { + "message": ".name" + } + } + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/rpcgreet.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/rpcgreet.sw.json new file mode 100644 index 0000000000..6b0615fd0d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/rpcgreet.sw.json @@ -0,0 +1,81 @@ +{ + "id": "rpc-greet", + "version": "1.0", + "name": "Greeting workflow", + "description": "JSON based greeting workflow using grpc", + "start": "GreetPerson", + "functions": [ + { + "name": "sayHello", + "type": "rpc", + "operation": "greeting.proto#Greeter#SayHello" + }, + { + "name": "sayHelloArray", + "type": "rpc", + "operation": "greeting.proto#Greeter#SayHelloArray" + }, + { + "name": "doNothing", + "type": "rpc", + "operation": "greeting.proto#Greeter#DoNothing" + }, + { + "name" : "expression", + "type" : "expression", + "operation" : ".minority[0].message = \"marquitos\"" + } + ], + "states": [ + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "sayHello", + "functionRef" : { + "refName": "sayHello", + "arguments": { + "name": ".name", + "language": ".language", + "innerHello" : {"unknown": ".unknown"} + } + } + }, + { + "functionRef" : "doNothing" + }, + { + "functionRef" : { + "refName": "sayHelloArray", + "arguments": { + "requests" : [ + { + "name": "Marc", + "language": "catalan", + "innerHello" : {"unknown": true} + }, + { + "name": "Marco", + "language": "italian", + "innerHello" : {"unknown": true} + } + ] + } + } + , + "actionDataFilter": { + "results" : ".replies", + "toStateData" : ".minority" + } + }, + { + "functionRef" : "expression" + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionInput.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionInput.json new file mode 100644 index 0000000000..05f1377f6a --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionInput.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Expression Input", + "description": "Input schema for expression test", + "type": "object", + "properties": { + "numbers": { + "description": "The array of numbers to be operated with", + "type": "array", + "items" : { + "type" : "object", + "properties" : { + "x" : {"type":"number"}, + "y" : {"type":"number"} + } + } + } + }, + "required": ["numbers"] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionOutput.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionOutput.json new file mode 100644 index 0000000000..b25317491b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/schema/expressionOutput.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Expression Output", + "description": "Output schema for expression test", + "type": "object", + "properties": { + "result": { + "type" : "number", + "description": "Maximum value on x" + }, + "message" : { + "type": "string", + "description" : "arbitrary message" + } + }, + "required": ["result", "message"] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/secure.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/secure.sw.json new file mode 100644 index 0000000000..e5c584f0b8 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/secure.sw.json @@ -0,0 +1,71 @@ +{ + "id": "secure", + "version": "1.0", + "name": "Greeting workflow", + "expressionLang": "jsonpath", + "description": "JSON based greeting workflow", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + }, + { + "name": "isEnglish", + "type": "expression", + "operation": "$.[?(@.language == 'English')]" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "fn:isEnglish", + "transition": "GreetInEnglish" + }, + { + "condition": "${ $.[?(@.language == 'Spanish')] }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "GreetInEnglish" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow," + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": "$.greeting $.name" + } + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/serialization-config.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/serialization-config.json new file mode 100644 index 0000000000..2e1c7b0650 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/serialization-config.json @@ -0,0 +1,5 @@ +[ + { + "name" : "java.util.Collections$SingletonMap" + } +] \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml new file mode 100644 index 0000000000..233485fd49 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/array.yaml @@ -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. +# + +--- +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /testArray: + post: + operationId: testArray + requestBody: + content: + application/json: + schema: + type: array + items: + type: number + responses: + "200": + description: OK + content: + application/json: + schema: + type: array + items: + type: number diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/asyncAPI.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/asyncAPI.yaml new file mode 100644 index 0000000000..9f0b86e381 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/asyncAPI.yaml @@ -0,0 +1,59 @@ +# +# 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. +# + +asyncapi: '2.0.0' +id: 'oneConsumer' +info: + title: Kafka Application + version: '1.0.0' + description: Kafka Application + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +servers: + production: + url: localhost:9092 + description: Development server + protocol: kafka + protocolVersion: '1.0.0' +channels: + wait: + description: A message channel + subscribe: + operationId: wait + summary: Get messages + message: + $ref: '#/components/messages/message' + publish: + operationId: sendWait + summary: Send messages + message: + $ref: '#/components/messages/message' +components: + messages: + message: + name: message + title: A message + summary: A message + contentType: application/json + payload: + $ref: "#/components/schemas/message" + schemas: + message: + type: object diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/callbackResults.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/callbackResults.yaml new file mode 100644 index 0000000000..be2797bae7 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/callbackResults.yaml @@ -0,0 +1,75 @@ +# +# 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. +# + +asyncapi: '2.0.0' +id: 'callbackResults' +info: + title: Kafka Application + version: '1.0.0' + description: Kafka Application + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +servers: + production: + url: localhost:9092 + description: Development server + protocol: kafka + protocolVersion: '1.0.0' +channels: + success: + description: A message channel for successful completions + publish: + operationId: sendSuccess + summary: Success + message: + $ref: '#/components/messages/message' + timeoutCallbackError: + description: A message channel for callback timeout error + publish: + operationId: sendTimeoutExpiredForCallbackError + summary: Timeout Expired + message: + $ref: '#/components/messages/message' + timeoutError: + description: A message channel for timeout error + publish: + operationId: sendTimeoutExpiredError + summary: Timeout Expired + message: + $ref: '#/components/messages/message' + error: + description: A message channel for failed executions + publish: + operationId: sendFailed + summary: Failed + message: + $ref: '#/components/messages/message' +components: + messages: + message: + name: message + title: A message + summary: A message + contentType: application/json + payload: + $ref: "#/components/schemas/message" + schemas: + message: + type: object diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/enum-parameter.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/enum-parameter.yaml new file mode 100644 index 0000000000..fd47c8df2a --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/enum-parameter.yaml @@ -0,0 +1,57 @@ +# +# 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. +# + +openapi: 3.0.3 +info: + title: echo + version: '1.0.0' + description: "" +paths: + /echo: + post: + summary: Echo + operationId: echo + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Message" + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Echo' +components: + schemas: + Echo: + type: object + properties: + echoedMsgType: + type: string + Message: + type: object + required: + - msgType + properties: + msgType: + type: string + enum: + - 'text' \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/external-service.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/external-service.yaml new file mode 100644 index 0000000000..4fa931c500 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/external-service.yaml @@ -0,0 +1,43 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: external-service API + version: 2.0.0-SNAPSHOT +paths: + /external-service/sendRequest: + post: + operationId: sendRequest + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK +components: + schemas: + QueryRequest: + type: object + properties: + query: + type: string \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/multiplication.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/multiplication.yaml new file mode 100644 index 0000000000..2ac90f7e75 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/multiplication.yaml @@ -0,0 +1,73 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /: + post: + operationId: Do Operation + parameters: + - in: header + name: pepe + schema: + type: string + required: true + - in: query + name: unusedList + required: false + schema: + type: array + items: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/MultiplicationOperation' + responses: + "200": + description: OK + content: + application/json: + schema: + type: object + properties: + product: + format: float + type: number +components: + schemas: + MultiplicationOperation: + type: object + required: [leftElement,rightElement] + properties: + leftElement: + format: float + type: number + rightElement: + format: float + type: number + unusedElement: + type: array + items: + type: string \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/subtraction.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/subtraction.yaml new file mode 100644 index 0000000000..beb8ae59b0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/subtraction.yaml @@ -0,0 +1,55 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: Generated API + version: "1.0" +paths: + /: + post: + operationId: Do Operation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SubtractionOperation' + responses: + "200": + description: OK + content: + application/json: + schema: + type: object + properties: + difference: + format: float + type: number +components: + schemas: + SubtractionOperation: + type: object + properties: + leftElement: + format: float + type: number + rightElement: + format: float + type: number \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service1.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service1.yaml new file mode 100644 index 0000000000..14354fb153 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service1.yaml @@ -0,0 +1,51 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: token-propagation-external-service1 API + version: 2.0.0-SNAPSHOT +paths: + /token-propagation-external-service1/executeQuery1: + post: + operationId: executeQuery1 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK + security: + - service1-http-bearer: [] +components: + schemas: + QueryRequest: + type: object + properties: + processInstanceId: + type: string + query: + type: string + securitySchemes: + service1-http-bearer: + type: http + scheme: bearer \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service2.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service2.yaml new file mode 100644 index 0000000000..3e1494a6e0 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service2.yaml @@ -0,0 +1,55 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: external-service2 API + version: 2.0.0-SNAPSHOT +paths: + /token-propagation-external-service2/executeQuery2: + post: + operationId: executeQuery2 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK + security: + - service2-oauth2: [] +components: + schemas: + QueryRequest: + type: object + properties: + processInstanceId: + type: string + query: + type: string + securitySchemes: + service2-oauth2: + type: oauth2 + flows: + clientCredentials: + authorizationUrl: https://example.com/oauth + tokenUrl: https://example.com/oauth/token + scopes: {} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service3.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service3.yaml new file mode 100644 index 0000000000..73b6880810 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service3.yaml @@ -0,0 +1,51 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: token-propagation-external-service3 API + version: 2.0.0-SNAPSHOT +paths: + /token-propagation-external-service3/executeQuery3: + post: + operationId: executeQuery3 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK + security: + - service3-http-bearer: [] +components: + schemas: + QueryRequest: + type: object + properties: + processInstanceId: + type: string + query: + type: string + securitySchemes: + service3-http-bearer: + type: http + scheme: bearer \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service4.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service4.yaml new file mode 100644 index 0000000000..009342b97e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service4.yaml @@ -0,0 +1,55 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: external-service4 API + version: 2.0.0-SNAPSHOT +paths: + /token-propagation-external-service4/executeQuery4: + post: + operationId: executeQuery4 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK + security: + - service4-oauth2: [] +components: + schemas: + QueryRequest: + type: object + properties: + processInstanceId: + type: string + query: + type: string + securitySchemes: + service4-oauth2: + type: oauth2 + flows: + clientCredentials: + authorizationUrl: https://example.com/oauth + tokenUrl: https://example.com/oauth/token + scopes: {} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service5.yaml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service5.yaml new file mode 100644 index 0000000000..9ca8493ca6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/specs/token-propagation-external-service5.yaml @@ -0,0 +1,55 @@ +# +# 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. +# + +--- +openapi: 3.0.3 +info: + title: external-service5 API + version: 2.0.0-SNAPSHOT +paths: + /token-propagation-external-service5/executeQuery5: + post: + operationId: executeQuery5 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryRequest' + responses: + "200": + description: OK + security: + - service5-oauth2: [] +components: + schemas: + QueryRequest: + type: object + properties: + processInstanceId: + type: string + query: + type: string + securitySchemes: + service5-oauth2: + type: oauth2 + flows: + clientCredentials: + authorizationUrl: https://example.com/oauth + tokenUrl: https://example.com/oauth/token + scopes: {} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/squareService.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/squareService.sw.json new file mode 100644 index 0000000000..13cd307bb7 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/squareService.sw.json @@ -0,0 +1,30 @@ +{ + "id": "squareService", + "version": "1.0", + "specVersion": "0.8", + "name": "Services call with arrays", + "description": "Testing service calls with arrays and expressions", + "start": "CallService", + "functions": [ + { + "name" : "square", + "type": "custom", + "operation": "service:org.kie.kogito.workflows.services.SquareService::squareAll" + } + ], + "states": [ + { + "name": "CallService", + "type": "operation", + "actions": [ + { + "functionRef": { + "refName": "square", + "arguments" : [".first",".second",".third"] + } + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow1.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow1.sw.yml new file mode 100644 index 0000000000..69b900fcb1 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow1.sw.yml @@ -0,0 +1,61 @@ +# +# 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. +# + +id: SubFlow1 +version: '1.0' +specVersion: '0.8' +name: subflow 1 +description: TBD +functions: + - name: sout + type: custom + operation: sysout +start: ShowStartSubFlow1Message +states: + - name: ShowStartSubFlow1Message + type: operation + actions: + - name: showStartSubFlow1MessageAction + functionRef: + refName: sout + arguments: + message: "\"SubFlow1 has started: \" + $WORKFLOW.instanceId" + transition: StartEventFlow1FromSubFlow1 + - name: StartEventFlow1FromSubFlow1 + type: operation + actions: + - name: startEventFlow1FromSubFlow1Action + subFlowRef: EventFlow1 + transition: StartSubflow2FromSubFlow1 + - name: StartSubflow2FromSubFlow1 + type: operation + actions: + - name: startSubflow2FromSubFlow1Action + subFlowRef: SubFlow2 + transition: ShowEndSubFlow1Message + - name: ShowEndSubFlow1Message + type: operation + actions: + - name: showEndSubFlow1MessageAction + functionRef: + refName: sout + arguments: + message: "\"SubFlow1 has ended: \" + $WORKFLOW.instanceId" + end: + terminate: true \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow2.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow2.sw.yml new file mode 100644 index 0000000000..bed0c4ff6f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow2.sw.yml @@ -0,0 +1,55 @@ +# +# 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. +# + +id: SubFlow2 +version: '1.0' +specVersion: '0.8' +name: subflow 2 +description: TBD +functions: + - name: sout + type: custom + operation: sysout +start: ShowStartSubFlow2Message +states: + - name: ShowStartSubFlow2Message + type: operation + actions: + - name: showStartSubFlow2MessageAction + functionRef: + refName: sout + arguments: + message: SubFlow2 has started + transition: StartEventFlow2FromSubFlow2 + - name: StartEventFlow2FromSubFlow2 + type: operation + actions: + - name: startEventFlow2FromSubFlow2Action + subFlowRef: EventFlow2 + transition: ShowEndSubFlow2Message + - name: ShowEndSubFlow2Message + type: operation + actions: + - name: showEndSubFlow2MessageAction + functionRef: + refName: sout + arguments: + message: SubFlow2 has ended + end: + terminate: true \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow3.sw.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow3.sw.yml new file mode 100644 index 0000000000..35d66c14b4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/subflow3.sw.yml @@ -0,0 +1,55 @@ +# +# 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. +# + +id: SubFlow3 +version: '1.0' +specVersion: '0.8' +name: subflow 3 +description: TBD +functions: + - name: sout + type: custom + operation: sysout +start: ShowStartSubFlow3Message +states: + - name: ShowStartSubFlow3Message + type: operation + actions: + - name: showStartSubFlow3MessageAction + functionRef: + refName: sout + arguments: + message: "\"SubFlow3 has started: \" + $WORKFLOW.instanceId" + transition: StartSubflow1FromSubFlow3 + - name: StartSubflow1FromSubFlow3 + type: operation + actions: + - name: startSubflow1FromSubFlow3Action + subFlowRef: SubFlow1 + transition: ShowEndSubFlow3Message + - name: ShowEndSubFlow3Message + type: operation + actions: + - name: showEndSubFlow3MessageAction + functionRef: + refName: sout + arguments: + message: "\"SubFlow3 has ended: \" + $WORKFLOW.instanceId" + end: + terminate: true \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/token-propagation.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/token-propagation.sw.json new file mode 100644 index 0000000000..190eb1fcca --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/main/resources/token-propagation.sw.json @@ -0,0 +1,173 @@ +{ + "id": "token_propagation", + "version": "1.0", + "name": "Token propagation SW", + "description": "Executes different external services at the time it combines the different token propagation options", + "start": "Execution1", + "errors": [ + { + "name": "execution_error", + "code": "jakarta.ws.rs.ProcessingException" + } + ], + "functions": [ + { + "name": "executeQuery1", + "type": "rest", + "operation": "specs/token-propagation-external-service1.yaml#executeQuery1" + }, + { + "name": "executeQuery2", + "type": "rest", + "operation": "specs/token-propagation-external-service2.yaml#executeQuery2" + }, + { + "name": "executeQuery3", + "type": "rest", + "operation": "specs/token-propagation-external-service3.yaml#executeQuery3" + }, + { + "name": "executeQuery4", + "type": "rest", + "operation": "specs/token-propagation-external-service4.yaml#executeQuery4" + }, + { + "name": "executeQuery5", + "type": "rest", + "operation": "specs/token-propagation-external-service5.yaml#executeQuery5" + } + ], + "states": [ + { + "name": "Execution1", + "type": "operation", + "actions": [ + { + "name": "executeQuery1Action", + "functionRef": { + "refName": "executeQuery1", + "arguments": { + "processInstanceId": "$WORKFLOW.instanceId", + "query": ".query" + } + } + } + ], + "transition": "Execution2", + "onErrors": [ + { + "errorRef": "execution_error", + "transition": "EndWithError" + } + ] + }, + { + "name": "Execution2", + "type": "operation", + "actions": [ + { + "name": "executeQuery2Action", + "functionRef": { + "refName": "executeQuery2", + "arguments": { + "processInstanceId": "$WORKFLOW.instanceId", + "query": ".query" + } + } + } + ], + "transition": "Execution3", + "onErrors": [ + { + "errorRef": "execution_error", + "transition": "EndWithError" + } + ] + }, + { + "name": "Execution3", + "type": "operation", + "actions": [ + { + "name": "executeQuery3Action", + "functionRef": { + "refName": "executeQuery3", + "arguments": { + "processInstanceId": "$WORKFLOW.instanceId", + "query": ".query" + } + } + } + ], + "transition": "Execution4", + "onErrors": [ + { + "errorRef": "execution_error", + "transition": "EndWithError" + } + ] + }, + { + "name": "Execution4", + "type": "operation", + "actions": [ + { + "name": "executeQuery4Action", + "functionRef": { + "refName": "executeQuery4", + "arguments": { + "processInstanceId": "$WORKFLOW.instanceId", + "query": ".query" + } + } + } + ], + "transition": "Execution5", + "onErrors": [ + { + "errorRef": "execution_error", + "transition": "EndWithError" + } + ] + }, + { + "name": "Execution5", + "type": "operation", + "actions": [ + { + "name": "executeQuery5Action", + "functionRef": { + "refName": "executeQuery5", + "arguments": { + "processInstanceId": "$WORKFLOW.instanceId", + "query": ".query" + } + } + } + ], + "transition": "End", + "onErrors": [ + { + "errorRef": "execution_error", + "transition": "EndWithError" + } + ] + }, + { + "name": "EndWithError", + "type": "inject", + "data": { + "executionStatus": "Service execution failed" + }, + "transition": "End" + }, + { + "name": "End", + "type": "inject", + "data": { + "executionStatus": "Service execution successful" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/ServerlessWorkflowCodestartTest.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/ServerlessWorkflowCodestartTest.java new file mode 100644 index 0000000000..ffce8e0e5c --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/ServerlessWorkflowCodestartTest.java @@ -0,0 +1,54 @@ +/* + * 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.quarkus; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartData; +import io.quarkus.devtools.testing.codestarts.QuarkusCodestartTest; +import io.quarkus.maven.dependency.ArtifactKey; + +import static io.quarkus.devtools.codestarts.quarkus.QuarkusCodestartCatalog.Language.JAVA; + +public class ServerlessWorkflowCodestartTest { + + @RegisterExtension + public static QuarkusCodestartTest codestartTest = QuarkusCodestartTest.builder() + .setupStandaloneExtensionTest("org.apache.kie.sonataflow:sonataflow-quarkus") + .extension(ArtifactKey.fromString("io.quarkus:quarkus-config-yaml")) + .putData(QuarkusCodestartData.QuarkusDataKey.APP_CONFIG, Map.of("quarkus.devservices.enabled", "false")) + .languages(JAVA) + .build(); + + @Test + void testContent() throws Throwable { + codestartTest.checkGeneratedTestSource("org.acme.GreetTest"); + codestartTest.assertThatGeneratedFileMatchSnapshot(JAVA, "src/main/resources/greet.sw.json"); + codestartTest.assertThatGeneratedFileMatchSnapshot(JAVA, "src/test/resources/application.yml"); + } + + @Test + void buildAllProjectsForLocalUse() throws Throwable { + codestartTest.buildAllProjects(); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractCallbackStateIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractCallbackStateIT.java new file mode 100644 index 0000000000..9b753f9566 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractCallbackStateIT.java @@ -0,0 +1,135 @@ +/* + * 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.quarkus.workflows; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.Map; +import java.util.UUID; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.kie.kogito.event.process.ProcessInstanceVariableDataEvent; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.jackson.JsonCloudEventData; +import io.cloudevents.jackson.JsonFormat; +import io.restassured.path.json.JsonPath; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.quarkus.workflows.ExternalServiceMock.GENERATE_ERROR_QUERY; +import static org.kie.kogito.quarkus.workflows.ExternalServiceMock.SUCCESSFUL_QUERY; +import static org.kie.kogito.quarkus.workflows.WorkflowTestUtils.waitForKogitoProcessInstanceEvent; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceExists; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceNotExists; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstance; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstanceAndGetId; + +abstract class AbstractCallbackStateIT { + + static final String ANSWER = "ANSWER"; + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + String kafkaBootstrapServers; + ObjectMapper objectMapper; + KafkaTestClient kafkaClient; + + @BeforeEach + void setup() { + kafkaClient = new KafkaTestClient(kafkaBootstrapServers); + objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + @AfterEach + void cleanUp() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + String executeCallbackStateSuccessfulPath(String callbackProcessPostUrl, + String callbackProcessGetByIdUrl, + String answer, + String callbackEventType, + String callbackEventTopic) throws Exception { + // start a new process instance by sending the post query and collect the process instance id. + String processInput = buildProcessInput(SUCCESSFUL_QUERY); + String processInstanceId = newProcessInstanceAndGetId(callbackProcessPostUrl, processInput); + + JsonPath processInstanceEventContent = + waitForKogitoProcessInstanceEvent(kafkaClient, ProcessInstanceVariableDataEvent.class, e -> "workflowdata".equals(e.get("data.variableName")), true); + Map workflowDataMap = processInstanceEventContent.getMap("data.variableValue"); + assertThat(workflowDataMap).containsEntry("query", SUCCESSFUL_QUERY); + + // double check that the process instance is there. + assertProcessInstanceExists(callbackProcessGetByIdUrl, processInstanceId); + + // prepare and send the response to the created process via kafka + String response = objectMapper.writeValueAsString(CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(URI.create("")) + .withType(callbackEventType) + .withTime(OffsetDateTime.now()) + .withExtension( + "kogitoprocrefid", processInstanceId) + .withData(JsonCloudEventData.wrap(objectMapper.createObjectNode().put("answer", answer))) + .build()); + kafkaClient.produce(response, callbackEventTopic); + + // give some time for the event to be processed and the process to finish. + assertProcessInstanceHasFinished(callbackProcessGetByIdUrl, processInstanceId, 1, 180); + return processInstanceId; + } + + String executeCallbackStateWithErrorPath(String callbackProcessPostUrl, String callbackProcessGetByIdUrl) throws Exception { + // start a new process instance and collect the results. + String processInput = buildProcessInput(GENERATE_ERROR_QUERY); + JsonPath result = newProcessInstance(callbackProcessPostUrl, processInput); + String processInstanceId = result.get("id"); + // ensure the process has failed as expected since GENERATE_ERROR_QUERY was used. + + String lastExecutedState = result.getString("workflowdata.lastExecutedState"); + assertThat(lastExecutedState).isEqualTo("FinalizeWithError"); + + JsonPath variableLastExecutedStateEventContent = + waitForKogitoProcessInstanceEvent(kafkaClient, ProcessInstanceVariableDataEvent.class, e -> "workflowdata".equals(e.get("data.variableName")), true); + Map lastExecutedStateDataMap = variableLastExecutedStateEventContent.getMap("data.variableValue"); + + assertThat(lastExecutedStateDataMap).containsEntry("lastExecutedState", "FinalizeWithError"); + assertThat(lastExecutedStateDataMap).containsEntry("query", GENERATE_ERROR_QUERY); + + // the process instance should not be there since an end state was reached. + assertProcessInstanceNotExists(callbackProcessGetByIdUrl, processInstanceId); + return processInstanceId; + } + + protected static String buildProcessInput(String query) { + return "{\"workflowdata\": {\"query\": \"" + query + "\"} }"; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractSwitchStateIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractSwitchStateIT.java new file mode 100644 index 0000000000..b1a93ae3fa --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AbstractSwitchStateIT.java @@ -0,0 +1,42 @@ +/* + * 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.quarkus.workflows; + +import io.restassured.path.json.JsonPath; + +import static org.assertj.core.api.Assertions.assertThat; + +abstract class AbstractSwitchStateIT { + + protected static final String DECISION_APPROVED = "Approved"; + protected static final String DECISION_DENIED = "Denied"; + protected static final String DECISION_INVALIDATED = "Invalidated"; + protected static final String DECISION_NO_DECISION = "NoDecision"; + + private static final String DECISION_PATH = "workflowdata.decision"; + + protected static void assertDecision(JsonPath jsonPath, String expectedDecision) { + String currentDecision = jsonPath.get(DECISION_PATH); + assertThat(currentDecision).isEqualTo(expectedDecision); + } + + protected static String buildProcessInput(int age) { + return "{\"workflowdata\": {\"age\": " + age + "} }"; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AssuredTestUtils.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AssuredTestUtils.java new file mode 100644 index 0000000000..b620b2de52 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AssuredTestUtils.java @@ -0,0 +1,102 @@ +/* + * 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.quarkus.workflows; + +import java.net.URI; +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; + +import org.kie.kogito.event.CloudEventMarshaller; +import org.kie.kogito.event.cloudevents.CloudEventExtensionConstants; + +import io.cloudevents.CloudEvent; +import io.cloudevents.core.builder.CloudEventBuilder; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; + +import static io.restassured.RestAssured.given; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.awaitility.Awaitility.await; + +class AssuredTestUtils { + + private AssuredTestUtils() { + } + + static String startProcess(String flowName) { + return startProcess(flowName, Optional.empty()); + } + + static String startProcess(String flowName, Optional businessKey) { + String id = startProcessNoCheck(flowName, businessKey); + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/" + flowName + "/{id}", id) + .then() + .statusCode(200); + return id; + } + + static String startProcessNoCheck(String flowName) { + return startProcessNoCheck(flowName, Optional.empty()); + } + + static String startProcessNoCheck(String flowName, Optional businessKey) { + RequestSpecification body = given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", Collections.emptyMap())); + businessKey.ifPresent(key -> body.queryParam("businessKey", key)); + return body.post("/" + flowName) + .then() + .statusCode(201) + .extract().path("id"); + } + + static void waitForFinish(String flowName, String id, Duration duration) { + await("dead").atMost(duration) + .with().pollInterval(1, SECONDS) + .untilAsserted(() -> given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/" + flowName + "/{id}", id) + .then() + .statusCode(404)); + } + + static CloudEvent buildCloudEvent(String id, Optional businessKey, String type, CloudEventMarshaller marshaller) { + io.cloudevents.core.v1.CloudEventBuilder builder = CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(URI.create("")) + .withType(type) + .withTime(OffsetDateTime.now()) + .withData(marshaller.cloudEventDataFactory().apply(Collections.singletonMap(type, "This has been injected by the event"))); + businessKey.ifPresentOrElse(key -> builder.withExtension(CloudEventExtensionConstants.BUSINESS_KEY, key), () -> builder.withExtension(CloudEventExtensionConstants.PROCESS_REFERENCE_ID, id)); + return builder.build(); + } + + static CloudEvent buildCloudEvent(String id, String type, CloudEventMarshaller marshaller) { + return buildCloudEvent(id, Optional.empty(), type, marshaller); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AsyncAPIIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AsyncAPIIT.java new file mode 100644 index 0000000000..d6e185c2a2 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/AsyncAPIIT.java @@ -0,0 +1,117 @@ +/* + * 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.quarkus.workflows; + +import java.io.IOException; +import java.time.Duration; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.kafka.common.serialization.ByteArrayDeserializer; +import org.apache.kafka.common.serialization.ByteArraySerializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.CloudEventMarshaller; +import org.kie.kogito.event.Converter; +import org.kie.kogito.event.cloudevents.CloudEventExtensionConstants; +import org.kie.kogito.event.impl.ByteArrayCloudEventMarshaller; +import org.kie.kogito.event.impl.ByteArrayCloudEventUnmarshallerFactory; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTypedTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.CloudEvent; +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.buildCloudEvent; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.startProcess; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.startProcessNoCheck; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.waitForFinish; + +@QuarkusIntegrationTest +@QuarkusTestResource(KafkaQuarkusTestResource.class) +class AsyncAPIIT extends AbstractCallbackStateIT { + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + String kafkaBootstrapServers; + private CloudEventMarshaller marshaller; + private ObjectMapper objectMapper; + private KafkaTypedTestClient kafkaClient; + + private final static Logger logger = LoggerFactory.getLogger(AsyncAPIIT.class); + + @Override + @BeforeEach + void setup() { + kafkaClient = new KafkaTypedTestClient<>(kafkaBootstrapServers, ByteArraySerializer.class, ByteArrayDeserializer.class); + objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + marshaller = new ByteArrayCloudEventMarshaller(objectMapper); + } + + @Override + @AfterEach + void cleanUp() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + @Test + @Order(1) + void testPublisher() throws InterruptedException { + String id = startProcessNoCheck("asyncEventPublisher"); + Converter converter = new ByteArrayCloudEventUnmarshallerFactory(objectMapper).unmarshaller(Map.class).cloudEvent(); + final CountDownLatch countDownLatch = new CountDownLatch(1); + kafkaClient.consume("wait", v -> { + try { + CloudEvent event = converter.convert(v); + if (id.equals(event.getExtension(CloudEventExtensionConstants.PROCESS_INSTANCE_ID))) { + countDownLatch.countDown(); + } + } catch (IOException e) { + logger.info("Unmarshall exception", e); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + assertThat(countDownLatch.getCount()).isZero(); + } + + @Test + @Order(2) + void testConsumer() throws IOException { + final String flowId = "asyncEventConsumer"; + String id = startProcess(flowId); + kafkaClient.produce(marshaller.marshall(buildCloudEvent(id, "wait", marshaller)), "wait"); + waitForFinish(flowId, id, Duration.ofSeconds(10)); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateIT.java new file mode 100644 index 0000000000..3939fb7c2c --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateIT.java @@ -0,0 +1,52 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +@QuarkusTestResource(ExternalServiceMock.class) +@QuarkusTestResource(KafkaQuarkusTestResource.class) +class CallbackStateIT extends AbstractCallbackStateIT { + + private static final String CALLBACK_STATE_SERVICE_URL = "/callback_state"; + private static final String CALLBACK_STATE_SERVICE_GET_BY_ID_URL = CALLBACK_STATE_SERVICE_URL + "/{id}"; + private static final String CALLBACK_STATE_EVENT_TYPE = "callback_state_event_type"; + private static final String CALLBACK_STATE_EVENT_TOPIC = "callback_state_event_type"; + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateSuccessful() throws Exception { + executeCallbackStateSuccessfulPath(CALLBACK_STATE_SERVICE_URL, + CALLBACK_STATE_SERVICE_GET_BY_ID_URL, + ANSWER, + CALLBACK_STATE_EVENT_TYPE, + CALLBACK_STATE_EVENT_TOPIC); + } + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateWithError() throws Exception { + executeCallbackStateWithErrorPath(CALLBACK_STATE_SERVICE_URL, CALLBACK_STATE_SERVICE_GET_BY_ID_URL); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateTimeoutsIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateTimeoutsIT.java new file mode 100644 index 0000000000..1b43ae637d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateTimeoutsIT.java @@ -0,0 +1,68 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.kie.kogito.quarkus.workflows.ExternalServiceMock.SUCCESSFUL_QUERY; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceExists; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstanceAndGetId; + +@QuarkusIntegrationTest +@QuarkusTestResource(ExternalServiceMock.class) +@QuarkusTestResource(KafkaQuarkusTestResource.class) +class CallbackStateTimeoutsIT extends AbstractCallbackStateIT { + + private static final String CALLBACK_STATE_TIMEOUTS_SERVICE_URL = "/callback_state_timeouts"; + private static final String CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL = CALLBACK_STATE_TIMEOUTS_SERVICE_URL + "/{id}"; + private static final String CALLBACK_STATE_TIMEOUTS_EVENT_TYPE = "callback_state_timeouts_event_type"; + private static final String CALLBACK_STATE_TIMEOUTS_TOPIC = "callback_state_timeouts_event_type"; + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateTimeoutsSuccessful() throws Exception { + executeCallbackStateSuccessfulPath(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, + CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, + ANSWER, + CALLBACK_STATE_TIMEOUTS_EVENT_TYPE, + CALLBACK_STATE_TIMEOUTS_TOPIC); + } + + @Test + void callbackStateTimeoutsExceeded() { + // start a new process instance by sending a query and collect the process instance id. + String processInput = buildProcessInput(SUCCESSFUL_QUERY); + String processInstanceId = newProcessInstanceAndGetId(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, processInput); + // assert the process instance is there + assertProcessInstanceExists(CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId); + // do nothing more and wait until eventTimeout is fired and the process instance finalizes. + assertProcessInstanceHasFinished(CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId, 1, 10); + } + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateWithError() throws Exception { + executeCallbackStateWithErrorPath(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateWithTimeoutsErrorHandlerIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateWithTimeoutsErrorHandlerIT.java new file mode 100644 index 0000000000..658e2b8ac5 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CallbackStateWithTimeoutsErrorHandlerIT.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.quarkus.workflows; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.Converter; +import org.kie.kogito.event.cloudevents.CloudEventExtensionConstants; +import org.kie.kogito.event.impl.StringCloudEventUnmarshallerFactory; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.cloudevents.CloudEvent; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.quarkus.workflows.ExternalServiceMock.SUCCESSFUL_QUERY; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceExists; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstanceAndGetId; + +@QuarkusIntegrationTest +@QuarkusTestResource(ExternalServiceMock.class) +@QuarkusTestResource(KafkaQuarkusTestResource.class) +class CallbackStateWithTimeoutsErrorHandlerIT extends AbstractCallbackStateIT { + private static final Logger LOGGER = LoggerFactory.getLogger(CallbackStateWithTimeoutsErrorHandlerIT.class); + + private static final String CALLBACK_STATE_TIMEOUTS_SERVICE_URL = "/callback_state_with_timeouts_error_handler"; + private static final String CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL = CALLBACK_STATE_TIMEOUTS_SERVICE_URL + "/{id}"; + private static final String CALLBACK_STATE_TIMEOUTS_EVENT_TYPE = "callback_state_timeouts_event_type"; + private static final String CALLBACK_STATE_TIMEOUTS_TOPIC = "callback_state_timeouts_event_type"; + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateTimeoutsSuccessful() throws Exception { + String processInstanceId = executeCallbackStateSuccessfulPath(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, + CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, + ANSWER, + CALLBACK_STATE_TIMEOUTS_EVENT_TYPE, + CALLBACK_STATE_TIMEOUTS_TOPIC); + waitForFinalizedEvent(processInstanceId, "success"); + } + + @Test + void callbackStateTimeoutsExceeded() throws Exception { + String processInput = buildProcessInput(SUCCESSFUL_QUERY); + String processInstanceId = newProcessInstanceAndGetId(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, processInput); + assertProcessInstanceExists(CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId); + + assertProcessInstanceHasFinished(CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId, 1, 10); + waitForFinalizedEvent(processInstanceId, "timeoutCallbackError"); + } + + @Test + @SuppressWarnings("squid:S2699") + void callbackStateWithError() throws Exception { + String processInstanceId = executeCallbackStateWithErrorPath(CALLBACK_STATE_TIMEOUTS_SERVICE_URL, CALLBACK_STATE_TIMEOUTS_GET_BY_ID_URL); + waitForFinalizedEvent(processInstanceId, "error"); + } + + private void waitForFinalizedEvent(String processInstanceId, String topic) throws InterruptedException { + Converter converter = new StringCloudEventUnmarshallerFactory(objectMapper).unmarshaller(Map.class).cloudEvent(); + final CountDownLatch countDownLatch = new CountDownLatch(1); + kafkaClient.consume(topic, v -> { + try { + CloudEvent event = converter.convert(v); + LOGGER.debug("Found on topic {} CE {}", topic, event); + if (processInstanceId.equals(event.getExtension(CloudEventExtensionConstants.PROCESS_INSTANCE_ID))) { + countDownLatch.countDown(); + } + } catch (IOException e) { + LOGGER.info("Unmarshall exception", e); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + assertThat(countDownLatch.getCount()).isZero(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ConversationFlowIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ConversationFlowIT.java new file mode 100644 index 0000000000..ced575a07e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ConversationFlowIT.java @@ -0,0 +1,61 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; + +@QuarkusTestResource(OperationsMockService.class) +@QuarkusIntegrationTest +class ConversationFlowIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void sanityVerification() { + given() + .contentType(ContentType.JSON) + .when() + .body( + Collections + .singletonMap( + "workflowdata", + Collections.singletonMap("fahrenheit", "100"))) + .post("/fahrenheit_to_celsius") + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.fahrenheit", is("100")) + .body("workflowdata.celsius", is(37.808f)); //values from mock server + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CorrelationIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CorrelationIT.java new file mode 100644 index 0000000000..db3bf2ff99 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CorrelationIT.java @@ -0,0 +1,146 @@ +/* + * 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.quarkus.workflows; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.cloudevents.CloudEventExtensionConstants; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.jackson.JsonCloudEventData; +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.awaitility.Awaitility.await; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceExists; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.getProcessInstance; + +@QuarkusIntegrationTest +@QuarkusTestResource(KafkaQuarkusTestResource.class) +public class CorrelationIT { + + public static final String USER_ID = "userid"; + public static final String PROCESS_URL = "/correlation"; + public static final String PROCESS_GET_BY_ID_URL = PROCESS_URL + "/{id}"; + public static final String CORRELATION_EVENT_TYPE = "correlation_event_type"; + public static final String CORRELATION_EVENT_TOPIC = CORRELATION_EVENT_TYPE; + public static final String START_EVENT_TYPE = "correlation_start_event_type"; + public static final String START_EVENT_TOPIC = START_EVENT_TYPE; + private static final Logger LOGGER = LoggerFactory.getLogger(CorrelationIT.class); + + private KafkaTestClient kafkaClient; + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + private String kafkaBootstrapServers; + + private ObjectMapper objectMapper; + + @BeforeEach + void setup() { + kafkaClient = new KafkaTestClient(kafkaBootstrapServers); + objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + @AfterEach + public void cleanup() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + @Test + void correlationEventTestDefault() throws Exception { + correlationEventTest(null); + } + + private void correlationEventTest(String startNode) throws JsonProcessingException { + final String userId = UUID.randomUUID().toString(); + + // start a new process instance by sending and event + LOGGER.debug("Sending create correlation workflow event"); + io.cloudevents.core.v1.CloudEventBuilder cloudEventBuilder = CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(URI.create("")) + .withType(START_EVENT_TYPE) + .withTime(OffsetDateTime.now()) + .withExtension(USER_ID, userId) + .withData(JsonCloudEventData.wrap(objectMapper.createObjectNode().put("message", "Starting workflow using correlation"))); + + //setting start node if present + if (startNode != null) { + cloudEventBuilder.withExtension(CloudEventExtensionConstants.PROCESS_START_FROM_NODE, startNode); + } + + String request = objectMapper.writeValueAsString(cloudEventBuilder + .build()); + kafkaClient.produce(request, START_EVENT_TOPIC); + + // double check that the process instance is there. + AtomicReference processInstanceId = new AtomicReference<>(); + await().with().pollDelay(2, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).atMost(2, TimeUnit.MINUTES).until(() -> { + String id = getProcessInstance(PROCESS_URL); + LOGGER.debug("Created workflow instance id = " + id); + processInstanceId.set(id); + return id != null; + }); + + assertProcessInstanceExists(PROCESS_GET_BY_ID_URL, processInstanceId.get()); + + // prepare and send the response to the created process via kafka + LOGGER.debug("Sending correlation event to complete Workflow {}", processInstanceId.get()); + String response = objectMapper.writeValueAsString(CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(URI.create("")) + .withType(CORRELATION_EVENT_TYPE) + .withTime(OffsetDateTime.now()) + .withExtension(USER_ID, userId) + .withData(JsonCloudEventData.wrap(objectMapper.createObjectNode().put("message", "Hello using correlation"))) + .build()); + kafkaClient.produce(response, CORRELATION_EVENT_TOPIC); + // give some time for the event to be processed and the process to finish. + assertProcessInstanceHasFinished(PROCESS_GET_BY_ID_URL, processInstanceId.get(), 1, 180); + LOGGER.debug("Workflow {} completed", processInstanceId.get()); + } + + @Test + void correlationEventStartFromNodeTest() throws Exception { + correlationEventTest("printWaitMessage");//printWaitMessage node + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CustomRestIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CustomRestIT.java new file mode 100644 index 0000000000..f07b3dd8aa --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/CustomRestIT.java @@ -0,0 +1,54 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class CustomRestIT { + + @Test + void testCustomType() { + testIt("customType"); + } + + @Test + void testCustomFunction() { + testIt("customFunction"); + } + + private void testIt(String path) { + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.emptyMap()) + .post(path) + .then() + .statusCode(201) + .body("workflowdata.name", is("John")); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/DivisionMockService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/DivisionMockService.java new file mode 100644 index 0000000000..ead8c1214b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/DivisionMockService.java @@ -0,0 +1,67 @@ +/* + * 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.quarkus.workflows; + +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.matching.EqualToPattern; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class DivisionMockService implements QuarkusTestResourceLifecycleManager { + + private WireMockServer server; + + private static final String DIVISION_SERVICE_MOCK_PORT = "kogito.sw.functions.division.port"; + + static final int[] dividends = { 2, 4, 6, 8, 10 }; + static final int divisor = 2; + + @Override + public Map start() { + server = new WireMockServer(options().dynamicPort()); + for (int dividend : dividends) { + stubFor(dividend, divisor); + } + server.start(); + return Map.of(DIVISION_SERVICE_MOCK_PORT, Integer.toString(server.port())); + } + + private void stubFor(int dividend, int divisor) { + server.stubFor(get(urlPathEqualTo("/division")) + .withQueryParam("dividend", new EqualToPattern(Integer.toString(dividend))) + .withQueryParam("divisor", new EqualToPattern(Integer.toString(divisor))) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody("{\"result\":" + dividend / divisor + "}"))); + } + + @Override + public void stop() { + if (server != null) { + server.stop(); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumProfile.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumProfile.java new file mode 100644 index 0000000000..a0c7ed7244 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumProfile.java @@ -0,0 +1,40 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import org.kie.kogito.serverless.workflow.rpc.RPCWorkItemHandler; + +import io.quarkus.test.junit.QuarkusTestProfile; + +public class EmitEnumProfile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + return Collections.singletonMap(RPCWorkItemHandler.GRPC_ENUM_DEFAULT_PROPERTY, "true"); + } + + @Override + public Set tags() { + return Collections.singleton("emitEnum"); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumRPCGreetIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumRPCGreetIT.java new file mode 100644 index 0000000000..d73db98095 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EmitEnumRPCGreetIT.java @@ -0,0 +1,52 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.quarkus.test.junit.TestProfile; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTestResource(GrpcServerPortResource.class) +@QuarkusIntegrationTest +@TestProfile(EmitEnumProfile.class) +class EmitEnumRPCGreetIT { + + @Test + void testStateIsUnknown() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Javierito\", \"language\":\"Spanish\", \"unknown\": true}}").when() + .post("/rpc-greet") + .then() + .statusCode(201) + .body("workflowdata.state", is("UNKNOWN")) + .body("workflowdata.innerMessage.state", is("UNKNOWN")) + .body("workflowdata.minority[0].state", is("UNKNOWN")) + .body("workflowdata.minority[0].innerMessage.state", is("UNKNOWN")) + .body("workflowdata.minority[1].state", is("UNKNOWN")) + .body("workflowdata.minority[1].innerMessage.state", is("UNKNOWN")); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EnumEchoServiceMock.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EnumEchoServiceMock.java new file mode 100644 index 0000000000..83a7017de4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EnumEchoServiceMock.java @@ -0,0 +1,61 @@ +/* + * 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.quarkus.workflows; + +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; + +public class EnumEchoServiceMock implements QuarkusTestResourceLifecycleManager { + + private WireMockServer wireMockServer; + + @Override + public Map start() { + configureWiremockServer(); + return Map.of("quarkus.rest-client.enum_parameter_yaml.url", wireMockServer.baseUrl()); + } + + private void configureWiremockServer() { + wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig().extensions(new ResponseTemplateTransformer(false)).dynamicPort()); + wireMockServer.start(); + + wireMockServer.stubFor(post(urlEqualTo("/echo")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{ \"echoedMsgType\": \"{{jsonPath request.body '$.msgType'}}\"}") + .withTransformers("response-template"))); + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java new file mode 100644 index 0000000000..84afb41192 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ErrorRestIT.java @@ -0,0 +1,99 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusTestResource(PerfectSquareServiceMock.class) +@QuarkusIntegrationTest +public class ErrorRestIT { + + @Test + public void testErrorRest() { + innerErrorRest("error"); + } + + @Test + public void testErrorGenericRest() { + innerErrorRest("errorGeneric"); + } + + @Test + public void testErrorRepeatedRest() { + innerErrorRest("errorRepeated"); + } + + private void innerErrorRest(String workflowId) { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"number\" : 1}}").when() + .post("/" + workflowId) + .then() + .statusCode(201) + .body("workflowdata.numberType", is("odd")) + .body("workflowdata.perfect", is("Error invoking publishPerfectSquare")); + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"number\" : 4}}").when() + .post("/" + workflowId) + .then() + .statusCode(201) + .body("workflowdata.numberType", is("even")) + .body("workflowdata.perfect", is(true)); + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"number\" : 6}}").when() + .post("/" + workflowId) + .then() + .statusCode(201) + .body("workflowdata.numberType", is("even")) + .body("workflowdata.perfect", is(false)); + } + + @Test + public void testErrorWithMetadata() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number\" : 12342}") + .when() + .post("/errorWithMetadata") + .then() + .statusCode(201); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number\" : 12341}") + .when() + .post("/errorWithMetadata") + .then() + .statusCode(400); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventFlowIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventFlowIT.java new file mode 100644 index 0000000000..91ede4a21f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventFlowIT.java @@ -0,0 +1,143 @@ +/* + * 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.quarkus.workflows; + +import java.io.IOException; +import java.time.Duration; +import java.util.Map; +import java.util.Optional; + +import org.awaitility.core.ConditionTimeoutException; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.CloudEventMarshaller; +import org.kie.kogito.event.impl.ByteArrayCloudEventMarshaller; +import org.kie.kogito.workflows.services.JavaSerializationMarshaller; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.*; + +@QuarkusIntegrationTest +class EventFlowIT { + + private static Map> marshallers; + + private static CloudEventMarshaller defaultMarshaller; + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + ObjectMapper mapper = new ObjectMapper().registerModule(JsonFormat.getCloudEventJacksonModule()); + marshallers = Map.of("quiet", new JavaSerializationMarshaller()); + defaultMarshaller = new ByteArrayCloudEventMarshaller(mapper); + } + + @Test + void testNotStartingEvent() throws IOException { + doIt("nonStartEvent", Optional.of("manolo"), "move"); + } + + @Test + void testNotStartingMultipleEvent() throws IOException { + doIt("nonStartMultipleEvent", "quiet", "never"); + } + + @Test + void testNotStartingMultipleEventTimeout() throws IOException { + doIt("nonStartMultipleEventTimeout", "eventTimeout1", "eventTimeout2"); + } + + @Test + void testNotStartingMultipleEventTimeoutExclusive() throws IOException { + doIt("nonStartMultipleEventTimeoutExclusive"); + } + + @Test + void testNotStartingMultipleEventExclusive1() throws IOException { + doIt("nonStartMultipleEventExclusive", "event1Exclusive"); + } + + @Test + void testNotStartingEventWorkflowTimeout() { + final String flowName = "nonStartMultipleEventWorkflowTimeout"; + String id = startProcess(flowName); + waitForFinish(flowName, id, Duration.ofSeconds(5)); + } + + @Test + void testNotStartingMultipleEventExclusive2() throws IOException { + doIt("nonStartMultipleEventExclusive", "event2Exclusive"); + } + + @Test + void testNotStartingMultipleEventExclusive3() throws IOException { + doIt("nonStartMultipleEventExclusive", "event3Exclusive"); + } + + @Test + void testNotStartingMultipleEventRainy() throws IOException { + final String flowName = "nonStartMultipleEvent"; + final String id = startProcess(flowName); + sendEvents(id, "quiet"); + assertThrows(ConditionTimeoutException.class, () -> waitForFinish(flowName, id, Duration.ofSeconds(5))); + sendEvents(id, "never"); + waitForFinish(flowName, id, Duration.ofSeconds(5)); + } + + private void sendEvents(String id, String... eventTypes) throws IOException { + sendEvents(id, Optional.empty(), eventTypes); + } + + private void sendEvents(String id, Optional businessKey, String... eventTypes) throws IOException { + for (String eventType : eventTypes) { + given() + .contentType(ContentType.JSON) + .when() + .body(generateCloudEvent(id, businessKey, eventType)) + .post("/" + eventType) + .then() + .statusCode(202); + } + } + + private void doIt(String flowName, Optional businessKey, String... eventTypes) throws IOException { + String id = startProcess(flowName, businessKey); + sendEvents(id, businessKey, eventTypes); + waitForFinish(flowName, id, Duration.ofSeconds(15)); + } + + private void doIt(String flowName, String... eventTypes) throws IOException { + String id = startProcess(flowName); + sendEvents(id, eventTypes); + waitForFinish(flowName, id, Duration.ofSeconds(15)); + } + + private byte[] generateCloudEvent(String id, Optional businessKey, String type) throws IOException { + CloudEventMarshaller marshaller = marshallers.getOrDefault(type, defaultMarshaller); + return marshaller.marshall(buildCloudEvent(id, businessKey, type, marshaller)); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventTimedoutIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventTimedoutIT.java new file mode 100644 index 0000000000..544b016549 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/EventTimedoutIT.java @@ -0,0 +1,96 @@ +/* + * 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.quarkus.workflows; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.kafka.common.serialization.ByteArrayDeserializer; +import org.apache.kafka.common.serialization.ByteArraySerializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.Converter; +import org.kie.kogito.event.cloudevents.CloudEventExtensionConstants; +import org.kie.kogito.event.impl.ByteArrayCloudEventUnmarshallerFactory; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTypedTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.CloudEvent; +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.quarkus.workflows.AssuredTestUtils.startProcess; + +@QuarkusIntegrationTest +@QuarkusTestResource(KafkaQuarkusTestResource.class) +public class EventTimedoutIT { + + private final static Logger logger = LoggerFactory.getLogger(EventTimedoutIT.class); + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + String kafkaBootstrapServers; + private ObjectMapper objectMapper; + private KafkaTypedTestClient kafkaClient; + + @BeforeEach + void setup() { + kafkaClient = new KafkaTypedTestClient<>(kafkaBootstrapServers, ByteArraySerializer.class, ByteArrayDeserializer.class); + objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + @AfterEach + void cleanUp() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + @Test + void testTimedout() throws InterruptedException { + String id = startProcess("eventTimedout"); + Converter converter = new ByteArrayCloudEventUnmarshallerFactory(objectMapper).unmarshaller(Map.class).cloudEvent(); + final CountDownLatch countDownLatch = new CountDownLatch(1); + kafkaClient.consume("timeoutError", v -> { + try { + CloudEvent event = converter.convert(v); + if (id.equals(event.getExtension(CloudEventExtensionConstants.PROCESS_INSTANCE_ID))) { + countDownLatch.countDown(); + } + } catch (IOException e) { + logger.info("Unmarshall exception", e); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + assertThat(countDownLatch.getCount()).isZero(); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExpressionRestIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExpressionRestIT.java new file mode 100644 index 0000000000..e27b4d1ba6 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExpressionRestIT.java @@ -0,0 +1,77 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; + +@QuarkusIntegrationTest +class ExpressionRestIT { + + @Test + void testExpressionRest() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .header("pepe", "pepa") + .body("{\"workflowdata\":{\"numbers\":[{\"x\":2, \"y\": 1},{\"x\":4, \"y\": 3}]}}").when() + .post("/expression") + .then() + .statusCode(201) + .body("workflowdata.result", is(4)) + .body("workflowdata.number", nullValue()) + .body("workflowdata.message", is("my name is javierito and in my native language dog is translated to perro and the header pepe is pepa")) + .body("workflowdata.user", is("anonymous")) + .body("workflowdata.discardedResult", nullValue()); + } + + @Test + void testExpressionInputValidation() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\":{\"numbers\":[{\"x\":\"abcdedf\", \"y\": 1},{\"x\":4, \"y\": 3}]}}").when() + .post("/expression") + .then() + .statusCode(is(400)) + .body("message", notNullValue()) + .body("id", nullValue()); + } + + @Test + void testExpressionOutputValidation() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\":{\"numbers\":[{\"x\":2, \"y\": 1},{\"x\":4, \"y\": 3}]}}").when() + .post("/invalidOutputExpression") + .then() + .statusCode(is(400)) + .body("message", containsString("message")) + .body("id", notNullValue()); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExternalServiceMock.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExternalServiceMock.java new file mode 100644 index 0000000000..7ffa1481b7 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ExternalServiceMock.java @@ -0,0 +1,79 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.http.Fault; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +public class ExternalServiceMock implements QuarkusTestResourceLifecycleManager { + + public static final String SUCCESSFUL_QUERY = "SUCCESSFUL_QUERY"; + public static final String GENERATE_ERROR_QUERY = "GENERATE_ERROR_QUERY"; + + public static final String EXTERNAL_SERVICE_MOCK_URL = "external-service-mock.url"; + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + configureFor(wireMockServer.port()); + + // mock a successful invocation + stubFor(post("/external-service/sendRequest") + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON)) + .withRequestBody(equalToJson("{\"query\" : \"" + SUCCESSFUL_QUERY + "\"}")) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody("{}"))); + + // mock a failing invocation + stubFor(post("/external-service/sendRequest") + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON)) + .withRequestBody(equalToJson("{\"query\" : \"" + GENERATE_ERROR_QUERY + "\"}")) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withFault(Fault.CONNECTION_RESET_BY_PEER))); + + return Collections.singletonMap(EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl()); + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ForEachRestIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ForEachRestIT.java new file mode 100644 index 0000000000..03c83ac5c3 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ForEachRestIT.java @@ -0,0 +1,65 @@ +/* + * 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.quarkus.workflows; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; + +@QuarkusTestResource(DivisionMockService.class) +@QuarkusIntegrationTest +class ForEachRestIT { + + @ParameterizedTest + @ValueSource(strings = { "/forEachCustomType", "/forEachRest" }) + void testForEachWorkItem(String id) { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"input\" : " + Arrays.toString(DivisionMockService.dividends) + ", \"divisor\": " + DivisionMockService.divisor + "}}").when() + .post(id) + .then() + .statusCode(201) + .body("workflowdata.output", is(Arrays.stream(DivisionMockService.dividends).map(i -> i / DivisionMockService.divisor + 1).boxed().collect(Collectors.toList()))) + .body("workflowdata.response", nullValue()); + } + + @Test + void testForEachSubflow() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"numbers\" : [1,2,3,4,5], \"constant\": 2}").when() + .post("/foreach_parent") + .then() + .statusCode(201) + .body("workflowdata.products", is(Arrays.asList(2, 4, 6, 8, 10))); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GreetRestIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GreetRestIT.java new file mode 100644 index 0000000000..72920b0459 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GreetRestIT.java @@ -0,0 +1,98 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; +import org.kie.kogito.internal.process.runtime.KogitoWorkflowProcess; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class GreetRestIT { + + @Test + void testGreetHiddenRest() { + assertIt("greethidden", "Hello from JSON Workflow,"); + } + + @Test + void testGreetRest() { + assertIt("greet", "Hello from JSON Workflow,"); + } + + @Test + void testGreetUnknownRest() { + assertIt("greetUnknown", "I'm not familiar with your language,"); + } + + @Test + void testWorkflowType() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/greetdetails") + .then() + .statusCode(200) + .body("type", is(KogitoWorkflowProcess.SW_TYPE)); + } + + @Test + void testVersion() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/greetdetails") + .then() + .statusCode(200) + .body("version", is("1.0")); + } + + private void assertIt(String flowName, String unknownMessage) { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"John\", \"language\":\"English\"}}").when() + .post("/" + flowName) + .then() + .statusCode(201) + .body("workflowdata.greeting", is("Hello from JSON Workflow,")); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Javierito\", \"language\":\"Spanish\"}}").when() + .post("/" + flowName) + .then() + .statusCode(201) + .body("workflowdata.greeting", is("Saludos desde JSON Workflow,")); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"John\", \"language\":\"Unknown\"}}").when() + .post("/" + flowName) + .then() + .statusCode(201) + .body("workflowdata.greeting", is(unknownMessage)); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GrpcServerPortResource.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GrpcServerPortResource.java new file mode 100644 index 0000000000..bdb084a029 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/GrpcServerPortResource.java @@ -0,0 +1,41 @@ +/* + * 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.quarkus.workflows; + +import java.util.Map; + +import org.apache.groovy.util.Maps; +import org.kie.kogito.test.utils.SocketUtils; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +public class GrpcServerPortResource implements QuarkusTestResourceLifecycleManager { + + @Override + public Map start() { + String port = Integer.toString(SocketUtils.findAvailablePort()); + return Maps.of("quarkus.grpc.clients.Greeter.port", port, + "quarkus.grpc.server.port", port, + "quarkus.grpc.server.test-port", port); + } + + @Override + public void stop() { + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/HelloWorldIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/HelloWorldIT.java new file mode 100644 index 0000000000..24f7f9e2c9 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/HelloWorldIT.java @@ -0,0 +1,74 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.kie.kogito.serverless.workflow.SWFConstants; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.specification.RequestSpecification; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class HelloWorldIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void testeEmptyBodyHelloWorld() { + byte counter = 2; + do { + doIt(Collections.emptyMap()); + } while (counter-- > 0); + } + + @Test + void testStringBodyHelloWorld() { + doIt(Collections.singletonMap(SWFConstants.DEFAULT_WORKFLOW_VAR, "")); + } + + @Test + void testNoBodyHelloWorld() { + doIt(null); + } + + @Test + void testEmptyWorkflowDataHelloWorld() { + doIt(Collections.singletonMap(SWFConstants.DEFAULT_WORKFLOW_VAR, Collections.emptyMap())); + } + + private void doIt(Object body) { + RequestSpecification request = given() + .contentType(ContentType.JSON); + if (body != null) { + request = request.body(body); + } + request.post("/helloworld").then().statusCode(201).body("workflowdata.result", is("Hello World!")); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeepActiveIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeepActiveIT.java new file mode 100644 index 0000000000..f300d668df --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeepActiveIT.java @@ -0,0 +1,90 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class KeepActiveIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void testKeepActive() { + Map body = Collections.singletonMap("keepActive", true); + String id = given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", body)) + .post("/keepActive") + .then() + .statusCode(201) + .extract().path("id"); + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/keepActive/{id}", id) + .then().statusCode(200).body("workflowdata.message", is("this will never end")); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .delete("/keepActive/{id}", id); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/keepActive/{id}", id) + .then().statusCode(404); + + } + + @Test + void testEndIt() { + Map body = Collections.singletonMap("keepActive", false); + String id = given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", body)) + .post("/keepActive") + .then() + .statusCode(201) + .extract().path("id"); + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/keepActive/{id}", id) + .then().statusCode(404); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeycloakServiceMock.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeycloakServiceMock.java new file mode 100644 index 0000000000..67d3d66bcc --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/KeycloakServiceMock.java @@ -0,0 +1,97 @@ +/* + * 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.quarkus.workflows; + +import java.util.HashMap; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +/** + * Lightweight Keycloak mock to use when an OidcClient is required, and we don't want/need to start a full Keycloak + * container as part of the tests, etc. Keep the things simple. + */ +public class KeycloakServiceMock implements QuarkusTestResourceLifecycleManager { + + public static final String KEY_CLOAK_SERVICE_URL = "keycloak.mock.service.url"; + public static final String KEY_CLOAK_SERVICE_TOKEN_PATH = "keycloak.mock.service.token-path"; + public static final String REALM = "kogito-tests"; + public static final String KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE = "/realms/" + REALM + "/protocol/openid-connect/token"; + public static final String CLIENT_ID = "kogito-app"; + public static final String SECRET = "secret"; + public static final String KEYCLOAK_ACCESS_TOKEN = "KEYCLOAK_ACCESS_TOKEN"; + public static final String KEYCLOAK_REFRESH_TOKEN = "KEYCLOAK_REFRESH_TOKEN"; + public static final String KEYCLOAK_SESSION_STATE = "KEYCLOAK_SESSION_STATE"; + + public static final String AUTH_REQUEST_BODY = "grant_type=client_credentials"; + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + configureFor(wireMockServer.port()); + + stubFor(post(KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_FORM_URLENCODED)) + .withBasicAuth(CLIENT_ID, SECRET) + .withRequestBody(equalTo(AUTH_REQUEST_BODY)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody(getTokenResult()))); + + Map properties = new HashMap<>(); + properties.put(KEY_CLOAK_SERVICE_URL, wireMockServer.baseUrl()); + properties.put(KEY_CLOAK_SERVICE_TOKEN_PATH, KEY_CLOAK_SERVICE_TOKEN_PATH_VALUE); + return properties; + } + + private static String getTokenResult() { + return "{\n" + + " \"access_token\": \"" + KEYCLOAK_ACCESS_TOKEN + "\",\n" + + " \"expires_in\": 300,\n" + + " \"refresh_expires_in\": 1800,\n" + + " \"refresh_token\": \"" + KEYCLOAK_REFRESH_TOKEN + "\",\n" + + " \"token_type\": \"bearer\",\n" + + " \"not-before-policy\": 0,\n" + + " \"session_state\": \"" + KEYCLOAK_SESSION_STATE + "\",\n" + + " \"scope\": \"email profile\"\n" + + "}"; + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/MultipleTimerInstancesIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/MultipleTimerInstancesIT.java new file mode 100644 index 0000000000..bb9a6e6b04 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/MultipleTimerInstancesIT.java @@ -0,0 +1,49 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstanceAndGetId; + +@QuarkusIntegrationTest +class MultipleTimerInstancesIT { + + private static final String MULTIPLE_TIMER_INSTANCES_EVENT_STATE_TIMEOUTS = "/multiple_timer_instances_event_state_timeouts"; + private static final String MULTIPLE_TIMER_EVENT_STATE_TIMEOUTS_GET_BY_ID_URL = MULTIPLE_TIMER_INSTANCES_EVENT_STATE_TIMEOUTS + "/{id}"; + private static final String EMPTY_WORKFLOW_DATA = "{\"workflowdata\" : \"\"}"; + private static final int AT_LEAST_SECONDS = 1; + private static final int AT_MOST_SECONDS = 120; + + @Test + void eventStateTimeouts() { + // Start 3 simultaneous instances. + String processInstanceId1 = newProcessInstanceAndGetId(MULTIPLE_TIMER_INSTANCES_EVENT_STATE_TIMEOUTS, EMPTY_WORKFLOW_DATA); + String processInstanceId2 = newProcessInstanceAndGetId(MULTIPLE_TIMER_INSTANCES_EVENT_STATE_TIMEOUTS, EMPTY_WORKFLOW_DATA); + String processInstanceId3 = newProcessInstanceAndGetId(MULTIPLE_TIMER_INSTANCES_EVENT_STATE_TIMEOUTS, EMPTY_WORKFLOW_DATA); + + // The three instances must finish in a period of time, otherwise the issue is still present. + assertProcessInstanceHasFinished(MULTIPLE_TIMER_EVENT_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId1, AT_LEAST_SECONDS, AT_MOST_SECONDS); + assertProcessInstanceHasFinished(MULTIPLE_TIMER_EVENT_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId2, AT_LEAST_SECONDS, AT_MOST_SECONDS); + assertProcessInstanceHasFinished(MULTIPLE_TIMER_EVENT_STATE_TIMEOUTS_GET_BY_ID_URL, processInstanceId3, AT_LEAST_SECONDS, AT_MOST_SECONDS); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java new file mode 100644 index 0000000000..c4923c191b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayFlowIT.java @@ -0,0 +1,57 @@ +/* + * 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.quarkus.workflows; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; + +@QuarkusTestResource(OpenAPIArrayMockService.class) +@QuarkusIntegrationTest +class OpenAPIArrayFlowIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void testArray() { + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("inputArray", Arrays.asList(1, 2, 3, 4))) + .post("/openapiarray") + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.response", is(Arrays.asList(1, 2, 3, 4))); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java new file mode 100644 index 0000000000..c67a911d8e --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIArrayMockService.java @@ -0,0 +1,62 @@ +/* + * 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.quarkus.workflows; + +import java.util.Map; +import java.util.function.UnaryOperator; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class OpenAPIArrayMockService implements QuarkusTestResourceLifecycleManager { + + private static WireMockServer arrayService; + + @Override + public Map start() { + arrayService = startServer("[1,2,3,4]", p -> p); + return Map.of("array-service-mock.url", arrayService.baseUrl()); + } + + @Override + public void stop() { + if (arrayService != null) { + arrayService.stop(); + } + } + + private static WireMockServer startServer(final String response, UnaryOperator function) { + final WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + server.stubFor(function.apply(post(urlEqualTo("/testArray")).withRequestBody(equalToJson("[1,2,3,4]"))) + .withPort(server.port()) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(response))); + return server; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIEnumParameterIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIEnumParameterIT.java new file mode 100644 index 0000000000..a480fe7754 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIEnumParameterIT.java @@ -0,0 +1,48 @@ +/* + * 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.quarkus.workflows; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +@QuarkusTestResource(EnumEchoServiceMock.class) +class OpenAPIEnumParameterIT { + + @Test + void test() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .body(Map.of()) + .post("/openapienumparameter") + .then() + .statusCode(201) + .body("workflowdata.echoedMsgType", is("text")); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIInterfaceGenIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIInterfaceGenIT.java new file mode 100644 index 0000000000..8c0cacb3ad --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OpenAPIInterfaceGenIT.java @@ -0,0 +1,54 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +/** + * Handle integration tests for OpenAPI Spec Interface generation for SW projects. + */ +@QuarkusIntegrationTest +public class OpenAPIInterfaceGenIT { + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void verifyOperationIdIsGeneratedByDefault() { + given() + .accept(ContentType.JSON) + .when() + .get("/q/openapi?format=json") + .then() + .statusCode(200) + // verifies the get path in the helloworld SW + .body("paths.'/helloworld'.get.operationId", is("getResources_helloworld")); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OperationsMockService.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OperationsMockService.java new file mode 100644 index 0000000000..4a698d3cce --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/OperationsMockService.java @@ -0,0 +1,77 @@ +/* + * 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.quarkus.workflows; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.UnaryOperator; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.MappingBuilder; +import com.github.tomakehurst.wiremock.matching.EqualToPattern; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class OperationsMockService implements QuarkusTestResourceLifecycleManager { + + private static WireMockServer subtractionService; + private static WireMockServer multiplicationService; + + public static final String SUBTRACTION_SERVICE_MOCK_URL = "subtraction-service-mock.url"; + public static final String MULTIPLICATION_SERVICE_MOCK_URL = "multiplication-service-mock.url"; + + @Override + public Map start() { + multiplicationService = + startServer("{ \"product\": 37.808 }", p -> p.withHeader("pepe", new EqualToPattern("pepa"))); + subtractionService = + startServer("{ \"difference\": 68.0 }", p -> p); + + Map result = new HashMap<>(); + result.put(MULTIPLICATION_SERVICE_MOCK_URL, multiplicationService.baseUrl()); + result.put(SUBTRACTION_SERVICE_MOCK_URL, subtractionService.baseUrl()); + return result; + } + + @Override + public void stop() { + if (multiplicationService != null) { + multiplicationService.stop(); + } + if (subtractionService != null) { + subtractionService.stop(); + } + } + + private static WireMockServer startServer(final String response, UnaryOperator function) { + final WireMockServer server = new WireMockServer(options().dynamicPort()); + server.start(); + server.stubFor(function.apply(post(urlEqualTo("/"))) + .withPort(server.port()) + .willReturn(aResponse() + .withHeader("Content-Type", "application/json") + .withBody(response))); + return server; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ParallelStateIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ParallelStateIT.java new file mode 100644 index 0000000000..9bb5e1644f --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/ParallelStateIT.java @@ -0,0 +1,55 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.hasLength; + +@QuarkusIntegrationTest +class ParallelStateIT { + + @Test + void testAllParallelRest() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\":{\"numCompleted\": 3}}").when() + .post("/parallel") + .then() + .statusCode(201) + .body("workflowdata.result", hasLength(3)); + } + + @Test + void testPartialParallelRest() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\":{\"numCompleted\": 2}}").when() + .post("/parallel") + .then() + .statusCode(201) + .body("workflowdata.result", hasLength(2)); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PerfectSquareServiceMock.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PerfectSquareServiceMock.java new file mode 100644 index 0000000000..4e2b176183 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PerfectSquareServiceMock.java @@ -0,0 +1,66 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.badRequest; +import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; + +public class PerfectSquareServiceMock implements QuarkusTestResourceLifecycleManager { + + public static final String PERFECT_SQUARE_MOCK_PORT = "perfect-square-mock.port"; + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + int port = wireMockServer.port(); + configureFor(port); + + // mock a successful invocation + stubFor(post("/publish/odd/1") + .willReturn(badRequest())); + + stubFor(post("/publish/even/4") + .willReturn(aResponse().withBody("{\"perfect\":true}"))); + + stubFor(post("/publish/even/6") + .willReturn(aResponse().withBody("{\"perfect\":false}"))); + return Collections.singletonMap("kogito.sw.functions.publishPerfectSquare.port", Integer.toString(port)); + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PojoServiceIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PojoServiceIT.java new file mode 100644 index 0000000000..728b7c2012 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/PojoServiceIT.java @@ -0,0 +1,172 @@ +/* + * 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.quarkus.workflows; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.process.ProcessInstanceVariableDataEvent; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.kie.kogito.workflows.services.BasicDataPerson; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.path.json.JsonPath; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.kie.kogito.quarkus.workflows.WorkflowTestUtils.waitForKogitoProcessInstanceEvent; + +@QuarkusIntegrationTest +class PojoServiceIT { + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + String kafkaBootstrapServers; + + KafkaTestClient kafkaClient; + + @BeforeAll + static void init() { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @BeforeEach + void setup() { + kafkaClient = new KafkaTestClient(kafkaBootstrapServers); + } + + @AfterEach + public void cleanup() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + @Test + void testPojo() throws Exception { + doIt("pojoService"); + } + + @Test + void testFilterPojo() throws Exception { + doIt("pojoServiceFilter"); + } + + @Test + void testSquareService() { + Map body = new HashMap<>(); + body.put("first", 3); + body.put("second", 6); + body.put("third", 9); + given() + .contentType(ContentType.JSON) + .when() + .body(body) + .post("/squareService") + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.response", is(Arrays.asList(9, 36, 81))); + } + + @Test + void testTypesPojo() throws Exception { + Map body = new HashMap<>(); + SimpleDateFormat formatter = new SimpleDateFormat("MM/dd/yyyy"); + Date creationDate = formatter.parse("07/07/2022"); + String cardId = "cardID"; + int count = 5; + boolean enabled = true; + Date birthDate = formatter.parse("08/12/1999"); + BasicDataPerson basicDataPerson = new BasicDataPerson(cardId, 100.01, count, enabled, birthDate); + + body.put("name", "javierito"); + body.put("age", 666); + body.put("income", 666.3); + body.put("creationDate", creationDate); + body.put("basicDataPerson", basicDataPerson); + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", body)) + .post("/pojoServiceTypes") + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.name", is("javieritoPerson")) + .body("workflowdata.age", is(1)) + .body("workflowdata.income", is(20000.5f)) + .body("workflowdata.creationDate", is(creationDate.getTime())) + .body("workflowdata.cardId", is(cardId)) + .body("workflowdata.discount", is(100.01f)) + .body("workflowdata.count", is(count)) + .body("workflowdata.enabled", is(enabled)) + .body("workflowdata.birthDate", is(birthDate.getTime())); + + JsonPath processInstanceEventContent = waitForKogitoProcessInstanceEvent(kafkaClient, ProcessInstanceVariableDataEvent.class, + e -> "pojoServiceTypes".equals(e.get("kogitoprocid")) && "workflowdata".equals(e.get("data.variableName")), true); + Map workflowDataMap = processInstanceEventContent.getMap("data.variableValue"); + assertThat(workflowDataMap) + .hasSize(9) + .containsEntry("name", "javieritoPerson") + .containsEntry("creationDate", creationDate.getTime()) + .containsEntry("age", 1) + .containsEntry("income", 20000.5f) + .containsEntry("cardId", "cardID") + .containsEntry("discount", 100.01f) + .containsEntry("count", count) + .containsEntry("enabled", enabled) + .containsEntry("birthDate", birthDate.getTime()); + } + + private void doIt(String flowName) throws Exception { + Map body = new HashMap<>(); + body.put("name", "javierito"); + body.put("age", 666); + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", body)) + .post("/" + flowName) + .then() + .statusCode(201) + .body("id", notNullValue()) + .body("workflowdata.name", is("javieritoPerson")) + .body("workflowdata.age", nullValue()); + JsonPath processInstanceEventContent = waitForKogitoProcessInstanceEvent(kafkaClient, ProcessInstanceVariableDataEvent.class, + e -> flowName.equals(e.get("kogitoprocid")) && "workflowdata".equals(e.get("data.variableName")), true); + Map workflowDataMap = processInstanceEventContent.getMap("data.variableValue"); + assertThat(workflowDataMap).hasSize(1); + assertThat(workflowDataMap).containsEntry("name", "javieritoPerson"); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RPCGreetIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RPCGreetIT.java new file mode 100644 index 0000000000..f866547742 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RPCGreetIT.java @@ -0,0 +1,93 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.Matchers.hasSize; + +@QuarkusTestResource(GrpcServerPortResource.class) +@QuarkusIntegrationTest +class RPCGreetIT { + + private static final String FLOW_ID = "rpc-greet"; + + @Test + void testEnglish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"John\", \"language\":\"English\"}").when() + .post(FLOW_ID) + .then() + .statusCode(201) + .body("workflowdata.message", is("Hello from gRPC service John")) + .body("workflowdata.state", is("SUCCESS")) + .body("workflowdata.innerMessage.number", is(23)) + .body("workflowdata.minority", hasSize(2)) + .body("workflowdata.minority[0].message", is("marquitos")) + .body("workflowdata.minority[1].message", is("Boungiorno Marco")); + + } + + @Test + void testSpanish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Javierito\", \"language\":\"Spanish\", \"unknown\": true}}").when() + .post(FLOW_ID) + .then() + .statusCode(201) + .body("workflowdata.message", is("Saludos desde gRPC service Javierito")) + .body("workflowdata.state", nullValue()); + } + + @Test + void testDefaultLanguage() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"John\"}}").when() + .post(FLOW_ID) + .then() + .statusCode(201) + .body("workflowdata.message", containsString("Hello")); + } + + @Test + void testUnsupportedLanguage() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Jan\", \"language\":\"Czech\"}}").when() + .post(FLOW_ID) + .then() + .statusCode(201) + .body("workflowdata.message", containsString("Hello")); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RetriggerIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RetriggerIT.java new file mode 100644 index 0000000000..749c40c49d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/RetriggerIT.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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class RetriggerIT { + + @Test + void testRetrigger() { + String id = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number1\":2,\"number2\":0}").when() + .post("/division") + .then() + .statusCode(400) + .extract().path("id"); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"number2\":1}").when() + .patch("/division/{id}", id) + .then() + .statusCode(200); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON).when() + .post("/management/processes/division/instances/{id}/retrigger", id) + .then().statusCode(200).body("workflowdata.response", is(2)); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON).when() + .get("/division/{id}", id) + .then().statusCode(404); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SubFlowsIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SubFlowsIT.java new file mode 100644 index 0000000000..57ab33832d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SubFlowsIT.java @@ -0,0 +1,159 @@ +/* + * 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.quarkus.workflows; + +import java.time.Duration; +import java.util.UUID; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import jakarta.ws.rs.core.MediaType; + +import static io.restassured.RestAssured.given; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.CoreMatchers.is; + +@QuarkusIntegrationTest +class SubFlowsIT { + + public static final Duration TIMEOUT = Duration.ofSeconds(5); + + static { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + @Test + void testSubFlows() { + String mainId = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .post("/MainFlow") + .then() + .statusCode(201) + .extract().path("id"); + + String[] eventFlow1Id = new String[1]; + eventFlow1Id[0] = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/EventFlow1") + .then() + .statusCode(200) + .extract().path("[0].id"); + + given() + .header("ce-specversion", "1.0") + .header("ce-id", UUID.randomUUID().toString()) + .header("ce-source", "org.persistence") + .header("ce-type", "event1") + .header("ce-kogitoprocrefid", eventFlow1Id[0]) + .contentType(MediaType.APPLICATION_JSON) + .accept(ContentType.JSON) + .post("/") + .then() + .statusCode(202); + + String[] eventFlow2Id = new String[1]; + await().timeout(TIMEOUT).untilAsserted(() -> eventFlow2Id[0] = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/EventFlow2") + .then() + .statusCode(200) + .body("$.size()", is(1)) + .extract().path("[0].id")); + + given() + .header("ce-specversion", "1.0") + .header("ce-id", UUID.randomUUID().toString()) + .header("ce-source", "org.persistence") + .header("ce-type", "event2") + .header("ce-kogitoprocrefid", eventFlow2Id[0]) + .contentType(MediaType.APPLICATION_JSON) + .accept(ContentType.JSON) + .post("/") + .then() + .statusCode(202); + + await().timeout(TIMEOUT).untilAsserted(() -> eventFlow1Id[0] = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/EventFlow1") + .then() + .log().all() + .statusCode(200) + .body("$.size()", is(1)) + .extract().path("[0].id")); + + given() + .header("ce-specversion", "1.0") + .header("ce-id", UUID.randomUUID().toString()) + .header("ce-source", "org.persistence") + .header("ce-type", "event1") + .header("ce-kogitoprocrefid", eventFlow1Id[0]) + .contentType(MediaType.APPLICATION_JSON) + .accept(ContentType.JSON) + .post("/") + .then() + .statusCode(202); + + await().timeout(TIMEOUT).untilAsserted(() -> eventFlow2Id[0] = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/EventFlow2") + .then() + .log().all() + .statusCode(200) + .body("$.size()", is(1)) + .extract().path("[0].id")); + + given() + .header("ce-specversion", "1.0") + .header("ce-id", UUID.randomUUID().toString()) + .header("ce-source", "org.persistence") + .header("ce-type", "event2") + .header("ce-kogitoprocrefid", eventFlow2Id[0]) + .contentType(MediaType.APPLICATION_JSON) + .accept(ContentType.JSON) + .post("/") + .then() + .statusCode(202); + + await().timeout(TIMEOUT).untilAsserted(() -> given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/MainFlow/{id}", mainId) + .then() + .statusCode(404)); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/MainFlow") + .then() + .statusCode(200) + .body("$.size()", is(0)); + + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwaggerIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwaggerIT.java new file mode 100644 index 0000000000..e95c0b95cb --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwaggerIT.java @@ -0,0 +1,72 @@ +/* + * 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.quarkus.workflows; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; +import io.swagger.parser.OpenAPIParser; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; + +@QuarkusIntegrationTest +class SwaggerIT { + + private static final Logger logger = LoggerFactory.getLogger(SwaggerIT.class); + + @Test + void testSwagger() { + String response = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .get("/q/openapi") + .then() + .statusCode(200).extract().asString(); + logger.trace("Open API body {}", response); + OpenAPI openAPI = new OpenAPIParser().readContents(response, null, null).getOpenAPI(); + Set tags = new HashSet<>(); + + for (PathItem path : openAPI.getPaths().values()) { + checkOperation(path.getPost(), tags); + checkOperation(path.getGet(), tags); + checkOperation(path.getPatch(), tags); + checkOperation(path.getDelete(), tags); + checkOperation(path.getPut(), tags); + } + logger.debug("Tags collected {}", tags); + assertThat(tags).contains("expression"); + + } + + private void checkOperation(Operation operation, Set tags) { + if (operation != null && operation.getTags() != null) { + tags.addAll(operation.getTags()); + } + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateDataConditionBasedIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateDataConditionBasedIT.java new file mode 100644 index 0000000000..072f4d3617 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateDataConditionBasedIT.java @@ -0,0 +1,75 @@ +/* + * 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.quarkus.workflows; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.path.json.JsonPath; + +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstance; + +@QuarkusIntegrationTest +class SwitchStateDataConditionBasedIT extends AbstractSwitchStateIT { + + private static final String SWITCH_STATE_DATA_CONDITION_TRANSITION_SERVICE_URL = "/switch_state_data_condition_transition"; + private static final String SWITCH_STATE_DATA_CONDITION_END_URL = "/switch_state_data_condition_end"; + private static final String DECISION_END_DECISION = "EndDecision"; + + @Test + void switchStateDataConditionTransitionApproved() { + // Start a new process instance that must be "Approved" and check the result. + JsonPath result = newProcessInstance(SWITCH_STATE_DATA_CONDITION_TRANSITION_SERVICE_URL, buildProcessInput(20)); + assertDecision(result, DECISION_APPROVED); + } + + @Test + void switchStateDataConditionTransitionDenied() { + // Start a new process instance that must be "Denied" and check the result. + JsonPath result = newProcessInstance(SWITCH_STATE_DATA_CONDITION_TRANSITION_SERVICE_URL, buildProcessInput(10)); + assertDecision(result, DECISION_DENIED); + } + + @Test + void switchStateDefaultConditionTransition() { + // Start a new process instance that must go through the default condition check the result. + JsonPath result = newProcessInstance(SWITCH_STATE_DATA_CONDITION_TRANSITION_SERVICE_URL, buildProcessInput(-20)); + assertDecision(result, DECISION_DENIED); + } + + @Test + void switchStateDataConditionEndApproved() { + switchStateDataConditionEnd(20); + } + + @Test + void switchStateDataConditionEndDenied() { + switchStateDataConditionEnd(10); + } + + @Test + void switchStateDefaultConditionEnd() { + switchStateDataConditionEnd(-20); + } + + private void switchStateDataConditionEnd(int age) { + JsonPath result = newProcessInstance(SWITCH_STATE_DATA_CONDITION_END_URL, buildProcessInput(age)); + assertDecision(result, DECISION_END_DECISION); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateEventConditionBasedIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateEventConditionBasedIT.java new file mode 100644 index 0000000000..a32d29675d --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/SwitchStateEventConditionBasedIT.java @@ -0,0 +1,277 @@ +/* + * 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.quarkus.workflows; + +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.UUID; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.core.builder.CloudEventBuilder; +import io.cloudevents.jackson.JsonCloudEventData; +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.path.json.JsonPath; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.assertProcessInstanceHasFinished; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstanceAndGetId; + +@QuarkusIntegrationTest +@QuarkusTestResource(KafkaQuarkusTestResource.class) +class SwitchStateEventConditionBasedIT extends AbstractSwitchStateIT { + + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL = "/switch_state_event_condition_timeouts_transition"; + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL_GET_BY_ID_URL = SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL + "/{id}"; + + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL = "/switch_state_event_condition_timeouts_transition2"; + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL_GET_BY_ID_URL = SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL + "/{id}"; + + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_URL = "/switch_state_event_condition_timeouts_end"; + private static final String SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_GET_BY_ID_URL = SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_URL + "/{id}"; + + /** + * Topics and event types for the switch_state_event_condition_timeouts_transition SW. + */ + private static final String VISA_APPROVED_EVENT_TOPIC_TRANSITION = "visa_approved_topic_transition"; + private static final String VISA_DENIED_EVENT_TOPIC_TRANSITION = "visa_denied_topic_transition"; + private static final String VISA_APPROVED_EVENT_TYPE_TRANSITION = "visa_approved_in_transition"; + private static final String VISA_DENIED_EVENT_TYPE_TRANSITION = "visa_denied_in_transition"; + private static final String PROCESS_RESULT_EVENT_TYPE_TRANSITION = "process_result_event_transition"; + + /** + * Topics and event types for the switch_state_event_condition_timeouts_transition2 SW. + */ + private static final String VISA_APPROVED_EVENT_TOPIC_TRANSITION2 = "visa_approved_topic_transition2"; + private static final String VISA_DENIED_EVENT_TOPIC_TRANSITION2 = "visa_denied_topic_transition2"; + private static final String VISA_APPROVED_EVENT_TYPE_TRANSITION2 = "visa_approved_in_transition2"; + private static final String VISA_DENIED_EVENT_TYPE_TRANSITION2 = "visa_denied_in_transition2"; + private static final String PROCESS_RESULT_EVENT_TYPE_TRANSITION2 = "process_result_event_transition2"; + + /** + * Topics and event types for the switch-state-event-condition-timeouts-end. + */ + private static final String VISA_APPROVED_EVENT_TOPIC_CONDITION_END = "visa_approved_topic_condition_end"; + private static final String VISA_DENIED_EVENT_TOPIC_CONDITION_END = "visa_denied_topic_condition_end"; + private static final String VISA_APPROVED_EVENT_TYPE_CONDITION_END = "visa_approved_in_condition_end"; + private static final String VISA_DENIED_EVENT_TYPE_CONDITION_END = "visa_denied_in_condition_end"; + private static final String PROCESS_RESULT_EVENT_TYPE_CONDITION_END = "process_result_event_condition_end"; + + private static final String EVENT_DECISION_PATH = "data.decision"; + private static final String EVENT_PROCESS_INSTANCE_ID_PATH = "kogitoprocinstanceid"; + private static final String EVENT_TYPE_PATH = "type"; + + private static final String KOGITO_OUTGOING_STREAM_TOPIC = "kogito-sw-out-events"; + + private static final String EMPTY_WORKFLOW_DATA = "{\"workflowdata\" : \"\"}"; + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + String kafkaBootstrapServers; + + ObjectMapper objectMapper; + + KafkaTestClient kafkaClient; + + @BeforeEach + void setup() throws Exception { + kafkaClient = new KafkaTestClient(kafkaBootstrapServers); + objectMapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } + + @AfterEach + void cleanUp() { + if (kafkaClient != null) { + kafkaClient.shutdown(); + } + } + + @Test + void switchStateEventConditionTimeoutsTransitionApproved() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL_GET_BY_ID_URL, + VISA_APPROVED_EVENT_TYPE_TRANSITION, + VISA_APPROVED_EVENT_TOPIC_TRANSITION, + PROCESS_RESULT_EVENT_TYPE_TRANSITION, + DECISION_APPROVED); + } + + @Test + void switchStateEventConditionTimeoutsTransitionDenied() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL_GET_BY_ID_URL, + VISA_DENIED_EVENT_TYPE_TRANSITION, + VISA_DENIED_EVENT_TOPIC_TRANSITION, + PROCESS_RESULT_EVENT_TYPE_TRANSITION, + DECISION_DENIED); + } + + @Test + void switchStateEventConditionTimeoutsTransitionTimeoutsExceeded() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithTimeoutsExceeded(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL_GET_BY_ID_URL, + PROCESS_RESULT_EVENT_TYPE_TRANSITION, + DECISION_NO_DECISION); + } + + @Test + void switchStateEventConditionTimeoutsTransition2Approved() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL_GET_BY_ID_URL, + VISA_APPROVED_EVENT_TYPE_TRANSITION2, + VISA_APPROVED_EVENT_TOPIC_TRANSITION2, + PROCESS_RESULT_EVENT_TYPE_TRANSITION2, + DECISION_APPROVED); + } + + @Test + void switchStateEventConditionTimeoutsTransition2Denied() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL_GET_BY_ID_URL, + VISA_DENIED_EVENT_TYPE_TRANSITION2, + VISA_DENIED_EVENT_TOPIC_TRANSITION2, + PROCESS_RESULT_EVENT_TYPE_TRANSITION2, + DECISION_DENIED); + } + + @Test + void switchStateEventConditionTimeoutsTransition2TimeoutsExceeded() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithTimeoutsExceeded(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL_GET_BY_ID_URL, + PROCESS_RESULT_EVENT_TYPE_TRANSITION2, + DECISION_DENIED); + } + + @Test + void switchStateEventConditionTimeoutsEndTApproved() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_GET_BY_ID_URL, + VISA_APPROVED_EVENT_TYPE_CONDITION_END, + VISA_APPROVED_EVENT_TOPIC_CONDITION_END, + PROCESS_RESULT_EVENT_TYPE_CONDITION_END, + DECISION_APPROVED); + } + + @Test + void switchStateEventConditionTimeoutsEndDenied() throws Exception { + switchStateEventConditionTimeoutsTransitionBasedWithEvent(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_URL, + SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_GET_BY_ID_URL, + VISA_DENIED_EVENT_TYPE_CONDITION_END, + VISA_DENIED_EVENT_TOPIC_CONDITION_END, + PROCESS_RESULT_EVENT_TYPE_CONDITION_END, + DECISION_DENIED); + } + + @Test + void switchStateEventConditionTimeoutsEndTimeoutsExceeded() throws Exception { + // Start a new process instance. + String processInstanceId = newProcessInstanceAndGetId(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_URL, EMPTY_WORKFLOW_DATA); + // Give enough time for the timeout to exceed. + assertProcessInstanceHasFinished(SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_END_GET_BY_ID_URL, processInstanceId, 1, 180); + // When the process has finished the default case event must arrive. + JsonPath result = waitForEvent(KOGITO_OUTGOING_STREAM_TOPIC, PROCESS_RESULT_EVENT_TYPE_CONDITION_END, 50); + assertThat(result.getString("data")).isEmpty(); + } + + /** + * Executes the happy path for the SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL and the + * SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL processes. + */ + private void switchStateEventConditionTimeoutsTransitionBasedWithEvent(String processUrl, + String processGetByIdUrl, + String eventTypeToSend, + String eventTopicToSend, + String expectedDecisionEventType, + String expectedDecision) throws Exception { + // Start a new process instance. + String processInstanceId = newProcessInstanceAndGetId(processUrl, EMPTY_WORKFLOW_DATA); + + // Send the event to activate the switch state. + String response = objectMapper.writeValueAsString(CloudEventBuilder.v1() + .withId(UUID.randomUUID().toString()) + .withSource(URI.create("")) + .withType(eventTypeToSend) + .withTime(OffsetDateTime.now()) + .withExtension("kogitoprocrefid", processInstanceId) + .withData(JsonCloudEventData.wrap(objectMapper.createObjectNode())) + .build()); + kafkaClient.produce(response, eventTopicToSend); + // Give some time for the event to be processed and the process to finish. + assertProcessInstanceHasFinished(processGetByIdUrl, processInstanceId, 1, 180); + + // Give some time to consume the event and very the expected decision was made. + JsonPath result = waitForEvent(KOGITO_OUTGOING_STREAM_TOPIC, expectedDecisionEventType, 50); + assertDecisionEvent(result, processInstanceId, expectedDecisionEventType, expectedDecision); + } + + /** + * Executes timeout exceeded path for the SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION_URL and the + * SWITCH_STATE_EVENT_CONDITION_TIMEOUTS_TRANSITION2_URL processes. + */ + private void switchStateEventConditionTimeoutsTransitionBasedWithTimeoutsExceeded(String processUrl, + String processGetByIdUrl, + String expectedDecisionEventType, + String expectedDecision) throws Exception { + // Start a new process instance. + String processInstanceId = newProcessInstanceAndGetId(processUrl, EMPTY_WORKFLOW_DATA); + // Give enough time for the timeout to exceed. + assertProcessInstanceHasFinished(processGetByIdUrl, processInstanceId, 1, 180); + // When the process has finished the default case event must arrive. + JsonPath result = waitForEvent(KOGITO_OUTGOING_STREAM_TOPIC, expectedDecisionEventType, 50); + assertDecisionEvent(result, processInstanceId, expectedDecisionEventType, expectedDecision); + } + + protected JsonPath waitForEvent(String topic, String eventType, long seconds) throws Exception { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AtomicReference cloudEvent = new AtomicReference<>(); + kafkaClient.consume(topic, rawCloudEvent -> { + cloudEvent.set(rawCloudEvent); + countDownLatch.countDown(); + }); + // give some time to consume the event and verify the expected decision was made. + assertThat(countDownLatch.await(seconds, TimeUnit.SECONDS)).isTrue(); + JsonPath jsonPath = new JsonPath(cloudEvent.get()); + assertThat(jsonPath.getString(EVENT_TYPE_PATH)).isEqualTo(eventType); + return new JsonPath(cloudEvent.get()); + } + + protected static void assertDecisionEvent(JsonPath cloudEventJsonPath, + String expectedProcessInstanceId, + String expectedEventType, + String expectedDecision) { + assertThat(cloudEventJsonPath.getString(EVENT_PROCESS_INSTANCE_ID_PATH)).isEqualTo(expectedProcessInstanceId); + assertThat(cloudEventJsonPath.getString(EVENT_TYPE_PATH)).isEqualTo(expectedEventType); + assertThat(cloudEventJsonPath.getString(EVENT_DECISION_PATH)).isEqualTo(expectedDecision); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationExternalServicesMock.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationExternalServicesMock.java new file mode 100644 index 0000000000..acc2c88be5 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationExternalServicesMock.java @@ -0,0 +1,96 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collections; +import java.util.Map; + +import com.github.tomakehurst.wiremock.WireMockServer; + +import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; + +import jakarta.ws.rs.core.HttpHeaders; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.configureFor; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.kie.kogito.quarkus.workflows.KeycloakServiceMock.KEYCLOAK_ACCESS_TOKEN; + +public class TokenPropagationExternalServicesMock implements QuarkusTestResourceLifecycleManager { + + public static final String AUTHORIZATION_TOKEN = "AUTHORIZATION_TOKEN"; + public static final String SERVICE3_HEADER_TO_PROPAGATE = "SERVICE3_HEADER_TO_PROPAGATE"; + public static final String SERVICE3_AUTHORIZATION_TOKEN = "SERVICE3_AUTHORIZATION_TOKEN"; + public static final String SERVICE4_HEADER_TO_PROPAGATE = "SERVICE4_HEADER_TO_PROPAGATE"; + public static final String SERVICE4_AUTHORIZATION_TOKEN = "SERVICE4_AUTHORIZATION_TOKEN"; + + private static final String BEARER = "Bearer "; + + public static final String TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL = "propagation-external-service-mock.url"; + + private WireMockServer wireMockServer; + + @Override + public Map start() { + wireMockServer = new WireMockServer(options().dynamicPort()); + wireMockServer.start(); + configureFor(wireMockServer.port()); + + // stub the token-propagation-external-service1 invocation with the expected token + stubForExternalService("/token-propagation-external-service1/executeQuery1", AUTHORIZATION_TOKEN); + + // stub the token-propagation-external-service2 invocation with the expected token + stubForExternalService("/token-propagation-external-service2/executeQuery2", AUTHORIZATION_TOKEN); + + // stub the token-propagation-external-service3 invocation with the expected token + stubForExternalService("/token-propagation-external-service3/executeQuery3", SERVICE3_AUTHORIZATION_TOKEN); + + // stub the token-propagation-external-service4 invocation with the expected token + stubForExternalService("/token-propagation-external-service4/executeQuery4", SERVICE4_AUTHORIZATION_TOKEN); + + // stub the token-propagation-external-service5 invocation with the expected token, no propagation is produced + // in this case but the service must receive the token provided by Keycloak since it has oauth2 security + // configured. + stubForExternalService("/token-propagation-external-service5/executeQuery5", KEYCLOAK_ACCESS_TOKEN); + + return Collections.singletonMap(TOKEN_PROPAGATION_EXTERNAL_SERVICE_MOCK_URL, wireMockServer.baseUrl()); + } + + private static void stubForExternalService(String tokenPropagationExternalServiceUrl, String authorizationToken) { + stubFor(post(tokenPropagationExternalServiceUrl) + .withHeader(CONTENT_TYPE, equalTo(APPLICATION_JSON)) + .withHeader(HttpHeaders.AUTHORIZATION, equalTo(BEARER + authorizationToken)) + .willReturn(aResponse() + .withHeader(CONTENT_TYPE, APPLICATION_JSON) + .withBody("{}"))); + } + + @Override + public void stop() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationIT.java new file mode 100644 index 0000000000..174ce0c6eb --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/TokenPropagationIT.java @@ -0,0 +1,66 @@ +/* + * 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.quarkus.workflows; + +import java.util.HashMap; +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.path.json.JsonPath; + +import jakarta.ws.rs.core.HttpHeaders; + +import static org.kie.kogito.quarkus.workflows.ExternalServiceMock.SUCCESSFUL_QUERY; +import static org.kie.kogito.quarkus.workflows.TokenPropagationExternalServicesMock.AUTHORIZATION_TOKEN; +import static org.kie.kogito.quarkus.workflows.TokenPropagationExternalServicesMock.SERVICE3_AUTHORIZATION_TOKEN; +import static org.kie.kogito.quarkus.workflows.TokenPropagationExternalServicesMock.SERVICE3_HEADER_TO_PROPAGATE; +import static org.kie.kogito.quarkus.workflows.TokenPropagationExternalServicesMock.SERVICE4_AUTHORIZATION_TOKEN; +import static org.kie.kogito.quarkus.workflows.TokenPropagationExternalServicesMock.SERVICE4_HEADER_TO_PROPAGATE; +import static org.kie.kogito.test.utils.ProcessInstancesRESTTestUtils.newProcessInstance; + +@QuarkusTestResource(TokenPropagationExternalServicesMock.class) +@QuarkusTestResource(KeycloakServiceMock.class) +@QuarkusIntegrationTest +class TokenPropagationIT { + + @Test + void tokenPropagations() { + // start a new process instance by sending the post query and collect the process instance id. + String processInput = buildProcessInput(SUCCESSFUL_QUERY); + Map headers = new HashMap<>(); + // prepare the headers to pass to the token_propagation SW. + // service token-propagation-external-service1 and token-propagation-external-service2 will receive the AUTHORIZATION_TOKEN + headers.put(HttpHeaders.AUTHORIZATION, AUTHORIZATION_TOKEN); + // service token-propagation-external-service3 will receive the SERVICE3_AUTHORIZATION_TOKEN + headers.put(SERVICE3_HEADER_TO_PROPAGATE, SERVICE3_AUTHORIZATION_TOKEN); + // service token-propagation-external-service4 will receive the SERVICE4_AUTHORIZATION_TOKEN + headers.put(SERVICE4_HEADER_TO_PROPAGATE, SERVICE4_AUTHORIZATION_TOKEN); + + JsonPath jsonPath = newProcessInstance("/token_propagation", processInput, headers); + Assertions.assertThat(jsonPath.getString("id")).isNotBlank(); + } + + protected static String buildProcessInput(String query) { + return "{\"workflowdata\": {\"query\": \"" + query + "\"} }"; + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowEventIT.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowEventIT.java new file mode 100644 index 0000000000..db120c19bd --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowEventIT.java @@ -0,0 +1,153 @@ +/* + * 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.quarkus.workflows; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.kie.kogito.event.EventPublisher; +import org.kie.kogito.event.process.ProcessDefinitionDataEvent; +import org.kie.kogito.event.process.ProcessDefinitionEventBody; +import org.kie.kogito.event.process.ProcessInstanceDataEvent; +import org.kie.kogito.test.quarkus.QuarkusTestProperty; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.kie.kogito.testcontainers.quarkus.KafkaQuarkusTestResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.shaded.org.awaitility.Awaitility; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; + +import io.cloudevents.jackson.JsonFormat; +import io.quarkus.test.common.QuarkusTestResource; +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@QuarkusIntegrationTest +@QuarkusTestResource(KafkaQuarkusTestResource.class) +public class WorkflowEventIT { + + static { + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + } + + private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowEventIT.class); + + public KafkaTestClient kafkaClient; + private ObjectMapper mapper; + + @QuarkusTestProperty(name = KafkaQuarkusTestResource.KOGITO_KAFKA_PROPERTY) + private String kafkaBootstrapServers; + + @BeforeEach + public void setup() { + kafkaClient = new KafkaTestClient(kafkaBootstrapServers); + mapper = new ObjectMapper() + .registerModule(JsonFormat.getCloudEventJacksonModule()) + .registerModule(new JavaTimeModule()); + } + + @Test + void testWorkflowEvents() throws Exception { + final CompletableFuture future = new CompletableFuture<>(); + + String username = "buddy"; + String password = "buddy"; + + kafkaClient.consume(Set.of(EventPublisher.PROCESS_INSTANCES_TOPIC_NAME), s -> { + LOGGER.debug("Received from kafka: {}", s); + try { + ProcessInstanceDataEvent event = mapper.readValue(s, ProcessInstanceDataEvent.class); + Map data = (Map) event.getData(); + if ("secure".equals(data.get("processId"))) { + if (event.getType().equals("ProcessInstanceStateDataEvent")) { + assertEquals("/secure", event.getSource().toString()); + assertEquals("secure", event.getKogitoProcessId()); + assertEquals("1.0", event.getKogitoProcessInstanceVersion()); + assertEquals(username, event.getKogitoIdentity()); + } + future.complete(null); + } + } catch (Throwable e) { + future.completeExceptionally(e); + } + }); + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .auth().basic(username, password) + .body("{\"workflowdata\" : {\"name\" : \"John\", \"language\":\"English\"}}").when() + .post("/secure") + .then() + .statusCode(201) + .body("workflowdata.greeting", is("Hello from JSON Workflow,")); + + future.get(10, TimeUnit.SECONDS); + } + + @Test + void testWorkflowDefinitionsEvents() { + Collection definitionDataEvents = new ConcurrentLinkedQueue<>(); + kafkaClient.consume(Set.of(EventPublisher.PROCESS_DEFINITIONS_TOPIC_NAME), s -> { + LOGGER.debug("Received from kafka: {}", s); + try { + ProcessDefinitionDataEvent event = mapper.readValue(s, ProcessDefinitionDataEvent.class); + definitionDataEvents.add(event); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + + List processIds = given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .get("/management/processes") + .then() + .statusCode(200) + .extract() + .body().as(List.class); + + Awaitility.waitAtMost(10, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).untilAsserted(() -> assertThat(definitionDataEvents).hasSize(processIds.size())); + + List processIdsFromEvent = definitionDataEvents.stream() + .map(ProcessDefinitionDataEvent::getData) + .map(ProcessDefinitionEventBody::getId) + .collect(Collectors.toList()); + + assertThat(processIdsFromEvent).containsAll(processIds); + } +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowTestUtils.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowTestUtils.java new file mode 100644 index 0000000000..7a381b0089 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/java/org/kie/kogito/quarkus/workflows/WorkflowTestUtils.java @@ -0,0 +1,72 @@ +/* + * 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.quarkus.workflows; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; + +import org.kie.kogito.event.DataEvent; +import org.kie.kogito.event.process.ProcessInstanceStateDataEvent; +import org.kie.kogito.test.quarkus.kafka.KafkaTestClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.restassured.path.json.JsonPath; + +import static org.assertj.core.api.Assertions.assertThat; + +public class WorkflowTestUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(WorkflowTestUtils.class); + public static final int TIME_OUT_SECONDS = 50; + public static final String KOGITO_PROCESSINSTANCES_EVENTS = "kogito-processinstances-events"; + + private WorkflowTestUtils() { + } + + public static JsonPath waitForKogitoProcessInstanceEvent(KafkaTestClient kafkaClient, boolean shutdownAfterConsume) throws Exception { + return waitForKogitoProcessInstanceEvent(kafkaClient, ProcessInstanceStateDataEvent.class, (e) -> true, shutdownAfterConsume); + } + + public static > JsonPath waitForKogitoProcessInstanceEvent(KafkaTestClient kafkaClient, Class eventType, Predicate predicate, boolean shutdownAfterConsume) + throws Exception { + final CountDownLatch countDownLatch = new CountDownLatch(1); + final AtomicReference cloudEvent = new AtomicReference<>(); + + kafkaClient.consume(KOGITO_PROCESSINSTANCES_EVENTS, rawCloudEvent -> { + JsonPath path = new JsonPath(rawCloudEvent); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CLOUD EVENT: {}", path.prettyPrint()); + } + String type = path.get("type"); + if (eventType.getSimpleName().equals(type) && predicate.test(path)) { + cloudEvent.set(path); + countDownLatch.countDown(); + } + }); + // give some time to consume the event + assertThat(countDownLatch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS)).isTrue(); + if (shutdownAfterConsume) { + kafkaClient.shutdown(); + } + return cloudEvent.get(); + } + +} diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_main_resources_greet.sw.json b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_main_resources_greet.sw.json new file mode 100644 index 0000000000..00b3b4d75b --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_main_resources_greet.sw.json @@ -0,0 +1,67 @@ +{ + "id": "greet", + "version": "1.0", + "name": "Greeting workflow", + "description": "JSON based greeting workflow", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "${ .language == \"English\" }", + "transition": "GreetInEnglish" + }, + { + "condition": "${ .language == \"Spanish\" }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "GreetInEnglish" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow, " + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow, " + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": ".greeting+.name" + } + } + } + ], + "end": { + "terminate": "true" + } + } + ] +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_java_ilove_quark_us_GreetTest.java b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_java_ilove_quark_us_GreetTest.java new file mode 100644 index 0000000000..0731472961 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_java_ilove_quark_us_GreetTest.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 ilove.quark.us; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; + +@QuarkusTest +public class GreetTest { + + @Test + public void testEnglish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Yoda\", \"language\":\"English\"}}") + .when() + .post("/greet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Hello")); + } + + @Test + public void testSpanish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"workflowdata\" : {\"name\" : \"Yoda\", \"language\":\"Spanish\"}}") + .when() + .post("/greet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Saludos")); + } +} \ No newline at end of file diff --git a/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_resources_application.yml b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_resources_application.yml new file mode 100644 index 0000000000..67e5ab5fd4 --- /dev/null +++ b/apps-integration-tests/kogito-quarkus-serverless-workflow-integration-test/src/test/resources/__snapshots__/ServerlessWorkflowCodestartTest/testContent/src_test_resources_application.yml @@ -0,0 +1,5 @@ +quarkus: + http: + test-port: 0 + devservices: + enabled: "false" \ No newline at end of file diff --git a/apps-integration-tests/pom.xml b/apps-integration-tests/pom.xml index 610c6427f6..df16aeedfe 100644 --- a/apps-integration-tests/pom.xml +++ b/apps-integration-tests/pom.xml @@ -35,6 +35,9 @@ + kogito-quarkus-integration-test-maven-devmode + kogito-quarkus-processes-integration-test-hot-reload + kogito-quarkus-serverless-workflow-integration-test integration-tests-data-index-service integration-tests-jobs-service integration-tests-trusty-service