From 0e04e3bb9fdcddc332e6750fb86792b4ecfd0da9 Mon Sep 17 00:00:00 2001 From: Helber Belmiro Date: Mon, 24 Jul 2023 09:16:38 -0300 Subject: [PATCH] KOGITO-9415 Added Live Reload support for CodeGenProviders (#3105) * KOGITO-9415 Added Live Reload support for CodeGenProviders Signed-off-by: Helber Belmiro * Update quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java https://github.com/kiegroup/kogito-runtimes/pull/3105#discussion_r1254162917 Co-authored-by: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> * KOGITO-9415 Francisco's review Signed-off-by: Helber Belmiro * Update quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/pom.xml Co-authored-by: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> * KOGITO-9415 Rebase Signed-off-by: Helber Belmiro * KOGITO-9415 added kogito-serverless-workflow-openapi-generated as dependency to integration test Signed-off-by: Helber Belmiro * KOGITO-9415 added kogito-quarkus-workflow-common-deployment as dependency to integration test Signed-off-by: Helber Belmiro * KOGITO-9415 added kogito-addons-quarkus-common-deployment as dependency to integration test Signed-off-by: Helber Belmiro * KOGITO-9415 added kogito-quarkus-serverless-workflow-deployment as dependency to integration test Signed-off-by: Helber Belmiro * KOGITO-9415 Modified package of model in integration test Signed-off-by: Helber Belmiro * KOGITO-9415 Fixed sonar warning Signed-off-by: Helber Belmiro * Update quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessorTest.java Co-authored-by: Tristan Radisson * KOGITO-9415 Added disabled test for AsyncAPI Signed-off-by: Helber Belmiro * KOGITO-9415 Fixed test for OpenAPI Signed-off-by: Helber Belmiro --------- Signed-off-by: Helber Belmiro Co-authored-by: Francisco Javier Tirado Sarti <65240126+fjtirado@users.noreply.github.com> Co-authored-by: Tristan Radisson --- .../workflow/WorkflowWorkItemHandler.java | 2 +- .../KogitoQuarkusResourceUtils.java | 13 +- .../LiveReloadExecutionBuildItem.java | 33 +++ .../ServerlessWorkflowAssetsProcessor.java | 5 +- .../livereload/LiveReloadProcessor.java | 218 ++++++++++++++++++ ...oadableAsyncApiGeneratorStreamCodeGen.java | 28 +++ .../LiveReloadableCodeGenProvider.java | 37 +++ .../LiveReloadableCodeGenProviderBase.java | 53 +++++ ...loadableOpenApiGeneratorStreamCodeGen.java | 28 +++ ...eReloadableWorkflowRPCCodeGenProvider.java | 28 +++ .../deployment/livereload/SkipLiveReload.java | 22 ++ .../WorkflowOpenApiHandlerGenerator.java | 3 +- ...t.livereload.LiveReloadableCodeGenProvider | 3 + .../pom.xml | 202 ++++++++++++++++ .../deployment/livereload/GreeterService.java | 149 ++++++++++++ .../src/main/proto/greeting.proto | 26 +++ .../src/main/resources/specs/asyncAPI.yaml | 40 ++++ .../main/resources/specs/enum-parameter.yaml | 38 +++ .../livereload/LiveReloadProcessorTest.java | 190 +++++++++++++++ .../src/test/resources/asyncPublisher.sw.json | 27 +++ .../resources/openAPIEnumParameter.sw.json | 38 +++ .../src/test/resources/rpcgreet.sw.json | 33 +++ .../pom.xml | 1 + 23 files changed, 1207 insertions(+), 10 deletions(-) create mode 100644 quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/LiveReloadExecutionBuildItem.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessor.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableAsyncApiGeneratorStreamCodeGen.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProvider.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProviderBase.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableOpenApiGeneratorStreamCodeGen.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableWorkflowRPCCodeGenProvider.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/SkipLiveReload.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/resources/META-INF/services/org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableCodeGenProvider create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/pom.xml create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/GreeterService.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/proto/greeting.proto create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/asyncAPI.yaml create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/enum-parameter.yaml create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessorTest.java create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json create mode 100644 quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json diff --git a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java index ce923720c05..b2d08e2b867 100644 --- a/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java +++ b/kogito-serverless-workflow/kogito-serverless-workflow-runtime/src/main/java/org/kie/kogito/serverless/workflow/WorkflowWorkItemHandler.java @@ -42,7 +42,7 @@ public void executeWorkItem(KogitoWorkItem workItem, KogitoWorkItemManager manag protected abstract Object internalExecute(KogitoWorkItem workItem, Map parameters); - protected final V buildBody(Map params, Class clazz) { + protected static V buildBody(Map params, Class clazz) { for (Object obj : params.values()) { if (obj != null && clazz.isAssignableFrom(obj.getClass())) { return clazz.cast(obj); diff --git a/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java b/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java index e9c95648e06..8a12c604e33 100644 --- a/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java +++ b/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/KogitoQuarkusResourceUtils.java @@ -162,13 +162,16 @@ public static void registerResources(Collection generatedFiles, } public static IndexView generateAggregatedIndex(IndexView baseIndex, List generatedKogitoClasses) { - List indexes = new ArrayList<>(); - indexes.add(baseIndex); - - indexes.addAll(generatedKogitoClasses.stream() + return generateAggregatedIndexNew(baseIndex, generatedKogitoClasses.stream() .map(KogitoGeneratedClassesBuildItem::getIndexedClasses) .collect(Collectors.toList())); - return CompositeIndex.create(indexes.toArray(new IndexView[0])); + } + + public static IndexView generateAggregatedIndexNew(IndexView baseIndex, List newIndexViews) { + List indexes = new ArrayList<>(); + indexes.add(baseIndex); + indexes.addAll(newIndexViews); + return CompositeIndex.create(indexes); } public static Path getTargetClassesPath(AppPaths appPaths) { diff --git a/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/LiveReloadExecutionBuildItem.java b/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/LiveReloadExecutionBuildItem.java new file mode 100644 index 00000000000..13b0e29d705 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-extension-common/kogito-quarkus-common-deployment/src/main/java/org/kie/kogito/quarkus/common/deployment/LiveReloadExecutionBuildItem.java @@ -0,0 +1,33 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.common.deployment; + +import org.jboss.jandex.IndexView; + +import io.quarkus.builder.item.SimpleBuildItem; + +public final class LiveReloadExecutionBuildItem extends SimpleBuildItem { + + private final IndexView indexView; + + public LiveReloadExecutionBuildItem(IndexView indexView) { + this.indexView = indexView; + } + + public IndexView getIndexView() { + return indexView; + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/ServerlessWorkflowAssetsProcessor.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/ServerlessWorkflowAssetsProcessor.java index fe91ec1709d..a83fab59244 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/ServerlessWorkflowAssetsProcessor.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/ServerlessWorkflowAssetsProcessor.java @@ -38,6 +38,7 @@ import org.kie.kogito.process.expr.ExpressionHandler; import org.kie.kogito.quarkus.common.deployment.KogitoAddonsPreGeneratedSourcesBuildItem; import org.kie.kogito.quarkus.common.deployment.KogitoBuildContextBuildItem; +import org.kie.kogito.quarkus.common.deployment.LiveReloadExecutionBuildItem; import org.kie.kogito.quarkus.extensions.spi.deployment.KogitoProcessContainerGeneratorBuildItem; import org.kie.kogito.quarkus.serverless.workflow.WorkflowHandlerGeneratedFile; import org.kie.kogito.quarkus.serverless.workflow.WorkflowHandlerGenerator; @@ -86,9 +87,9 @@ NativeImageResourceBuildItem addExpressionHandlers(BuildProducer sources) { + void addWorkItemHandlers(KogitoBuildContextBuildItem contextBI, LiveReloadExecutionBuildItem liveReloadExecutionBuildItem, BuildProducer sources) { KogitoBuildContext context = contextBI.getKogitoBuildContext(); - IndexView index = indexBuildItem.getIndex(); + IndexView index = liveReloadExecutionBuildItem.getIndexView(); Collection generatedFiles = new ArrayList<>(); for (WorkflowHandlerGenerator generator : generators) { for (WorkflowHandlerGeneratedFile generated : generator.generateHandlerClasses(context, index)) { diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessor.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessor.java new file mode 100644 index 00000000000..398cb4257e8 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessor.java @@ -0,0 +1,218 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.drools.codegen.common.GeneratedFile; +import org.drools.codegen.common.GeneratedFileType; +import org.drools.drl.quarkus.util.deployment.DroolsQuarkusResourceUtils; +import org.eclipse.microprofile.config.Config; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.jandex.IndexView; +import org.jboss.jandex.Indexer; +import org.kie.kogito.codegen.api.context.KogitoBuildContext; +import org.kie.kogito.quarkus.common.deployment.KogitoAddonsPreGeneratedSourcesBuildItem; +import org.kie.kogito.quarkus.common.deployment.KogitoBuildContextBuildItem; +import org.kie.kogito.quarkus.common.deployment.KogitoQuarkusResourceUtils; +import org.kie.kogito.quarkus.common.deployment.LiveReloadExecutionBuildItem; + +import io.quarkus.arc.deployment.GeneratedBeanBuildItem; +import io.quarkus.bootstrap.model.ApplicationModel; +import io.quarkus.bootstrap.prebuild.CodeGenException; +import io.quarkus.deployment.CodeGenContext; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.builditem.CombinedIndexBuildItem; +import io.quarkus.deployment.builditem.LiveReloadBuildItem; +import io.quarkus.deployment.index.IndexingUtil; +import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; +import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; + +/** + * This class adds live reload support for {@link io.quarkus.deployment.CodeGenProvider} objects. + */ +public class LiveReloadProcessor { + + private final LiveReloadBuildItem liveReloadBuildItem; + + private final ApplicationModel applicationModel; + + private final Path workDir; + + private final IndexView computingIndex; + + private final IndexView index; + + private final KogitoBuildContext kogitoBuildContext; + + @Inject + public LiveReloadProcessor( + CombinedIndexBuildItem combinedIndexBuildItem, + LiveReloadBuildItem liveReloadBuildItem, + CurateOutcomeBuildItem curateOutcomeBuildItem, + OutputTargetBuildItem outputTargetBuildItem, + KogitoBuildContextBuildItem contextBuildItem) { + this.liveReloadBuildItem = liveReloadBuildItem; + applicationModel = curateOutcomeBuildItem.getApplicationModel(); + workDir = outputTargetBuildItem.getOutputDirectory(); + computingIndex = combinedIndexBuildItem.getComputingIndex(); + index = combinedIndexBuildItem.getIndex(); + kogitoBuildContext = contextBuildItem.getKogitoBuildContext(); + } + + @BuildStep(onlyIf = IsDevelopment.class) + public LiveReloadExecutionBuildItem liveReload(BuildProducer sourcesProducer) { + Collection generatedFiles = new ArrayList<>(); + List indexViews = new ArrayList<>(); + if (liveReloadBuildItem.isLiveReload()) { + if (shouldSkipLiveReload()) { + dontSkipNextLiveReload(); + } else { + ServiceLoader.load(LiveReloadableCodeGenProvider.class).stream() + .map(ServiceLoader.Provider::get) + .map(this::generateCode) + .forEach(codeGenerationResult -> { + generatedFiles.addAll(codeGenerationResult.getGeneratedFiles()); + indexViews.add(codeGenerationResult.getIndexView()); + }); + } + } + if (!generatedFiles.isEmpty()) { + sourcesProducer.produce(new KogitoAddonsPreGeneratedSourcesBuildItem(generatedFiles)); + skipNextLiveReload(); + return new LiveReloadExecutionBuildItem(KogitoQuarkusResourceUtils.generateAggregatedIndexNew(computingIndex, indexViews)); + } else { + dontSkipNextLiveReload(); + return new LiveReloadExecutionBuildItem(computingIndex); + } + } + + private CodeGenerationResult generateCode(LiveReloadableCodeGenProvider codeGenProvider) { + try { + Collection generatedFiles = new ArrayList<>(generateSources(codeGenProvider)); + return !generatedFiles.isEmpty() ? new CodeGenerationResult(generatedFiles, indexCompiledSources(compileGeneratedSources(generatedFiles))) + : new CodeGenerationResult(List.of(), computingIndex); + } catch (CodeGenException e) { + throw new IllegalStateException(e); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private IndexView indexCompiledSources(Collection generatedBeanBuildItems) { + Indexer kogitoIndexer = new Indexer(); + + for (GeneratedBeanBuildItem generatedBeanBuildItem : generatedBeanBuildItems) { + IndexingUtil.indexClass( + generatedBeanBuildItem.getName(), + kogitoIndexer, + index, + new HashSet<>(), + kogitoBuildContext.getClassLoader(), + generatedBeanBuildItem.getData()); + } + + return kogitoIndexer.complete(); + } + + private Collection compileGeneratedSources(Collection sources) { + return DroolsQuarkusResourceUtils.compileGeneratedSources( + kogitoBuildContext, + applicationModel.getRuntimeDependencies(), + sources, + true); + } + + private Collection generateSources(LiveReloadableCodeGenProvider codeGenProvider) + throws CodeGenException, IOException { + Path outDir = workDir.resolve("generated-sources").resolve(codeGenProvider.providerId()); + Collection generatedFiles = new ArrayList<>(); + Config config = ConfigProvider.getConfig(); + for (Path sourcePath : kogitoBuildContext.getAppPaths().getSourcePaths()) { + Path inputDir = sourcePath.resolve("main").resolve(codeGenProvider.inputDirectory()); + CodeGenContext codeGenContext = new CodeGenContext(applicationModel, outDir, workDir, inputDir, false, config, false); + if (codeGenProvider.shouldRun(inputDir, config) && codeGenProvider.trigger(codeGenContext)) { + try (Stream sources = Files.walk(outDir)) { + sources.filter(Files::isRegularFile) + .filter(path -> path.toString().endsWith(".java")) + .map(path -> { + try { + return new GeneratedFile(GeneratedFileType.SOURCE, outDir.relativize(path), Files.readAllBytes(path)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }) + .forEach(generatedFiles::add); + } + } + } + + return generatedFiles; + } + + private void skipNextLiveReload() { + liveReloadBuildItem.setContextObject(SkipLiveReload.class, SkipLiveReload.TRUE); + } + + private void dontSkipNextLiveReload() { + liveReloadBuildItem.setContextObject(SkipLiveReload.class, SkipLiveReload.FALSE); + } + + private boolean shouldSkipLiveReload() { + if (liveReloadBuildItem.getContextObject(SkipLiveReload.class) != null) { + return liveReloadBuildItem.getContextObject(SkipLiveReload.class) == SkipLiveReload.TRUE; + } + return false; + } + + @BuildStep(onlyIfNot = IsDevelopment.class) + public LiveReloadExecutionBuildItem executeWhenNotDevelopment() { + return new LiveReloadExecutionBuildItem(computingIndex); + } + + private static class CodeGenerationResult { + + private final Collection generatedFiles; + + private final IndexView indexView; + + CodeGenerationResult(Collection generatedFiles, IndexView indexView) { + this.generatedFiles = generatedFiles; + this.indexView = indexView; + } + + Collection getGeneratedFiles() { + return generatedFiles; + } + + IndexView getIndexView() { + return indexView; + } + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableAsyncApiGeneratorStreamCodeGen.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableAsyncApiGeneratorStreamCodeGen.java new file mode 100644 index 00000000000..b3c76ffe20d --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableAsyncApiGeneratorStreamCodeGen.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import io.quarkiverse.asyncapi.generator.input.AsyncApiGeneratorStreamCodeGen; + +/** + * Wrapper for {@link AsyncApiGeneratorStreamCodeGen} that implements the {@link LiveReloadableCodeGenProvider} Service Provider Interface. + */ +public class LiveReloadableAsyncApiGeneratorStreamCodeGen extends LiveReloadableCodeGenProviderBase { + + public LiveReloadableAsyncApiGeneratorStreamCodeGen() { + super(new AsyncApiGeneratorStreamCodeGen()); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProvider.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProvider.java new file mode 100644 index 00000000000..0d02db06c33 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProvider.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import java.nio.file.Path; + +import org.eclipse.microprofile.config.Config; + +import io.quarkus.bootstrap.prebuild.CodeGenException; +import io.quarkus.deployment.CodeGenContext; + +/** + * Service Provider Interface for {@link io.quarkus.deployment.CodeGenProvider} objects that need to be invoked on live reloads. + */ +interface LiveReloadableCodeGenProvider { + + boolean trigger(CodeGenContext context) throws CodeGenException; + + String providerId(); + + String inputDirectory(); + + boolean shouldRun(Path sourceDir, Config config); +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProviderBase.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProviderBase.java new file mode 100644 index 00000000000..a421ded7d7c --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableCodeGenProviderBase.java @@ -0,0 +1,53 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import java.nio.file.Path; + +import org.eclipse.microprofile.config.Config; + +import io.quarkus.bootstrap.prebuild.CodeGenException; +import io.quarkus.deployment.CodeGenContext; +import io.quarkus.deployment.CodeGenProvider; + +abstract class LiveReloadableCodeGenProviderBase implements LiveReloadableCodeGenProvider { + + private final T delegate; + + LiveReloadableCodeGenProviderBase(T delegate) { + this.delegate = delegate; + } + + @Override + public final boolean trigger(CodeGenContext context) throws CodeGenException { + return delegate.trigger(context); + } + + @Override + public String inputDirectory() { + return delegate.inputDirectory(); + } + + @Override + public String providerId() { + return delegate.providerId(); + } + + @Override + public boolean shouldRun(Path sourceDir, Config config) { + return delegate.shouldRun(sourceDir, config); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableOpenApiGeneratorStreamCodeGen.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableOpenApiGeneratorStreamCodeGen.java new file mode 100644 index 00000000000..87dd6367dd2 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableOpenApiGeneratorStreamCodeGen.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import io.quarkiverse.openapi.generator.deployment.codegen.OpenApiGeneratorStreamCodeGen; + +/** + * Wrapper for {@link OpenApiGeneratorStreamCodeGen} that implements the {@link LiveReloadableCodeGenProvider} Service Provider Interface. + */ +public class LiveReloadableOpenApiGeneratorStreamCodeGen extends LiveReloadableCodeGenProviderBase { + + public LiveReloadableOpenApiGeneratorStreamCodeGen() { + super(new OpenApiGeneratorStreamCodeGen()); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableWorkflowRPCCodeGenProvider.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableWorkflowRPCCodeGenProvider.java new file mode 100644 index 00000000000..e24e16a4c25 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadableWorkflowRPCCodeGenProvider.java @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import org.kie.kogito.quarkus.serverless.workflow.rpc.WorkflowRPCCodeGenProvider; + +/** + * Wrapper for {@link WorkflowRPCCodeGenProvider} that implements the {@link LiveReloadableCodeGenProvider} Service Provider Interface. + */ +public class LiveReloadableWorkflowRPCCodeGenProvider extends LiveReloadableCodeGenProviderBase { + + public LiveReloadableWorkflowRPCCodeGenProvider() { + super(new WorkflowRPCCodeGenProvider()); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/SkipLiveReload.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/SkipLiveReload.java new file mode 100644 index 00000000000..1abbe302cd7 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/SkipLiveReload.java @@ -0,0 +1,22 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +enum SkipLiveReload { + + TRUE, + FALSE +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java index efb1b1bd624..310ed2cfdf9 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/java/org/kie/kogito/quarkus/serverless/workflow/openapi/WorkflowOpenApiHandlerGenerator.java @@ -44,7 +44,6 @@ import com.github.javaparser.ast.expr.NameExpr; import com.github.javaparser.ast.expr.NullLiteralExpr; import com.github.javaparser.ast.expr.StringLiteralExpr; -import com.github.javaparser.ast.expr.SuperExpr; import com.github.javaparser.ast.stmt.BlockStmt; import com.github.javaparser.ast.stmt.ReturnStmt; import com.github.javaparser.ast.type.ClassOrInterfaceType; @@ -107,7 +106,7 @@ private WorkflowHandlerGeneratedFile generateHandler(KogitoBuildContext context, if (annotation != null) { methodCallExpr.addArgument(new CastExpr(fromClass(param), new MethodCallExpr(parameters, "remove").addArgument(new StringLiteralExpr(annotation.value().asString())))); } else { - methodCallExpr.addArgument(new MethodCallExpr(new SuperExpr(), "buildBody").addArgument(parameters).addArgument(new ClassExpr(fromClass(param)))); + methodCallExpr.addArgument(new MethodCallExpr("buildBody").addArgument(parameters).addArgument(new ClassExpr(fromClass(param)))); } } clazz.addMethod("getRestClass", Keyword.PROTECTED).setType(parseClassOrInterfaceType(Class.class.getCanonicalName()).setTypeArguments(classNameType)) diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/resources/META-INF/services/org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableCodeGenProvider b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/resources/META-INF/services/org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableCodeGenProvider new file mode 100644 index 00000000000..6bf1e19518e --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-deployment/src/main/resources/META-INF/services/org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableCodeGenProvider @@ -0,0 +1,3 @@ +org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableAsyncApiGeneratorStreamCodeGen +org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableOpenApiGeneratorStreamCodeGen +org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.LiveReloadableWorkflowRPCCodeGenProvider \ No newline at end of file diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/pom.xml b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/pom.xml new file mode 100644 index 00000000000..0d423fec564 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/pom.xml @@ -0,0 +1,202 @@ + + + 4.0.0 + + org.kie.kogito + kogito-quarkus-serverless-workflow-extension + 2.0.0-SNAPSHOT + + + kogito-quarkus-serverless-workflow-extension-live-reload-test + Kogito :: Quarkus Workflows Extension :: Image Integration Tests + + + + org.kie.kogito + kogito-quarkus-serverless-workflow + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + + io.quarkiverse.asyncapi + quarkus-asyncapi + + + + io.quarkus + quarkus-smallrye-reactive-messaging-kafka + + + + io.grpc + grpc-netty-shaded + ${version.io.grpc} + runtime + + + io.grpc + grpc-protobuf + ${version.io.grpc} + + + io.grpc + grpc-stub + ${version.io.grpc} + + + + org.apache.tomcat + annotations-api + 6.0.53 + provided + + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + com.github.tomakehurst + wiremock-jre8 + test + + + org.kie.kogito + kogito-test-utils + test + + + + + org.kie.kogito + kogito-serverless-workflow-openapi-generated + ${project.version} + pom + test + + + * + * + + + + + org.kie.kogito + kogito-addons-quarkus-common-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie.kogito + kogito-quarkus-workflow-common-deployment + ${project.version} + pom + test + + + * + * + + + + + org.kie.kogito + kogito-quarkus-serverless-workflow-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + ${version.io.quarkus} + true + + ${skipTests} + + + + + build + generate-code + generate-code-tests + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.jboss.logmanager.LogManager + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.jboss.logmanager.LogManager + ${container.image.kafka} + ${project.version} + + + + + + + ${project.basedir}/src/main/proto + + greeting.proto + + + + ${project.basedir}/src/main/resources + + + + + \ No newline at end of file diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/GreeterService.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/GreeterService.java new file mode 100644 index 00000000000..62699e1676f --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/GreeterService.java @@ -0,0 +1,149 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.Greeting.HelloReply; +import org.kie.kogito.quarkus.serverless.workflow.deployment.livereload.Greeting.HelloRequest; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.Status; +import io.grpc.stub.StreamObserver; +import io.quarkus.grpc.GrpcService; + +@GrpcService +public class GreeterService extends GreeterGrpc.GreeterImplBase { + + protected static final String[] SUPPORTED_LANGUAGES = { "English", "Spanish" }; + + public static void main(String[] args) throws IOException, InterruptedException { + Server server = buildServer(Integer.getInteger("grpc.port", 50051)); + server.start(); + server.awaitTermination(); + } + + public static Server buildServer(int port) { + return ServerBuilder.forPort(port).addService(new GreeterService()).build(); + } + + @Override + public void sayHello(Greeting.HelloRequest request, + StreamObserver responseObserver) { + responseObserver.onNext(HelloReply.newBuilder().setMessage(getMessage(request)).build()); + responseObserver.onCompleted(); + } + + @Override + public void sayHelloAllLanguages(HelloRequest request, StreamObserver responseObserver) { + for (String language : SUPPORTED_LANGUAGES) { + HelloRequest languageRequest = HelloRequest.newBuilder(request).setLanguage(language).build(); + responseObserver.onNext(HelloReply.newBuilder().setMessage(getMessage(languageRequest)).build()); + } + responseObserver.onCompleted(); + } + + @Override + public StreamObserver sayHelloMultipleLanguagesAtOnce(StreamObserver responseObserver) { + return new StreamObserver<>() { + + private final List messages = new ArrayList<>(); + + @Override + public void onNext(HelloRequest helloRequest) { + messages.add(getMessage(helloRequest)); + } + + @Override + public void onError(Throwable throwable) { + // ignore + } + + @Override + public void onCompleted() { + responseObserver.onNext(HelloReply.newBuilder().setMessage(String.join("\n", messages)).build()); + responseObserver.onCompleted(); + } + }; + } + + @Override + public StreamObserver sayHelloMultipleLanguages(StreamObserver responseObserver) { + return new StreamObserver<>() { + @Override + public void onNext(HelloRequest helloRequest) { + responseObserver.onNext(HelloReply.newBuilder().setMessage(getMessage(helloRequest)).build()); + } + + @Override + public void onError(Throwable throwable) { + // ignore + } + + @Override + public void onCompleted() { + responseObserver.onCompleted(); + } + }; + } + + @Override + public StreamObserver sayHelloMultipleLanguagesError(StreamObserver responseObserver) { + return new StreamObserver<>() { + int counter; + + @Override + public void onNext(HelloRequest helloRequest) { + counter++; + if (counter == 2) { + responseObserver.onNext(HelloReply.newBuilder().setMessage(getMessage(helloRequest)).build()); + RuntimeException ex = Status.OUT_OF_RANGE.asRuntimeException(); + responseObserver.onError(ex); + } else if (counter < 2) { + responseObserver.onNext(HelloReply.newBuilder().setMessage(getMessage(helloRequest)).build()); + } + } + + @Override + public void onError(Throwable throwable) { + // ignore + } + + @Override + public void onCompleted() { + if (counter < 2) { + responseObserver.onCompleted(); + } + } + }; + } + + private static String getMessage(HelloRequest request) { + String message; + switch (request.getLanguage().toLowerCase()) { + case "spanish": + message = "Saludos desde gRPC service " + request.getName(); + break; + case "english": + default: + message = "Hello from gRPC service " + request.getName(); + } + return message; + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/proto/greeting.proto b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/proto/greeting.proto new file mode 100644 index 00000000000..d4cb5327727 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/proto/greeting.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +option java_package="org.kie.kogito.quarkus.serverless.workflow.deployment.livereload"; + + + +// The greeter service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + rpc SayHelloAllLanguages (HelloRequest) returns (stream HelloReply) {} + rpc SayHelloMultipleLanguagesAtOnce (stream HelloRequest) returns (HelloReply) {} + rpc SayHelloMultipleLanguages (stream HelloRequest) returns (stream HelloReply) {} + rpc SayHelloMultipleLanguagesError (stream HelloRequest) returns (stream HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; + string language=2; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} \ No newline at end of file diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/asyncAPI.yaml b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/asyncAPI.yaml new file mode 100644 index 00000000000..7b39a29ac50 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/asyncAPI.yaml @@ -0,0 +1,40 @@ +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/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/enum-parameter.yaml b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/enum-parameter.yaml new file mode 100644 index 00000000000..fedc438f159 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/main/resources/specs/enum-parameter.yaml @@ -0,0 +1,38 @@ +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/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessorTest.java b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessorTest.java new file mode 100644 index 00000000000..52aff8fba70 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/java/org/kie/kogito/quarkus/serverless/workflow/deployment/livereload/LiveReloadProcessorTest.java @@ -0,0 +1,190 @@ +/* + * Copyright 2023 Red Hat, Inc. and/or its affiliates. + * + * Licensed 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.serverless.workflow.deployment.livereload; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jboss.shrinkwrap.api.asset.StringAsset; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.kie.kogito.test.utils.SocketUtils; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.github.tomakehurst.wiremock.extension.responsetemplating.ResponseTemplateTransformer; + +import io.grpc.Server; +import io.quarkus.test.QuarkusDevModeTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; + +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 io.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; + +public class LiveReloadProcessorTest { + + private static final int PORT = SocketUtils.findAvailablePort(); + + @RegisterExtension + public final static QuarkusDevModeTest test = createTest(); + + private static WireMockServer wireMockServer; + + private static QuarkusDevModeTest createTest() { + configureWiremockServer(); + + return new QuarkusDevModeTest() + .withApplicationRoot(jar -> { + try { + jar.addAsResource(new StringAsset(applicationProperties(wireMockServer.baseUrl())), "/application.properties"); + jar.add(new StringAsset(new String(Files.readAllBytes(Path.of("src/main/proto/greeting.proto")))), "src/main/proto/greeting.proto"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); + } + + private static 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"))); + } + + private static String applicationProperties(String wireMockBaseUrl) { + return Stream.of( + "quarkus.rest-client.\"enum_parameter_yaml\".url=" + wireMockBaseUrl, + "quarkus.grpc.clients.Greeter.host=localhost", + "quarkus.grpc.clients.Greeter.port=" + PORT, + "quarkus.grpc.server.port=" + PORT, + "quarkus.grpc.server.test-port=" + PORT) + .collect(Collectors.joining(System.lineSeparator())); + } + + @AfterAll + static void tearDown() { + if (wireMockServer != null) { + wireMockServer.stop(); + } + } + + @Test + void testOpenApi() throws IOException { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .body(Map.of()) + .post("/openapienumparameter") + .then() + .statusCode(404); + + try (FileInputStream inputStream = new FileInputStream("src/test/resources/openAPIEnumParameter.sw.json")) { + test.addResourceFile("openAPIEnumParameter.sw.json", new String(Objects.requireNonNull(inputStream).readAllBytes())); + } + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .when() + .body(Map.of()) + .post("/openapienumparameter") + .then() + .statusCode(201) + .body("workflowdata.echoedMsgType", is("text")); + } + + @Test + void testGrpc() throws InterruptedException, IOException { + Server server = GreeterService.buildServer(PORT); + server.start(); + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + + try { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"John\", \"language\":\"English\"}").when() + .post("/jsongreet") + .then() + .statusCode(404); + + try (FileInputStream inputStream = new FileInputStream("src/test/resources/rpcgreet.sw.json")) { + test.addResourceFile("rpcgreet.sw.json", new String(Objects.requireNonNull(inputStream).readAllBytes())); + } + + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"John\", \"language\":\"English\"}").when() + .post("/jsongreet") + .then() + .statusCode(201) + .body("workflowdata.message", containsString("Hello")); + } finally { + server.shutdownNow(); + server.awaitTermination(); + } + } + + @Test + @Disabled("Disabled until https://issues.redhat.com/browse/KOGITO-9614 is resolved") + void testAsyncApi() throws IOException { + given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", Collections.emptyMap())) + .post("/asyncEventPublisher") + .then() + .statusCode(404); + + try (FileInputStream inputStream = new FileInputStream("src/test/resources/asyncPublisher.sw.json")) { + test.addResourceFile("asyncPublisher.sw.json", new String(Objects.requireNonNull(inputStream).readAllBytes())); + } + + String id = given() + .contentType(ContentType.JSON) + .when() + .body(Collections.singletonMap("workflowdata", Collections.emptyMap())) + .post("/asyncEventPublisher") + .then() + .statusCode(201) + .extract().path("id"); + + assertThat(id).isNotBlank(); + } +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/asyncPublisher.sw.json new file mode 100644 index 00000000000..92643e41450 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/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/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/openAPIEnumParameter.sw.json new file mode 100644 index 00000000000..589cab4e150 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/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/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json new file mode 100644 index 00000000000..502ff1b6fa9 --- /dev/null +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/kogito-quarkus-serverless-workflow-extension-live-reload-test/src/test/resources/rpcgreet.sw.json @@ -0,0 +1,33 @@ +{ + "id": "jsongreet", + "version": "1.0", + "name": "gRPC Greeting workflow", + "description": "JSON based greeting workflow using gRPC", + "start": "GreetPerson", + "functions": [ + { + "name": "sayHello", + "type": "rpc", + "operation": "greeting.proto#Greeter#SayHello" + } + ], + "states": [ + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "sayHello", + "functionRef" : { + "refName": "sayHello", + "arguments": { + "name": ".name", + "language": ".language" + } + } + } + ], + "end": true + } + ] +} diff --git a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/pom.xml b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/pom.xml index f0c943041ad..d0682bf3423 100644 --- a/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/pom.xml +++ b/quarkus/extensions/kogito-quarkus-serverless-workflow-extension/pom.xml @@ -19,6 +19,7 @@ kogito-quarkus-serverless-workflow-deployment kogito-quarkus-serverless-workflow-integration-test kogito-quarkus-serverless-workflow-image-integration-test + kogito-quarkus-serverless-workflow-extension-live-reload-test \ No newline at end of file