diff --git a/packages/sonataflow-quarkus-devui-extension/env/index.js b/packages/sonataflow-quarkus-devui-extension/env/index.js
new file mode 100644
index 00000000000..33ffd575a68
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/env/index.js
@@ -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.
+ */
+
+const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env");
+
+module.exports = composeEnv([require("@kie-tools/root-env/env")], {
+ vars: varsWithName({}),
+ get env() {
+ return {
+ sonataflowQuarkusDevuiExtension: {
+ version: require("../package.json").version,
+ },
+ };
+ },
+});
diff --git a/packages/sonataflow-quarkus-devui-extension/install.js b/packages/sonataflow-quarkus-devui-extension/install.js
new file mode 100644
index 00000000000..99bfadb8cf8
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/install.js
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+const buildEnv = require("./env");
+const { setup } = require("@kie-tools/maven-config-setup-helper");
+
+setup(`
+ -Drevision=${buildEnv.env.sonataflowQuarkusDevuiExtension.version}
+`);
diff --git a/packages/sonataflow-quarkus-devui-extension/package.json b/packages/sonataflow-quarkus-devui-extension/package.json
new file mode 100644
index 00000000000..9407dfdba19
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/package.json
@@ -0,0 +1,41 @@
+{
+ "private": true,
+ "name": "@kie-tools/sonataflow-quarkus-devui-extension",
+ "version": "0.0.0",
+ "description": "",
+ "license": "Apache-2.0",
+ "homepage": "https://github.com/apache/incubator-kie-tools",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/apache/incubator-kie-tools.git"
+ },
+ "bugs": {
+ "url": "https://github.com/apache/incubator-kie-tools/issues"
+ },
+ "scripts": {
+ "build:dev": "run-script-os",
+ "build:dev:darwin:linux": "mvn clean install -DskipTests",
+ "build:dev:win32": "pnpm powershell \"mvn clean install -DskipTests \"",
+ "build:prod": "pnpm lint && run-script-os",
+ "build:prod:darwin:linux": "mvn clean install -DskipTests=$(build-env tests.run --not) -Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures)",
+ "build:prod:win32": "pnpm powershell \"mvn clean install `-DskipTests `-Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures)\"",
+ "install": "node install.js",
+ "lint": "echo 'Linting'",
+ "powershell": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command",
+ "quarkus:dev": "run-script-os",
+ "quarkus:dev:darwin:linux": "mvn clean package quarkus:dev -DskipTests",
+ "quarkus:dev:win32": "mvn clean package quarkus:dev -DskipTests"
+ },
+ "devDependencies": {
+ "@kie-tools/maven-config-setup-helper": "workspace:*",
+ "@kie-tools/root-env": "workspace:*",
+ "@kie-tools/serverless-workflow-dev-ui-webapp": "workspace:*",
+ "run-script-os": "^1.1.6"
+ },
+ "kieTools": {
+ "requiredPreinstalledCliCommands": [
+ "java",
+ "mvn"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/packages/sonataflow-quarkus-devui-extension/pom.xml b/packages/sonataflow-quarkus-devui-extension/pom.xml
new file mode 100644
index 00000000000..cded104b09b
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/pom.xml
@@ -0,0 +1,150 @@
+
+
+
+
+ 4.0.0
+ KIE Tools :: SonataFlow Quarkus Dev UI Extension
+ org.apache.kie.sonataflow
+ ${revision}
+ sonataflow-quarkus-devui-extension-parent
+ pom
+
+
+
+
+ central
+ Central Repository
+ https://repo.maven.apache.org/maven2
+ default
+
+ false
+
+
+
+ apache-public-repository-group
+ Apache Public Repository Group
+ https://repository.apache.org/content/groups/public/
+
+ true
+ never
+
+
+ true
+ daily
+
+
+
+
+
+ 3.12.1
+ 17
+ 17
+ UTF-8
+ 3.2.0
+ org.kie.kogito.quarkus.swf.dev.ui
+ 1.3.0
+ 3.2.9.Final
+ 999-20240218-SNAPSHOT
+
+
+
+ sonataflow-quarkus-devui-extension-deployment
+ sonataflow-quarkus-devui-extension
+
+
+
+
+
+ io.quarkus
+ quarkus-bom
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ flatten-maven-plugin
+ ${version.flatten.plugin}
+
+ true
+ resolveCiFriendliesOnly
+
+
+
+ flatten-revision
+ process-resources
+
+ flatten
+
+
+
+ flatten-revision-clean
+ clean
+
+ clean
+
+
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+
+
+ maven-surefire-plugin
+
+
+ org.jboss.logmanager.LogManager
+
+
+
+
+ maven-failsafe-plugin
+
+
+ org.jboss.logmanager.LogManager
+
+
+
+
+ maven-compiler-plugin
+
+
+
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/pom.xml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/pom.xml
new file mode 100644
index 00000000000..e319b361962
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/pom.xml
@@ -0,0 +1,201 @@
+
+
+
+ 4.0.0
+
+
+ org.apache.kie.sonataflow
+ sonataflow-quarkus-devui-extension-parent
+ ${revision}
+ ../pom.xml
+
+
+ sonataflow-quarkus-devui-extension-deployment
+ KIE Tools :: SonataFlow Quarkus Dev UI Extension :: Deployment
+
+
+ ../../serverless-workflow-dev-ui-webapp
+
+
+
+
+
+ io.quarkus
+ quarkus-development-mode-spi
+
+
+ io.quarkus
+ quarkus-core-deployment
+
+
+ io.quarkus
+ quarkus-arc-deployment
+
+
+ io.quarkus
+ quarkus-resteasy-deployment
+
+
+ io.quarkus
+ quarkus-resteasy-jackson-deployment
+
+
+ io.quarkus
+ quarkus-rest-client-deployment
+
+
+ io.quarkus
+ quarkus-resteasy-multipart-deployment
+
+
+
+
+ io.quarkus
+ quarkus-undertow-deployment
+
+
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.apache.kie.sonataflow
+ sonataflow-quarkus-devui-extension
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-junit5-internal
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${quarkus.platform.version}
+
+
+
+
+
+ maven-resources-plugin
+ ${version.resources.plugin}
+
+
+ copy-webapp
+ process-resources
+
+ copy-resources
+
+
+ ${basedir}/target/classes/dev-static/resources/webapp
+
+
+ ${path.to.webapp.app}/dist/resources/webapp
+ false
+
+
+ ${path.to.webapp.app}/dist/webapp
+ false
+
+
+
+
+
+
+ copy-envelope-resources
+ process-resources
+
+ copy-resources
+
+
+ ${basedir}/target/classes/dev-static
+
+
+ ${path.to.webapp.app}/dist
+
+ form-displayer.html
+ form-displayer.js
+ serverless-workflow-combined-editor-envelope.html
+ serverless-workflow-combined-editor-envelope.js
+ serverless-workflow-text-editor-envelope.html
+ serverless-workflow-text-editor-envelope.js
+ serverless-workflow-diagram-editor-envelope.html
+ serverless-workflow-diagram-editor-envelope.js
+ vendors-*.bundle.js
+ *.worker.js
+
+
+
+
+ ${path.to.webapp.app}/dist
+
+ diagram/
+
+
+
+
+
+
+
+ copy-index
+ process-resources
+
+ copy-resources
+
+
+ ${basedir}/target/classes/dev-static
+
+
+ ${basedir}/target/classes/static
+
+ index.html
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/DevConsoleProcessor.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/DevConsoleProcessor.java
new file mode 100644
index 00000000000..721a00c4e2e
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/DevConsoleProcessor.java
@@ -0,0 +1,133 @@
+/*
+ * 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.sonataflow.swf.tools.deployment;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.file.Path;
+
+import io.quarkus.deployment.IsDevelopment;
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
+import io.quarkus.deployment.builditem.ConfigurationBuildItem;
+import io.quarkus.deployment.builditem.LaunchModeBuildItem;
+import io.quarkus.deployment.builditem.LiveReloadBuildItem;
+import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
+import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
+import io.quarkus.deployment.util.WebJarUtil;
+import io.quarkus.devui.spi.page.CardPageBuildItem;
+import io.quarkus.devui.spi.page.Page;
+import io.quarkus.maven.dependency.ResolvedDependency;
+import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
+import io.quarkus.vertx.http.deployment.RouteBuildItem;
+import io.quarkus.vertx.http.runtime.devmode.DevConsoleRecorder;
+import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
+
+public class DevConsoleProcessor {
+
+ private static final String STATIC_RESOURCES_PATH = "dev-static/";
+ private static final String BASE_RELATIVE_URL = "/q/dev-v1/org.apache.kie.sonataflow.sonataflow-quarkus-devui-extension";
+
+ @BuildStep(onlyIf = IsDevelopment.class)
+ public CardPageBuildItem pages(NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
+ ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig,
+ LaunchModeBuildItem launchModeBuildItem,
+ ConfigurationBuildItem configurationBuildItem) throws UnsupportedEncodingException {
+
+ String uiPath = nonApplicationRootPathBuildItem.resolveManagementPath(BASE_RELATIVE_URL,
+ managementInterfaceBuildTimeConfig, launchModeBuildItem, true);
+
+ String devUIUrl = getProperty(configurationBuildItem, "kogito.dev-ui.url");
+ String devUIUrlQueryParam = devUIUrl != null ? "&devUIUrl=" + URLEncoder.encode(devUIUrl, "UTF-8") : "";
+
+ String dataIndexUrl = getProperty(configurationBuildItem, "kogito.data-index.url");
+ String dataIndexUrlQueryParam = dataIndexUrl != null ? "&dataIndexUrl=" + URLEncoder.encode(dataIndexUrl, "UTF-8") : "";
+
+ CardPageBuildItem cardPageBuildItem = new CardPageBuildItem();
+
+ cardPageBuildItem.addPage(Page.externalPageBuilder("Workflows")
+ .url(uiPath + "/index.html?page=Workflows" + devUIUrlQueryParam + dataIndexUrlQueryParam, uiPath)
+ .isHtmlContent()
+ .icon("font-awesome-solid:diagram-project"));
+
+ cardPageBuildItem.addPage(Page.externalPageBuilder("Monitoring")
+ .url(uiPath + "/index.html?page=Monitoring" + devUIUrlQueryParam + dataIndexUrlQueryParam, uiPath)
+ .isHtmlContent()
+ .icon("font-awesome-solid:gauge-high"));
+
+ return cardPageBuildItem;
+ }
+
+ @BuildStep(onlyIf = IsDevelopment.class)
+ @Record(ExecutionTime.RUNTIME_INIT)
+ public void deployStaticResources(final DevConsoleRecorder recorder,
+ final CurateOutcomeBuildItem curateOutcomeBuildItem,
+ final LiveReloadBuildItem liveReloadBuildItem,
+ final LaunchModeBuildItem launchMode,
+ final ShutdownContextBuildItem shutdownContext,
+ final BuildProducer routeBuildItemBuildProducer) throws IOException {
+ ResolvedDependency devConsoleResourcesArtifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem,
+ "org.apache.kie.sonataflow",
+ "sonataflow-quarkus-devui-extension-deployment");
+
+ Path devConsoleStaticResourcesDeploymentPath = WebJarUtil.copyResourcesForDevOrTest(
+ liveReloadBuildItem,
+ curateOutcomeBuildItem,
+ launchMode,
+ devConsoleResourcesArtifact,
+ STATIC_RESOURCES_PATH,
+ true);
+
+ routeBuildItemBuildProducer.produce(new RouteBuildItem.Builder()
+ .route(BASE_RELATIVE_URL + "/*")
+ .handler(recorder.devConsoleHandler(devConsoleStaticResourcesDeploymentPath.toString(),
+ shutdownContext))
+ .build());
+ }
+
+ private static String getProperty(ConfigurationBuildItem configurationBuildItem,
+ String propertyKey) {
+
+ String propertyValue = configurationBuildItem
+ .getReadResult()
+ .getAllBuildTimeValues()
+ .get(propertyKey);
+
+ if (propertyValue == null) {
+ propertyValue = configurationBuildItem
+ .getReadResult()
+ .getBuildTimeRunTimeValues()
+ .get(propertyKey);
+ } else {
+ return propertyValue;
+ }
+
+ if (propertyValue == null) {
+ propertyValue = configurationBuildItem
+ .getReadResult()
+ .getRunTimeDefaultValues()
+ .get(propertyKey);
+ }
+
+ return propertyValue;
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/SonataFlowQuarkusExtensionProcessor.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/SonataFlowQuarkusExtensionProcessor.java
new file mode 100644
index 00000000000..60d10d1853b
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/java/org/kie/sonataflow/swf/tools/deployment/SonataFlowQuarkusExtensionProcessor.java
@@ -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 org.kie.sonataflow.swf.tools.deployment;
+
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.FeatureBuildItem;
+
+class SonataFlowQuarkusExtensionProcessor {
+
+ private static final String FEATURE = "sonataflow-quarkus-devui-extension";
+
+ @BuildStep
+ FeatureBuildItem feature() {
+ return new FeatureBuildItem(FEATURE);
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/embedded.html b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/embedded.html
new file mode 100644
index 00000000000..da2e7069430
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/embedded.html
@@ -0,0 +1,53 @@
+
+
+
+ Workflow Instances
+
+
+
+
+ Monitoring
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/monitoring.html b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/monitoring.html
new file mode 100644
index 00000000000..2132fd4c35c
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/monitoring.html
@@ -0,0 +1,44 @@
+
+{#include main fluid=true} {#style} .main-container { margin: 0; padding: 0; } #envelope-app { width: 100vw; height:
+calc(100vh - 98px); } {/style} {#title}Runtime UI{/title} {#body}
+
+
+
+
+{/body} {/include}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/workflowInstances.html b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/workflowInstances.html
new file mode 100644
index 00000000000..90370483336
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/dev-templates/workflowInstances.html
@@ -0,0 +1,45 @@
+
+{#include main fluid=true} {#style} .main-container { margin: 0; padding: 0; } #envelope-app { width: 100vw; height:
+calc(100vh - 98px); } {/style} {#title}Runtime UI{/title} {#body}
+
+
+
+
+{/body} {/include}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/static/index.html b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/static/index.html
new file mode 100644
index 00000000000..8e850ece508
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension-deployment/src/main/resources/static/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/pom.xml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/pom.xml
new file mode 100644
index 00000000000..87e8bf44fe4
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/pom.xml
@@ -0,0 +1,159 @@
+
+
+
+ 4.0.0
+
+ org.apache.kie.sonataflow
+ sonataflow-quarkus-devui-extension-parent
+ ${revision}
+ ../pom.xml
+
+ sonataflow-quarkus-devui-extension
+ KIE Tools :: SonataFlow Quarkus Dev UI Extension :: Runtime
+ Runtime development tools for Serverless Workflows
+
+
+
+ io.quarkus
+ quarkus-core
+
+
+
+ io.quarkus
+ quarkus-resteasy
+
+
+
+ io.quarkus
+ quarkus-resteasy-jackson
+
+
+
+ io.quarkus
+ quarkus-rest-client
+
+
+
+ io.quarkus
+ quarkus-resteasy-multipart
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+
+ io.quarkus.arc
+ arc-processor
+
+
+
+
+ io.quarkus
+ quarkus-undertow
+
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+
+ io.quarkus
+ quarkus-junit5-mockito
+ test
+
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+
+
+
+ io.quarkus
+ quarkus-extension-maven-plugin
+ ${quarkus.platform.version}
+
+
+ compile
+
+ extension-descriptor
+
+
+ ${project.groupId}:${project.artifactId}-deployment:${project.version}
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+
+ io.quarkus
+ quarkus-extension-processor
+ ${quarkus.platform.version}
+
+
+
+
+
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardService.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardService.java
new file mode 100644
index 00000000000..530819d4cae
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardService.java
@@ -0,0 +1,83 @@
+/*
+ * 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.sonataflow.swf.tools.custom.dashboard;
+
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+
+@Path("/customDashboard")
+public class CustomDashboardService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CustomDashboardService.class);
+
+ private CustomDashboardStorage storage;
+
+ @Inject
+ public void setStorage(CustomDashboardStorage storage) {
+ this.storage = storage;
+ }
+
+ @GET
+ @Path("/count")
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response getCustomDashboardFilesCount() {
+ try {
+ return Response.ok(storage.getCustomDashboardFilesCount()).build();
+ } catch (Exception e) {
+ LOGGER.warn("Error while getting CustomDashboard file count: ", e);
+ return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard files count: " + e.getMessage()).build();
+ }
+ }
+
+ @GET
+ @Path("/list")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomDashboardFiles(@QueryParam("names") CustomDashboardFilter filter) {
+ try {
+ return Response.ok(storage.getCustomDashboardFiles(filter)).build();
+ } catch (Exception e) {
+ LOGGER.warn("Error while getting CustomDashboard list: ", e);
+ return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard files list: " + e.getMessage()).build();
+ }
+ }
+
+ @GET
+ @Path("/{name:\\S+}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getCustomDashboardFileContent(@PathParam("name") String name) {
+ try {
+ return Response.ok(storage.getCustomDashboardFileContent(name)).build();
+ } catch (Exception e) {
+ LOGGER.warn("Error while getting CustomDashboard file content: ", e);
+ return Response.status(INTERNAL_SERVER_ERROR.getStatusCode(), "Unexpected error while getting CustomDashboard file content: " + e.getMessage()).build();
+ }
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardStorage.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardStorage.java
new file mode 100644
index 00000000000..29f6096ad69
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/CustomDashboardStorage.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.sonataflow.swf.tools.custom.dashboard;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardInfo;
+
+public interface CustomDashboardStorage {
+
+ int getCustomDashboardFilesCount();
+
+ Collection getCustomDashboardFiles(CustomDashboardFilter filter);
+
+ String getCustomDashboardFileContent(String name) throws IOException;
+
+ void updateCustomDashboard(String content);
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.java
new file mode 100644
index 00000000000..fe648a586ad
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverter.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.sonataflow.swf.tools.custom.dashboard.converter;
+
+import java.util.Collections;
+import java.util.StringTokenizer;
+import java.util.stream.Collectors;
+
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+
+import jakarta.ws.rs.ext.ParamConverter;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+public class CustomDashboardFilterParamConverter implements ParamConverter {
+ public CustomDashboardFilter fromString(String names) {
+ StringTokenizer stringTokenizer = new StringTokenizer(names, ";");
+ return new CustomDashboardFilter(Collections.list(stringTokenizer).stream().map(s -> (String) s).collect(Collectors.toList()));
+ }
+
+ public String toString(CustomDashboardFilter names) {
+ return names.toString();
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.java
new file mode 100644
index 00000000000..dcf1487657b
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/converter/CustomDashboardFilterParamConverterProvider.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.sonataflow.swf.tools.custom.dashboard.converter;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+
+import jakarta.ws.rs.ext.ParamConverter;
+import jakarta.ws.rs.ext.ParamConverterProvider;
+import jakarta.ws.rs.ext.Provider;
+
+@Provider
+public class CustomDashboardFilterParamConverterProvider implements ParamConverterProvider {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) {
+ if (rawType.isAssignableFrom(CustomDashboardFilter.class)) {
+ return (ParamConverter) new CustomDashboardFilterParamConverter();
+ }
+ return null;
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java
new file mode 100644
index 00000000000..5060c20f749
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageImpl.java
@@ -0,0 +1,226 @@
+/*
+ * 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.sonataflow.swf.tools.custom.dashboard.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.WatchService;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.eclipse.microprofile.config.ConfigProvider;
+import org.kie.sonataflow.swf.tools.custom.dashboard.CustomDashboardStorage;
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import jakarta.enterprise.context.ApplicationScoped;
+
+import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
+import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+
+@ApplicationScoped
+public class CustomDashboardStorageImpl implements CustomDashboardStorage {
+
+ public static final String PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP = "quarkus.kogito-runtime-tools.custom.dashboard.folder";
+ private static final String CUSTOM_DASHBOARD_STORAGE_PATH = "/dashboards/";
+ private static final Logger LOGGER = LoggerFactory.getLogger(CustomDashboardStorageImpl.class);
+
+ private final Map customDashboardInfoMap = new HashMap<>();
+
+ private URL classLoaderCustomDashboardUrl;
+ private URL customDashStorageUrl;
+
+ public CustomDashboardStorageImpl() {
+ start(Thread.currentThread().getContextClassLoader().getResource(CUSTOM_DASHBOARD_STORAGE_PATH));
+ }
+
+ public CustomDashboardStorageImpl(final URL classLoaderFormsUrl) {
+ start(classLoaderFormsUrl);
+ }
+
+ private void start(final URL classLoaderFormsUrl) {
+ start(classLoaderFormsUrl, getCustomDashboardStorageUrl(classLoaderFormsUrl));
+ }
+
+ private void start(final URL classLoaderCustomDashboardUrl, final URL customDashStorageUrl) {
+ try {
+ this.classLoaderCustomDashboardUrl = classLoaderCustomDashboardUrl;
+ this.customDashStorageUrl = customDashStorageUrl;
+ } catch (Exception ex) {
+ LOGGER.warn("Couldn't properly initialize CustomDashboardStorageImpl");
+ } finally {
+ if (classLoaderCustomDashboardUrl == null) {
+ return;
+ }
+
+ init(readCustomDashboardResources());
+ String storageUrl = getStorageUrl(classLoaderCustomDashboardUrl);
+ Thread t = new Thread(new DashboardFilesWatcher(reload(), storageUrl));
+ t.start();
+ }
+ }
+
+ protected String getStorageUrl(URL classLoaderCustomDashboardUrl) {
+ return ConfigProvider.getConfig()
+ .getOptionalValue(PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP, String.class)
+ .orElseGet(() -> classLoaderCustomDashboardUrl.getFile());
+ }
+
+ private URL getCustomDashboardStorageUrl(URL classLoaderCustomDashboardUrl) {
+ if (classLoaderCustomDashboardUrl == null) {
+ return null;
+ }
+
+ String storageUrl = getStorageUrl(classLoaderCustomDashboardUrl);
+
+ File customDashStorageeFolder = new File(storageUrl);
+
+ if (!customDashStorageeFolder.exists() || !customDashStorageeFolder.isDirectory()) {
+ LOGGER.warn("Cannot initialize form storage folder in path '" + customDashStorageeFolder.getPath() + "'");
+ }
+
+ try {
+ return customDashStorageeFolder.toURI().toURL();
+ } catch (MalformedURLException ex) {
+ LOGGER.warn("Cannot initialize form storage folder in path '" + customDashStorageeFolder.getPath() + "'", ex);
+ }
+ return null;
+ }
+
+ @Override
+ public int getCustomDashboardFilesCount() {
+ return customDashboardInfoMap.size();
+ }
+
+ @Override
+ public Collection getCustomDashboardFiles(CustomDashboardFilter filter) {
+ if (filter != null && !filter.getNames().isEmpty()) {
+ return customDashboardInfoMap.entrySet().stream()
+ .filter(entry -> StringUtils.containsAnyIgnoreCase(entry.getKey(), filter.getNames().toArray(new String[0])))
+ .map(Map.Entry::getValue)
+ .collect(Collectors.toList());
+ } else {
+ return customDashboardInfoMap.values();
+ }
+ }
+
+ @Override
+ public String getCustomDashboardFileContent(String name) throws IOException {
+ try {
+ return IOUtils.toString(new FileInputStream(customDashboardInfoMap.get(name).getPath()), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ LOGGER.info("custom-dashboard's file {} can not ready, because of {}", customDashboardInfoMap.get(name).getPath(), e.getMessage());
+ throw e;
+ }
+ }
+
+ @Override
+ public void updateCustomDashboard(String content) {
+
+ }
+
+ private void init(Collection files) {
+ customDashboardInfoMap.clear();
+ files.stream()
+ .forEach(file -> {
+ LocalDateTime lastModified = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), TimeZone.getDefault().toZoneId());
+ customDashboardInfoMap.put(file.getName(),
+ new CustomDashboardInfo(file.getName(), file.getPath(), lastModified));
+ });
+ }
+
+ private Collection readCustomDashboardResources() {
+ if (classLoaderCustomDashboardUrl != null) {
+ LOGGER.info("custom-dashboard's files path is {}", classLoaderCustomDashboardUrl.toString());
+ File rootFolder = FileUtils.toFile(classLoaderCustomDashboardUrl);
+ return FileUtils.listFiles(rootFolder, new String[] { "dash.yaml", "dash.yml" }, true);
+ }
+ return Collections.emptyList();
+ }
+
+ private Consumer> reload() {
+ return this::init;
+ }
+
+ private class DashboardFilesWatcher implements Runnable {
+
+ private final Map keys = new HashMap<>();
+ private Consumer> consumer;
+ private String folder;
+
+ public DashboardFilesWatcher(Consumer> consumer, String folder) {
+ this.consumer = consumer;
+ this.folder = folder;
+ }
+
+ @Override
+ public void run() {
+ try (WatchService ws = FileSystems.getDefault().newWatchService()) {
+ Path path = Path.of(folder);
+ keys.put(path.register(ws, ENTRY_MODIFY, ENTRY_CREATE), path);
+
+ Files.walkFileTree(path, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+ keys.put(dir.register(ws, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE), dir);
+ return FileVisitResult.CONTINUE;
+ }
+ });
+ WatchKey key;
+ while ((key = ws.take()) != null) {
+ for (WatchEvent> event : key.pollEvents()) {
+ LOGGER.warn("Event kind: {}. File affected: {}", event.kind(), event.context());
+ consumer.accept(readCustomDashboardResources());
+ }
+ key.reset();
+ }
+ } catch (InterruptedException e) {
+ LOGGER.warn("Exception in custom dashboard folder watcher for folder: {}, message: {}", folder, e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ } catch (IOException ex) {
+ LOGGER.warn("Exception in custom dashboard folder watcher for folder: {}, message: {}", folder, ex.getMessage(), ex);
+ }
+ }
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardFilter.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardFilter.java
new file mode 100644
index 00000000000..e4a59b5917e
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardFilter.java
@@ -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.
+ */
+package org.kie.sonataflow.swf.tools.custom.dashboard.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CustomDashboardFilter {
+
+ private final List names;
+
+ public CustomDashboardFilter() {
+ this.names = new ArrayList<>();
+ }
+
+ public CustomDashboardFilter(List names) {
+ this.names = names;
+ }
+
+ public List getNames() {
+ return names;
+ }
+
+ public void setNames(List names) {
+ this.names.addAll(names);
+ }
+
+ @Override
+ public String toString() {
+ return "CustomDashboardFilter{" +
+ "names=" + names +
+ '}';
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardInfo.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardInfo.java
new file mode 100644
index 00000000000..7c873cbd136
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/java/org/kie/sonataflow/swf/tools/custom/dashboard/model/CustomDashboardInfo.java
@@ -0,0 +1,95 @@
+/*
+ * 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.sonataflow.swf.tools.custom.dashboard.model;
+
+import java.time.LocalDateTime;
+
+public class CustomDashboardInfo {
+ String name;
+ String path;
+ LocalDateTime lastModified;
+
+ public CustomDashboardInfo(String name, String path, LocalDateTime lastModified) {
+ this.name = name;
+ this.path = path;
+ this.lastModified = lastModified;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public LocalDateTime getLastModified() {
+ return lastModified;
+ }
+
+ public void setLastModified(LocalDateTime lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CustomDashboardInfo that = (CustomDashboardInfo) o;
+
+ if (name != null ? !name.equals(that.name) : that.name != null) {
+ return false;
+ }
+ if (path != null ? !path.equals(that.path) : that.path != null) {
+ return false;
+ }
+ return lastModified != null ? lastModified.equals(that.lastModified) : that.lastModified == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name != null ? name.hashCode() : 0;
+ result = 31 * result + (path != null ? path.hashCode() : 0);
+ result = 31 * result + (lastModified != null ? lastModified.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CustomDashboardInfo{" +
+ "name='" + name + '\'' +
+ ", path='" + path + '\'' +
+ ", lastModified=" + lastModified +
+ '}';
+ }
+
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/beans.xml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/beans.xml
new file mode 100644
index 00000000000..030bdcbec5b
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/beans.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/quarkus-extension.yaml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/quarkus-extension.yaml
new file mode 100644
index 00000000000..06ec65f1e4c
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/main/resources/META-INF/quarkus-extension.yaml
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+name: Serverless Workflow Tools
+metadata:
+ keywords:
+ - "sonataflow"
+ - "workflows"
+ guide: "https://quarkus.io/guides/kogito"
+ categories:
+ - "business-automation"
+ status: "preview"
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java
new file mode 100644
index 00000000000..912c021d9a6
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.sonataflow.swf.tools.custom.dashboard.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+import org.kie.sonataflow.swf.tools.custom.dashboard.CustomDashboardStorage;
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardFilter;
+import org.kie.sonataflow.swf.tools.custom.dashboard.model.CustomDashboardInfo;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class CustomDashboardStorageTest {
+
+ private static String[] DASHBOARD_NAMES = { "age.dash.yml", "products.dash.yaml" };
+ private static String DASHBOARD_NAME = "age.dash.yml";
+
+ private CustomDashboardStorage customDashboardStorage;
+ private URL tempFolder;
+
+ @BeforeAll
+ public void init() {
+ tempFolder = Thread.currentThread().getContextClassLoader().getResource("custom/dashboards/");
+
+ customDashboardStorage = new CustomDashboardStorageImpl(tempFolder);
+ }
+
+ @Test
+ public void testGetFormInfoList() {
+ Collection customDashboardInfoFilterAll = customDashboardStorage.getCustomDashboardFiles(null);
+ assertEquals(2, customDashboardInfoFilterAll.size());
+
+ CustomDashboardFilter filterEmpty = new CustomDashboardFilter();
+ filterEmpty.setNames(Collections.emptyList());
+ Collection customDashboardInfoAllEmptyFilter = customDashboardStorage.getCustomDashboardFiles(filterEmpty);
+ assertEquals(2, customDashboardInfoAllEmptyFilter.size());
+
+ CustomDashboardFilter filter = new CustomDashboardFilter();
+ filter.setNames(Arrays.asList(DASHBOARD_NAMES));
+
+ Collection formInfos = customDashboardStorage.getCustomDashboardFiles(filter);
+ assertEquals(2, formInfos.size());
+ }
+
+ @Test
+ public void testHotReloading() throws IOException {
+ String storageUrl = Thread.currentThread().getContextClassLoader().getResource("custom/dashboards/").getFile();
+ File srcFile = new File(storageUrl + "products.dash.yaml");
+ File targetFile = new File(storageUrl + "copy.dash.yml");
+
+ assertEquals(false, targetFile.exists());
+ FileUtils.copyFile(srcFile, targetFile);
+ assertEquals(true, targetFile.exists());
+ await().atMost(20, TimeUnit.SECONDS).until(() -> testBeforeDelete());
+ Collection customDashboardInfoFilterAllBeforeDelete = customDashboardStorage.getCustomDashboardFiles(null);
+ assertEquals(3, customDashboardInfoFilterAllBeforeDelete.size());
+
+ assertEquals(true, targetFile.exists());
+ FileUtils.delete(targetFile);
+ assertEquals(false, targetFile.exists());
+ await().atMost(20, TimeUnit.SECONDS).until(() -> testAfterDelete());
+
+ Collection customDashboardInfoFilterAllAfterDelete = customDashboardStorage.getCustomDashboardFiles(null);
+ assertEquals(2, customDashboardInfoFilterAllAfterDelete.size());
+ }
+
+ private boolean testBeforeDelete() {
+ if (customDashboardStorage.getCustomDashboardFiles(null).size() == 3) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean testAfterDelete() {
+ if (customDashboardStorage.getCustomDashboardFiles(null).size() == 2) {
+ return true;
+ }
+ return false;
+ }
+
+ @Test
+ public void testGetFormContent() throws IOException {
+ String content = customDashboardStorage.getCustomDashboardFileContent(DASHBOARD_NAME);
+ assertNotNull(content);
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java
new file mode 100644
index 00000000000..9b9e3350be9
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/java/org/kie/sonataflow/swf/tools/custom/dashboard/impl/CustomDashboardStorageTestProfile.java
@@ -0,0 +1,46 @@
+/*
+ * 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.sonataflow.swf.tools.custom.dashboard.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Collections;
+import java.util.Map;
+
+import io.quarkus.test.junit.QuarkusTestProfile;
+
+import static org.kie.sonataflow.swf.tools.custom.dashboard.impl.CustomDashboardStorageImpl.PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP;
+
+public class CustomDashboardStorageTestProfile implements QuarkusTestProfile {
+
+ private String storagePath;
+
+ public CustomDashboardStorageTestProfile() throws IOException {
+ File storage = Files.createTempDirectory("CustomDashboardStorageTestProfile").toFile();
+ storage.deleteOnExit();
+ storage.mkdir();
+ storagePath = storage.getAbsolutePath();
+ }
+
+ @Override
+ public Map getConfigOverrides() {
+ return Collections.singletonMap(PROJECT_CUSTOM_DASHBOARD_STORAGE_PROP, storagePath);
+ }
+}
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/products.dash.yaml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/products.dash.yaml
new file mode 100644
index 00000000000..ea685a0b5c3
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/products.dash.yaml
@@ -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.
+#
+
+datasets:
+ - uuid: products
+ content: >-
+ [
+ ["Computers", "Scanner", 5, 3],
+ ["Computers", "Printer", 7, 4],
+ ["Computers", "Laptop", 3, 2],
+ ["Electronics", "Camera", 10, 7],
+ ["Electronics", "Headphones", 5, 9]
+ ]
+ columns:
+ - id: Section
+ type: LABEL
+ - id: Product
+ type: LABEL
+ - id: Quantity
+ type: NUMBER
+ - id: Quantity2
+ type: NUMBER
+pages:
+ - components:
+ - html: Welcome to Dashbuilder!
+ properties:
+ font-size: xx-large
+ margin-bottom: 30px
+ - settings:
+ type: BARCHART
+ dataSetLookup:
+ uuid: products
+ group:
+ - columnGroup:
+ source: Product
+ groupFunctions:
+ - source: Product
+ - source: Quantity
+ function: SUM
+ - source: Quantity2
+ function: SUM
+ - settings:
+ type: TABLE
+ dataSetLookup:
+ uuid: products
diff --git a/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/subdir/age.dash.yml b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/subdir/age.dash.yml
new file mode 100644
index 00000000000..011aff8684e
--- /dev/null
+++ b/packages/sonataflow-quarkus-devui-extension/sonataflow-quarkus-devui-extension/src/test/resources/custom/dashboards/subdir/age.dash.yml
@@ -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.
+#
+
+datasets:
+ - uuid: age
+ content: >-
+ [
+ ["John", 5],
+ ["Mary", 7],
+ ["Mark", 3]
+ ]
+ columns:
+ - id: Name
+ type: LABEL
+ - id: Age
+ type: Number
+pages:
+ - components:
+ - settings:
+ type: BARCHART
+ dataSetLookup:
+ uuid: age
+ group:
+ - columnGroup:
+ source: Name
+ groupFunctions:
+ - source: Name
+ - source: Age
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3dea46698d3..1308a8fde1b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9212,6 +9212,21 @@ importers:
specifier: ^5.9.0
version: 5.9.0
+ packages/sonataflow-quarkus-devui-extension:
+ devDependencies:
+ "@kie-tools/maven-config-setup-helper":
+ specifier: workspace:*
+ version: link:../maven-config-setup-helper
+ "@kie-tools/root-env":
+ specifier: workspace:*
+ version: link:../root-env
+ "@kie-tools/serverless-workflow-dev-ui-webapp":
+ specifier: workspace:*
+ version: link:../serverless-workflow-dev-ui-webapp
+ run-script-os:
+ specifier: ^1.1.6
+ version: 1.1.6
+
packages/storybook-base:
devDependencies:
"@kie-tools-core/webpack-base":
diff --git a/repo/graph.dot b/repo/graph.dot
index 30d6dff5996..ec49e8db3f4 100644
--- a/repo/graph.dot
+++ b/repo/graph.dot
@@ -150,6 +150,7 @@ digraph G {
"@kie-tools/serverless-workflow-service-catalog" [ color = "blue", fontcolor = "blue", style = "rounded" ];
"@kie-tools/serverless-workflow-language-service" [ color = "blue", fontcolor = "blue", style = "rounded" ];
"swf-vscode-extension" [ color = "blue", fontcolor = "blue", style = "rounded" ];
+ "@kie-tools/sonataflow-quarkus-devui-extension" [ color = "black", fontcolor = "black", style = "dashed, rounded" ];
"@kie-tools/stunner-editors-dmn-loader" [ color = "blue", fontcolor = "blue", style = "rounded" ];
"@kie-tools/unitables" [ color = "blue", fontcolor = "blue", style = "rounded" ];
"vscode-extension-dashbuilder-editor" [ color = "blue", fontcolor = "blue", style = "rounded" ];
@@ -462,6 +463,7 @@ digraph G {
"swf-vscode-extension" -> "@kie-tools/vscode-extension-common-test-helpers" [ style = "dashed", color = "blue" ];
"sonataflow-deployment-webapp" -> "@kie-tools-core/react-hooks" [ style = "dashed", color = "blue" ];
"sonataflow-deployment-webapp" -> "@kie-tools/runtime-tools-webapp-components" [ style = "dashed", color = "blue" ];
+ "@kie-tools/sonataflow-quarkus-devui-extension" -> "@kie-tools/serverless-workflow-dev-ui-webapp" [ style = "dashed", color = "black" ];
"@kie-tools/storybook-base" -> "@kie-tools-core/webpack-base" [ style = "dashed", color = "blue" ];
"@kie-tools/storybook-base" -> "@kie-tools/tsconfig" [ style = "dashed", color = "blue" ];
"@kie-tools/stunner-editors" -> "@kie-tools/stunner-editors-dmn-loader" [ style = "solid", color = "black" ];
diff --git a/repo/graph.json b/repo/graph.json
index 3440aac19f1..7014bcd673e 100644
--- a/repo/graph.json
+++ b/repo/graph.json
@@ -155,6 +155,7 @@
{ "id": "@kie-tools/serverless-workflow-jq-expressions" },
{ "id": "@kie-tools/serverless-workflow-service-catalog" },
{ "id": "swf-vscode-extension" },
+ { "id": "@kie-tools/sonataflow-quarkus-devui-extension" },
{ "id": "vscode-extension-dashbuilder-editor" },
{ "id": "vscode-extension-kie-ba-bundle" },
{ "id": "vscode-extension-kogito-bundle" },
@@ -963,6 +964,11 @@
"weight": 1
},
{ "source": "swf-vscode-extension", "target": "@kie-tools/vscode-extension-common-test-helpers", "weight": 1 },
+ {
+ "source": "@kie-tools/sonataflow-quarkus-devui-extension",
+ "target": "@kie-tools/serverless-workflow-dev-ui-webapp",
+ "weight": 1
+ },
{ "source": "vscode-extension-dashbuilder-editor", "target": "@kie-tools-core/vscode-extension", "weight": 1 },
{
"source": "vscode-extension-dashbuilder-editor",
@@ -1141,6 +1147,7 @@
["@kie-tools/serverless-workflow-text-editor", "packages/serverless-workflow-text-editor"],
["swf-vscode-extension", "packages/serverless-workflow-vscode-extension"],
["sonataflow-deployment-webapp", "packages/sonataflow-deployment-webapp"],
+ ["@kie-tools/sonataflow-quarkus-devui-extension", "packages/sonataflow-quarkus-devui-extension"],
["@kie-tools/storybook-base", "packages/storybook-base"],
["@kie-tools/stunner-editors", "packages/stunner-editors"],
["@kie-tools/stunner-editors-dmn-loader", "packages/stunner-editors-dmn-loader"],