diff --git a/.ci/kie-tools-ci-build.Dockerfile b/.ci/incubator-kie-tools-ci-build.Dockerfile similarity index 93% rename from .ci/kie-tools-ci-build.Dockerfile rename to .ci/incubator-kie-tools-ci-build.Dockerfile index 52036a7594a..0d186be2ab8 100644 --- a/.ci/kie-tools-ci-build.Dockerfile +++ b/.ci/incubator-kie-tools-ci-build.Dockerfile @@ -1,4 +1,4 @@ -FROM cruizba/ubuntu-dind:latest +FROM cruizba/ubuntu-dind:noble-26.1.3 SHELL ["/bin/bash", "-c"] @@ -23,7 +23,6 @@ libgtk-3-dev \ libssl-dev \ libxi6 \ libnss3 \ -libgconf-2-4 \ libpci-dev \ libglvnd0 \ libbtrfs-dev \ @@ -34,6 +33,7 @@ python3-pip \ python3-dev \ python3-venv \ python3-gssapi \ +gettext \ git \ jq \ vim \ @@ -42,7 +42,8 @@ zip \ unzip \ bzip2 \ xvfb \ -fluxbox && \ +fluxbox \ +subversion && \ apt-get clean autoclean && apt-get autoremove --yes && \ rm -rf /var/lib/{apt,dpkg,cache,log}/ @@ -78,13 +79,13 @@ RUN wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | # Node setup RUN source $HOME/.nvm/nvm.sh && \ - nvm install 18.14.0 && \ + nvm install 20.14.0 && \ sudo update-alternatives --install /usr/local/bin/node node $(which node) 1 && \ sudo update-alternatives --install /usr/local/bin/npm npm $(which npm) 1 # PNPM setup RUN source $HOME/.nvm/nvm.sh && \ - npm install -g pnpm@8.7.0 && \ + npm install -g pnpm@9.3.0 && \ sudo update-alternatives --install /usr/local/bin/pnpm pnpm $(which pnpm) 1 # Maven setup @@ -97,8 +98,8 @@ RUN curl -s "https://get.sdkman.io" | bash && \ sdk flush # Golang setup -RUN wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz -P /tmp && \ - sudo tar xzf /tmp/go1.21.5.linux-amd64.tar.gz -C /opt && rm /tmp/go1.21.5.linux-amd64.tar.gz && \ +RUN wget https://go.dev/dl/go1.21.9.linux-amd64.tar.gz -P /tmp && \ + sudo tar xzf /tmp/go1.21.9.linux-amd64.tar.gz -C /opt && rm /tmp/go1.21.9.linux-amd64.tar.gz && \ echo 'export GOPATH=${HOME}/go' | sudo tee /etc/profile.d/go.sh && \ echo 'export PATH=${PATH}:/opt/go/bin:${GOPATH}/bin' | sudo tee -a /etc/profile.d/go.sh && \ echo "source /etc/profile.d/go.sh" >> $HOME/.bashrc && \ @@ -130,7 +131,7 @@ RUN go install github.com/openshift/source-to-image/cmd/s2i@v1.3.9 ENV HOME="/home/nonrootuser" ENV JAVA_HOME="${HOME}/.sdkman/candidates/java/current/" ENV MAVEN_HOME="${HOME}/.sdkman/candidates/maven/current/" -ENV NODE_HOME="${HOME}/.nvm/versions/node/v18.14.0" +ENV NODE_HOME="${HOME}/.nvm/versions/node/v20.14.0" ENV DISPLAY=":99" ENV NODE_OPTIONS="--max_old_space_size=4096" ENV GOPATH="${HOME}/go" diff --git a/.ci/jenkins/Jenkinsfile.ci-main b/.ci/jenkins/Jenkinsfile.ci-main index ca2acd42c38..aec1b1f1f5b 100644 --- a/.ci/jenkins/Jenkinsfile.ci-main +++ b/.ci/jenkins/Jenkinsfile.ci-main @@ -55,7 +55,7 @@ pipeline { "${pipelineVars.kieToolsBotGithubCredentialsId}" ) - BUILD_IMAGE_REQUIRED = githubUtils.fileIsInChangeset('kie-tools-ci-build.Dockerfile') + BUILD_IMAGE_REQUIRED = githubUtils.fileIsInChangeset('incubator-kie-tools-ci-build.Dockerfile') } } } diff --git a/.ci/jenkins/Jenkinsfile.daily-dev-publish b/.ci/jenkins/Jenkinsfile.daily-dev-publish index 32336bcd44d..77e743005a0 100644 --- a/.ci/jenkins/Jenkinsfile.daily-dev-publish +++ b/.ci/jenkins/Jenkinsfile.daily-dev-publish @@ -20,7 +20,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' label util.avoidFaultyNodes() } @@ -38,99 +38,108 @@ pipeline { KIE_TOOLS_BUILD__runTests = 'false' KIE_TOOLS_BUILD__runEndToEndTests = 'false' - DEV_DEPLOYMENT_BASE_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_BASE_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_BASE_IMAGE__name = 'dev-deployment-base-image' - DEV_DEPLOYMENT_BASE_IMAGE__buildTags = 'daily-dev' + DEV_DEPLOYMENT_BASE_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_BASE_IMAGE__account = 'apache' + DEV_DEPLOYMENT_BASE_IMAGE__name = 'incubator-kie-sandbox-dev-deployment-base' + DEV_DEPLOYMENT_BASE_IMAGE__buildTag = 'daily-dev' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name = 'dev-deployment-kogito-quarkus-blank-app-image' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags = 'daily-dev' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account = 'apache' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name = 'incubator-dev-deployment-kogito-quarkus-blank-app' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag = 'daily-dev' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'dev-deployment-dmn-form-webapp-image' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags = 'daily-dev' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'apache' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'incubator-dev-deployment-dmn-form-webapp' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag = 'daily-dev' - ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentBaseImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentBaseImageName = 'dev-deployment-base-image' + ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base' ONLINE_EDITOR__devDeploymentBaseImageTag = 'daily-dev' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'dev-deployment-dmn-form-webapp-image' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp' ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = 'daily-dev' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-kogito-quarkus-blank-app' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageTag = 'daily-dev' ONLINE_EDITOR__corsProxyUrl = 'https://daily-dev-cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud' EXTENDED_SERVICES__kieSandboxUrl = 'https://apache.github.io/incubator-kie-kogito-online/dev' - KIE_SANDBOX__imageRegistry = 'quay.io' - KIE_SANDBOX__imageAccount = 'kie-tools' - KIE_SANDBOX__imageName = 'kie-sandbox-image' - KIE_SANDBOX__imageBuildTags = 'daily-dev' + KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry = 'docker.io' + KIE_SANDBOX_WEBAPP_IMAGE__imageAccount = 'apache' + KIE_SANDBOX_WEBAPP_IMAGE__imageName = 'incubator-kie-sandbox-webapp' + KIE_SANDBOX_WEBAPP_IMAGE__imageBuildTags = 'daily-dev' - KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry = 'quay.io' - KIE_SANDBOX_EXTENDED_SERVICES__imageAccount = 'kie-tools' - KIE_SANDBOX_EXTENDED_SERVICES__imageName = 'kie-sandbox-extended-services-image' + KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry = 'docker.io' + KIE_SANDBOX_EXTENDED_SERVICES__imageAccount = 'apache' + KIE_SANDBOX_EXTENDED_SERVICES__imageName = 'incubator-kie-sandbox-extended-services' KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags = 'daily-dev' - CORS_PROXY_IMAGE__imageRegistry = 'quay.io' - CORS_PROXY_IMAGE__imageAccount = 'kie-tools' - CORS_PROXY_IMAGE__imageName = 'cors-proxy-image' + CORS_PROXY_IMAGE__imageRegistry = 'docker.io' + CORS_PROXY_IMAGE__imageAccount = 'apache' + CORS_PROXY_IMAGE__imageName = 'incubator-kie-cors-proxy' CORS_PROXY_IMAGE__imageBuildTags = 'daily-dev' - KIE_SANDBOX_HELM_CHART__registry = 'quay.io' - KIE_SANDBOX_HELM_CHART__account = 'kie-tools' - KIE_SANDBOX_HELM_CHART__name = 'kie-sandbox-helm-chart' + KIE_SANDBOX_HELM_CHART__registry = 'docker.io' + KIE_SANDBOX_HELM_CHART__account = 'apache' + KIE_SANDBOX_HELM_CHART__name = 'incubator-kie-sandbox-helm-chart' KIE_SANDBOX_HELM_CHART__tag = '0.0.0-daily-dev' OPENSHIFT_NAMESPACE = 'kie-sandbox' OPENSHIFT_PART_OF = 'daily-dev-kie-sandbox-app' DEPLOY_TAG = 'daily-dev' - DASHBUILDER__viewerImageRegistry = 'quay.io' - DASHBUILDER__viewerImageAccount = 'kie-tools' - DASHBUILDER__viewerImageName = 'dashbuilder-viewer-image' + DASHBUILDER__viewerImageRegistry = 'docker.io' + DASHBUILDER__viewerImageAccount = 'apache' + DASHBUILDER__viewerImageName = 'incubator-kie-dashbuilder-viewer' DASHBUILDER__viewerImageBuildTags = 'daily-dev' SERVERLESS_LOGIC_WEB_TOOLS__dashbuilderViewerImageTag = 'daily-dev' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'serverless-logic-web-tools-swf-builder-image' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'incubator-serverless-logic-web-tools-swf-builder' SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageTag = 'daily-dev' SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags = 'daily-dev' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'serverless-logic-web-tools-base-builder-image' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'incubator-kie-serverless-logic-web-tools-base-builder' SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageTag = 'daily-dev' SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags = 'daily-dev' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'serverless-logic-web-tools-swf-dev-mode-image' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'incubator-kie-serverless-logic-web-tools-swf-dev-mode' SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageTag = 'daily-dev' SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags = 'daily-dev' SERVERLESS_LOGIC_WEB_TOOLS__corsProxyUrl = 'https://daily-dev-cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud' - KOGITO_TASK_CONSOLE__registry = 'quay.io' - KOGITO_TASK_CONSOLE__account = 'kie-tools' - KOGITO_TASK_CONSOLE__name = 'kogito-task-console' - KOGITO_TASK_CONSOLE__buildTags = 'daily-dev' + KOGITO_TASK_CONSOLE__registry = 'docker.io' + KOGITO_TASK_CONSOLE__account = 'apache' + KOGITO_TASK_CONSOLE__name = 'incubator-kie-kogito-task-console' + KOGITO_TASK_CONSOLE__buildTag = 'daily-dev' - KOGITO_MANAGEMENT_CONSOLE__registry = 'quay.io' - KOGITO_MANAGEMENT_CONSOLE__account = 'kie-tools' - KOGITO_MANAGEMENT_CONSOLE__name = 'kogito-management-console' - KOGITO_MANAGEMENT_CONSOLE__buildTags = 'daily-dev' + KOGITO_MANAGEMENT_CONSOLE__registry = 'docker.io' + KOGITO_MANAGEMENT_CONSOLE__account = 'apache' + KOGITO_MANAGEMENT_CONSOLE__name = 'incubator-kie-kogito-management-console' + KOGITO_MANAGEMENT_CONSOLE__buildTag = 'daily-dev' - KOGITO_SWF_BUILDER_IMAGE__registry = 'quay.io' - KOGITO_SWF_BUILDER_IMAGE__account = 'kiegroup' - KOGITO_SWF_BUILDER_IMAGE__name = 'kogito-swf-builder-nightly' - KOGITO_SWF_BUILDER_IMAGE__buildTag = 'latest' + SONATAFLOW_BUILDER_IMAGE__registry = 'docker.io' + SONATAFLOW_BUILDER_IMAGE__account = 'apache' + SONATAFLOW_BUILDER_IMAGE__name = 'incubator-kie-sonataflow-builder' + SONATAFLOW_BUILDER_IMAGE__buildTag = 'latest' - KOGITO_SWF_DEVMODE_IMAGE__registry = 'quay.io' - KOGITO_SWF_DEVMODE_IMAGE__account = 'kiegroup' - KOGITO_SWF_DEVMODE_IMAGE__name = 'kogito-swf-devmode-nightly' - KOGITO_SWF_DEVMODE_IMAGE__buildTag = 'latest' + SONATAFLOW_DEVMODE_IMAGE__registry = 'docker.io' + SONATAFLOW_DEVMODE_IMAGE__account = 'apache' + SONATAFLOW_DEVMODE_IMAGE__name = 'incubator-kie-sonataflow-devmode' + SONATAFLOW_DEVMODE_IMAGE__buildTag = 'latest' + + SONATAFLOW_OPERATOR__registry = 'docker.io' + SONATAFLOW_OPERATOR__account = 'apache' + SONATAFLOW_OPERATOR__name = 'incubator-kie-sonataflow-operator' + SONATAFLOW_OPERATOR__buildTag = 'latest' BUILD_DATE = sh(script: "echo `date +'%Y-%m-%d %T'`", returnStdout: true).trim() @@ -225,79 +234,106 @@ pipeline { } } - stage('Push kogito-swf-builder to quay.io') { + stage('Push sonataflow-builder to Docker Hub') { + steps { + script { + dockerUtils.pushImageToRegistry( + "${env.SONATAFLOW_BUILDER_IMAGE__registry}", + "${env.SONATAFLOW_BUILDER_IMAGE__account}", + "${env.SONATAFLOW_BUILDER_IMAGE__name}", + "${env.SONATAFLOW_BUILDER_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Push sonataflow-devmode to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_BUILDER_IMAGE__registry}/${env.KOGITO_SWF_BUILDER_IMAGE__account}", - "${env.KOGITO_SWF_BUILDER_IMAGE__name}", - "${env.KOGITO_SWF_BUILDER_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" + "${env.SONATAFLOW_DEVMODE_IMAGE__registry}", + "${env.SONATAFLOW_DEVMODE_IMAGE__account}", + "${env.SONATAFLOW_DEVMODE_IMAGE__name}", + "${env.SONATAFLOW_DEVMODE_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push kogito-swf-devmode to quay.io') { + stage('Push sonataflow-operator to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_DEVMODE_IMAGE__registry}/${env.KOGITO_SWF_DEVMODE_IMAGE__account}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__name}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" + "${env.SONATAFLOW_OPERATOR__registry}", + "${env.SONATAFLOW_OPERATOR__account}", + "${env.SONATAFLOW_OPERATOR__name}", + "${env.SONATAFLOW_OPERATOR__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push dev-deployment-base-image to quay.io') { + stage('Push dev-deployment-base-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}/${env.DEV_DEPLOYMENT_BASE_IMAGE__account}", + "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_BASE_IMAGE__account}", "${env.DEV_DEPLOYMENT_BASE_IMAGE__name}", - "${env.DEV_DEPLOYMENT_BASE_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_BASE_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push dev-deployment-dmn-form-webapp-image to quay.io') { + stage('Push dev-deployment-dmn-form-webapp-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}/${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}", + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}", "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push dev-deployment-kogito-quarkus-blank-app-image to quay.io') { + stage('Push dev-deployment-kogito-quarkus-blank-app-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}/${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}", + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}", "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push kie-sandbox-extended-services-image to quay.io') { + stage('Push kie-sandbox-extended-services-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}", + "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}", + "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}", "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}", "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -321,14 +357,16 @@ pipeline { } } - stage('Push cors-proxy-image to quay.io') { + stage('Push cors-proxy-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}", + "${env.CORS_PROXY_IMAGE__imageRegistry}", + "${env.CORS_PROXY_IMAGE__imageAccount}", "${env.CORS_PROXY_IMAGE__imageName}", "${env.CORS_PROXY_IMAGE__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -352,14 +390,16 @@ pipeline { } } - stage('Push kie-sandbox-image to quay.io') { + stage('Push kie-sandbox-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}", - "${env.KIE_SANDBOX__imageName}", - "${env.KIE_SANDBOX__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageAccount}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageName}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageBuildTags}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -381,7 +421,7 @@ pipeline { "${env.OPENSHIFT_NAMESPACE}", 'daily-dev-kie-sandbox', "${env.DEPLOY_TAG}", - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}/${env.KIE_SANDBOX__imageName}:${env.DEPLOY_TAG}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry}/${env.KIE_SANDBOX_WEBAPP_IMAGE__imageAccount}/${env.KIE_SANDBOX_WEBAPP_IMAGE__imageName}:${env.DEPLOY_TAG}", "${env.OPENSHIFT_PART_OF}", 'js', "${pipelineVars.openshiftCredentialsId}", @@ -391,92 +431,105 @@ pipeline { } } - stage('Push serverless-logic-web-tools-swf-builder-image to quay.io') { + stage('Push serverless-logic-web-tools-swf-builder-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push serverless-logic-web-tools-base-builder-image to quay.io') { + stage('Push serverless-logic-web-tools-base-builder-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push serverless-logic-web-tools-swf-dev-mode-image to quay.io') { + stage('Push serverless-logic-web-tools-swf-dev-mode-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push dashbuilder-viewer-image to quay.io') { + stage('Push dashbuilder-viewer-image to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.DASHBUILDER__viewerImageRegistry}/${env.DASHBUILDER__viewerImageAccount}", + "${env.DASHBUILDER__viewerImageRegistry}", + "${env.DASHBUILDER__viewerImageAccount}", "${env.DASHBUILDER__viewerImageName}", "${env.DASHBUILDER__viewerImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push kogito-task-console to quay.io') { + stage('Push kogito-task-console to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_TASK_CONSOLE__registry}/${env.KOGITO_TASK_CONSOLE__account}", + "${env.KOGITO_TASK_CONSOLE__registry}", + "${env.KOGITO_TASK_CONSOLE__account}", "${env.KOGITO_TASK_CONSOLE__name}", - "${env.KOGITO_TASK_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KOGITO_TASK_CONSOLE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push kogito-management-console to quay.io') { + stage('Push kogito-management-console to Docker Hub') { steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_MANAGEMENT_CONSOLE__registry}/${env.KOGITO_MANAGEMENT_CONSOLE__account}", + "${env.KOGITO_MANAGEMENT_CONSOLE__registry}", + "${env.KOGITO_MANAGEMENT_CONSOLE__account}", "${env.KOGITO_MANAGEMENT_CONSOLE__name}", - "${env.KOGITO_MANAGEMENT_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KOGITO_MANAGEMENT_CONSOLE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } - stage('Push kie-sandbox-helm-chart to quay.io') { + stage('Push kie-sandbox-helm-chart to Docker Hub') { steps { dir('kie-tools') { script { helmUtils.pushChartToRegistry( "${env.KIE_SANDBOX_HELM_CHART__registry}/${env.KIE_SANDBOX_HELM_CHART__account}", "packages/kie-sandbox-helm-chart/dist/${env.KIE_SANDBOX_HELM_CHART__name}-${env.KIE_SANDBOX_HELM_CHART__tag}.tgz", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -512,12 +565,32 @@ pipeline { } } } + + stage('Build and Deploy (jbpm-quarkus-devui and sonataflow-quarkus-devui)') { + steps { + dir('kie-tools') { + script { + withCredentials([usernamePassword(credentialsId: "${pipelineVars.mavenDeployRepositoryCredentialsId}", usernameVariable: 'REPOSITORY_USER', passwordVariable: 'REPOSITORY_TOKEN')]) { + configFileProvider([configFile(fileId: "${pipelineVars.mavenSettingsConfigFileId}", variable: 'MAVEN_SETTINGS_FILE')]) { + sh """#!/bin/bash -el + export KIE_TOOLS_BUILD__mavenDeploySkip=false + pnpm -F jbpm-quarkus-devui... -F sonataflow-quarkus-devui... exec 'bash' '-c' 'echo -e "\n--settings=${MAVEN_SETTINGS_FILE}" >> .mvn/maven.config' + pnpm -F jbpm-quarkus-devui... -F sonataflow-quarkus-devui... exec 'bash' '-c' 'echo -Dapache.repository.username=${REPOSITORY_USER} >> .mvn/maven.config' + pnpm -F jbpm-quarkus-devui... -F sonataflow-quarkus-devui... exec 'bash' '-c' 'echo -Dapache.repository.password=${REPOSITORY_TOKEN} >> .mvn/maven.config' + pnpm -F jbpm-quarkus-devui... -F sonataflow-quarkus-devui... exec 'bash' '-c' 'echo -Drevision=999-SNAPSHOT >> .mvn/maven.config' + pnpm -F jbpm-quarkus-devui... -F sonataflow-quarkus-devui... build:prod + """.trim() + } + } + } + } + } + } } post { always { archiveArtifacts artifacts: "kie-tools/packages/serverless-workflow-vscode-extension/dist/serverless_workflow_vscode_extension_${env.VERSION}.vsix, kie-tools/packages/vscode-extension-dashbuilder-editor/dist/vscode_extension_dashbuilder_editor_${env.VERSION}.vsix, kie-tools/packages/kie-editors-dev-vscode-extension/dist/kie_editors_dev_vscode_extension_${env.VERSION}.vsix, kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${env.VERSION}.zip, kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${env.VERSION}.zip" - cleanWs(deleteDirs: true, disableDeferredWipeout: true) } } diff --git a/.ci/jenkins/Jenkinsfile.release-build b/.ci/jenkins/Jenkinsfile.release-build index 3d694b0c0ed..15cfa071494 100644 --- a/.ci/jenkins/Jenkinsfile.release-build +++ b/.ci/jenkins/Jenkinsfile.release-build @@ -20,7 +20,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' label util.avoidFaultyNodes() } @@ -33,9 +33,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) string(description: 'Base Ref', name: 'BASE_REF') - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Upload asset url', name: 'UPLOAD_ASSET_URL', defaultValue: '') - string(description: 'Runners', name: 'RUNNERS', defaultValue: '{"dev_deployment_base_image":"false","dev_deployment_kogito_quakus_blank_app_image":"false","dev_deployment_dmn_form_webapp_image":"false","dev_deployment_upload_service":"false","kie_sandbox_image":"false","kie_sandbox_extended_services_image":"false","cors_proxy_image":"false","online_editor":"false", "chrome_extensions":"false","vscode_extensions_dev":"false","vscode_extensions_prod":"false","npm_packages":"false","standalone_editors_cdn":"false","extended_services":"false","serverless_logic_web_tools":"false","serverless_logic_web_tools_swf_builder_image":"false","serverless_logic_web_tools_base_builder_image":"false","serverless_logic_web_tools_swf_dev_mode_image":"false","dashbuilder_viewer_image":"false","kn_plugin_workflow":"false","kie_sandbox_helm_chart":"false","kogito_task_console":"false","kogito_management_console":"false","kogito_swf_builder":"false","kogito_swf_devmode":"false"}') + string(description: 'Runners', name: 'RUNNERS', defaultValue: '{"dev_deployment_base_image":"false","dev_deployment_kogito_quakus_blank_app_image":"false","dev_deployment_dmn_form_webapp_image":"false","dev_deployment_upload_service":"false","kie_sandbox_image":"false","kie_sandbox_extended_services_image":"false","cors_proxy_image":"false","online_editor":"false", "chrome_extensions":"false","vscode_extensions_dev":"false","vscode_extensions_prod":"false","npm_packages":"false","standalone_editors_cdn":"false","extended_services":"false","serverless_logic_web_tools":"false","serverless_logic_web_tools_swf_builder_image":"false","serverless_logic_web_tools_base_builder_image":"false","serverless_logic_web_tools_swf_dev_mode_image":"false","dashbuilder_viewer_image":"false","kn_plugin_workflow":"false","kie_sandbox_helm_chart":"false","kogito_task_console":"false","kogito_management_console":"false","kogito_swf_builder":"false","kogito_swf_devmode":"false","kogito_serverless_operator":"false","jbpm_quarkus_devui":"false","sonataflow_quarkus_devui":"false"}') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -56,6 +58,7 @@ pipeline { KOGITO_MANAGEMENT_CONSOLE_JOB_RESULT = 'SKIPPED' KOGITO_SWF_BUILDER_JOB_RESULT = 'SKIPPED' KOGITO_SWF_DEVMODE_JOB_RESULT = 'SKIPPED' + KOGITO_SERVERLESS_OPERATOR_JOB_RESULT = 'SKIPPED' } stages { @@ -97,6 +100,9 @@ pipeline { env.KOGITO_MANAGEMENT_CONSOLE = runners.kogito_management_console env.KOGITO_SWF_BUILDER = runners.kogito_swf_builder env.KOGITO_SWF_DEVMODE = runners.kogito_swf_devmode + env.KOGITO_SERVERLESS_OPERATOR = runners.kogito_serverless_operator + env.JBPM_QUARKUS_DEVUI = runners.jbpm_quarkus_devui + env.SONATAFLOW_QUARKUS_DEVUI = runners.sonataflow_quarkus_devui } } } @@ -125,500 +131,659 @@ pipeline { echo "dashbuilder_viewer_image: ${env.DASHBUILDER_VIEWER_IMAGE}" echo "kn_plugin_workflow: ${env.KN_PLUGIN_WORKFLOW}" echo "kie_sandbox_helm_chart: ${env.KIE_SANDBOX_HELM_CHART}" - echo "kogito_task_console": ${env.KOGITO_TASK_CONSOLE}" - echo "kogito_management_console": ${env.KOGITO_MANAGEMENT_CONSOLE}" - echo "kogito_swf_builder": ${env.KOGITO_SWF_BUILDER}" - echo "kogito_swf_devmode": ${env.KOGITO_SWF_DEVMODE}" + echo "kogito_task_console: ${env.KOGITO_TASK_CONSOLE}" + echo "kogito_management_console: ${env.KOGITO_MANAGEMENT_CONSOLE}" + echo "kogito_swf_builder: ${env.KOGITO_SWF_BUILDER}" + echo "kogito_swf_devmode: ${env.KOGITO_SWF_DEVMODE}" + echo "kogito_serverless_operator: ${env.KOGITO_SERVERLESS_OPERATOR}" + echo "jbpm_quarkus_devui: ${env.JBPM_QUARKUS_DEVUI}" + echo "sonataflow_quarkus_devui: ${env.SONATAFLOW_QUARKUS_DEVUI}" """.trim() } } - stage('Dev Deployment Base Image') { - when { - expression { env.DEV_DEPLOYMENT_BASE_IMAGE == 'true' } - } - steps { - script { - env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-base-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Parallel - Builds') { + parallel { + stage('Dev Deployment Kogito Quarkus Blank App Image') { + when { + expression { env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE == 'true' } + } + steps { + script { + env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-kogito-quarkus-blank-app-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Dev Deployment DMN Form Webapp Image') { - when { - expression { env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE == 'true' } - } - steps { - script { - env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-dmn-form-webapp-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Dev Deployment Upload Service') { + when { + expression { env.DEV_DEPLOYMENT_UPLOAD_SERVICE == 'true' } + } + steps { + script { + env.DEV_DEPLOYMENT_UPLOAD_SERVICE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-upload-service', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Dev Deployment Kogito Quarkus Blank App Image') { - when { - expression { env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE == 'true' } - } - steps { - script { - env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-kogito-quarkus-blank-app-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('VSCode Extensions Dev') { + when { + expression { env.VSCODE_EXTENSIONS_DEV == 'true' } + } + steps { + script { + env.VSCODE_EXTENSIONS_DEV_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/vscode-extensions-dev', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") + ] + ).result + } + } } - } - } - stage('Dev Deployment Upload Service') { - when { - expression { env.DEV_DEPLOYMENT_UPLOAD_SERVICE == 'true' } - } - steps { - script { - env.DEV_DEPLOYMENT_UPLOAD_SERVICE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-upload-service', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result + stage('VSCode Extensions Prod') { + when { + expression { env.VSCODE_EXTENSIONS_PROD == 'true' } + } + steps { + script { + env.CHROME_EXTENSIONS_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/vscode-extensions-prod', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('KIE Sandbox Extended Services Image') { - when { - expression { env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE == 'true' } - } - steps { - script { - env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox-extended-services', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result - - env.KIE_SANDBOX_EXTENDED_SERVICES_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'kie-sandbox-extended-services', "${pipelineVars.openshiftCredentialsId}") + stage('NPM Packages') { + when { + expression { env.NPM_PACKAGES == 'true' } + } + steps { + script { + env.NPM_PACKAGES_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/npm-packages', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('CORS Proxy Image') { - when { - expression { env.CORS_PROXY_IMAGE == 'true' } - } - steps { - script { - env.CORS_PROXY_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/cors-proxy', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result - - env.KIE_SANDBOX_CORS_PROXY_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'cors-proxy', "${pipelineVars.openshiftCredentialsId}") + stage('Kn Plugin Workflow') { + when { + expression { env.KN_PLUGIN_WORKFLOW == 'true' } + } + steps { + script { + env.KN_PLUGIN_WORKFLOW_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kn-plugin-workflow', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('KIE Sandbox Image') { - when { - expression { env.KIE_SANDBOX_IMAGE == 'true' } - } - steps { - script { - env.KIE_SANDBOX_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'KIE_SANDBOX_EXTENDED_SERVICES_URL', value: "${env.KIE_SANDBOX_EXTENDED_SERVICES_URL}"), - string(name: 'KIE_SANDBOX_CORS_PROXY_URL', value: "${env.KIE_SANDBOX_CORS_PROXY_URL}") - ] - ).result - - env.KIE_SANDBOX_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'kie-sandbox', "${pipelineVars.openshiftCredentialsId}") + stage('Serverless Logic Web Tools Swf Builder Image') { + when { + expression { env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE == 'true' } + } + steps { + script { + env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-swf-builder-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - // TODO: Verify Windows + MacOS builds - stage('Extendend Services') { - when { - expression { env.EXTENDED_SERVICES == 'true' } - } - steps { - script { - env.EXTENDED_SERVICES_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/extended-services', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result + stage('Serverless Logic Web Tools Swf Dev Mode Image') { + when { + expression { env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE == 'true' } + } + steps { + script { + env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-swf-dev-mode-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Online Editor') { - when { - expression { env.ONLINE_EDITOR == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') } - } - steps { - script { - env.ONLINE_EDITOR_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/online-editor', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Serverless Logic Web Tools Base Builder Image') { + when { + expression { env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE == 'true' } + } + steps { + script { + env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-base-builder-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Chrome Extensions') { - when { - expression { env.CHROME_EXTENSIONS == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') && (env.ONLINE_EDITOR_JOB_RESULT == 'SUCCESS' || env.ONLINE_EDITOR_JOB_RESULT == 'SKIPPED') } - } - steps { - script { - env.CHROME_EXTENSIONS_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/chrome-extensions', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result + stage('Dashbuilder Viewer Image') { + when { + expression { env.DASHBUILDER_VIEWER_IMAGE == 'true' } + } + steps { + script { + env.DASHBUILDER_VIEWER_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/dashbuilder-viewer-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('VSCode Extensions Dev') { - when { - expression { env.VSCODE_EXTENSIONS_DEV == 'true' } - } - steps { - script { - env.VSCODE_EXTENSIONS_DEV_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/vscode-extensions-dev', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result + stage('Serverless Logic Web Tools') { + when { + expression { env.SERVERLESS_LOGIC_WEB_TOOLS == 'true' } + } + steps { + script { + env.SERVERLESS_LOGIC_WEB_TOOLS_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}") + ] + ).result + } + } } - } - } - stage('VSCode Extensions Prod') { - when { - expression { env.VSCODE_EXTENSIONS_PROD == 'true' } - } - steps { - script { - env.CHROME_EXTENSIONS_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/vscode-extensions-prod', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result + stage('Kogito Task Console Image') { + when { + expression { env.KOGITO_TASK_CONSOLE == 'true' } + } + steps { + script { + env.KOGITO_TASK_CONSOLE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-task-console', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('NPM Packages') { - when { - expression { env.NPM_PACKAGES == 'true' } - } - steps { - script { - env.NPM_PACKAGES_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/npm-packages', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Kogito Management Console Image') { + when { + expression { env.KOGITO_MANAGEMENT_CONSOLE == 'true' } + } + steps { + script { + env.KOGITO_MANAGEMENT_CONSOLE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-management-console', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Standalone Editors CDN') { - when { - expression { env.STANDALONE_EDITORS_CDN == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') && (env.ONLINE_EDITOR_JOB_RESULT == 'SUCCESS' || env.ONLINE_EDITOR_JOB_RESULT == 'SKIPPED') && (env.CHROME_EXTENSIONS_JOB_RESULT == 'SUCCESS' || env.CHROME_EXTENSIONS_JOB_RESULT == 'SKIPPED') } - } - steps { - script { - env.STANDALONE_EDITORS_CDN_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/standalone-editors-cdn', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result - } - } - } - - stage('Kn Plugin Workflow') { - when { - expression { env.KN_PLUGIN_WORKFLOW == 'true' } - } - steps { - script { - env.KN_PLUGIN_WORKFLOW_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kn-plugin-workflow', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}"), - string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}") - ] - ).result - } - } - } - - stage('Serverless Logic Web Tools Swf Builder Image') { - when { - expression { env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE == 'true' } - } - steps { - script { - env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-swf-builder-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result - } - } - } - - stage('Serverless Logic Web Tools Swf Dev Mode Image') { - when { - expression { env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE == 'true' } - } - steps { - script { - env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-swf-dev-mode-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Kogito SWF Builder Image') { + when { + expression { env.KOGITO_SWF_BUILDER == 'true' } + } + steps { + script { + env.KOGITO_SWF_BUILDER_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/sonataflow-builder', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Serverless Logic Web Tools Base Builder Image') { - when { - expression { env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE == 'true' } - } - steps { - script { - env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools-base-builder-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Kogito SWF Devmode Image') { + when { + expression { env.KOGITO_SWF_DEVMODE == 'true' } + } + steps { + script { + env.KOGITO_SWF_DEVMODE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/sonataflow-devmode', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Dashbuilder Viewer Image') { - when { - expression { env.DASHBUILDER_VIEWER_IMAGE == 'true' } - } - steps { - script { - env.DASHBUILDER_VIEWER_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/dashbuilder-viewer-image', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Kogito Serverless Operator Image') { + when { + expression { env.KOGITO_SERVERLESS_OPERATOR == 'true' } + } + steps { + script { + env.KOGITO_SERVERLESS_OPERATOR_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/sonataflow-operator', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } } - } - } - stage('Serverless Logic Web Tools') { - when { - expression { env.SERVERLESS_LOGIC_WEB_TOOLS == 'true' && (env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE_JOB_RESULT == 'SUCCESS' || env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_BUILDER_IMAGE_JOB_RESULT == 'SKIPPED') && (env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE_JOB_RESULT == 'SUCCESS' || env.SERVERLESS_LOGIC_WEB_TOOLS_BASE_BUILDER_IMAGE_JOB_RESULT == 'SKIPPED') && (env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE_JOB_RESULT == 'SUCCESS' || env.SERVERLESS_LOGIC_WEB_TOOLS_SWF_DEV_MODE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DASHBUILDER_VIEWER_IMAGE_JOB_RESULT == 'SUCCESS' || env.DASHBUILDER_VIEWER_IMAGE_JOB_RESULT == 'SKIPPED') } - } - steps { - script { - env.DASHBUILDER_VIEWER_IMAGE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/serverless-logic-web-tools', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('JBPM Quarkus Dev UI') { + when { + expression { env.JBPM_QUARKUS_DEVUI == 'true' } + } + steps { + script { + env.JBPM_QUARKUS_DEVUI_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/jbpm-quarkus-devui', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}") + ] + ).result + } + } } - } - } - stage('KIE Sandbox Helm Chart') { - when { - expression { env.KIE_SANDBOX_HELM_CHART == 'true' } - } - steps { - script { - env.KIE_SANDBOX_HELM_CHART_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox-helm-chart', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Sonataflow Quarkus Dev UI') { + when { + expression { env.SONATAFLOW_QUARKUS_DEVUI == 'true' } + } + steps { + script { + env.SONATAFLOW_QUARKUS_DEVUI_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/sonataflow-quarkus-devui', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}") + ] + ).result + } + } } - } - } - stage('Kogito Task Console Image') { - when { - expression { env.KOGITO_TASK_CONSOLE == 'true' } - } - steps { - script { - env.KOGITO_TASK_CONSOLE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-task-console', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('JBPM Quarkus Dev UI') { + when { + expression { env.JBPM_QUARKUS_DEVUI == 'true' } + } + steps { + script { + env.JBPM_QUARKUS_DEVUI_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/jbpm-quarkus-devui', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}") + ] + ).result + } + } } - } - } - stage('Kogito Management Console Image') { - when { - expression { env.KOGITO_MANAGEMENT_CONSOLE == 'true' } - } - steps { - script { - env.KOGITO_MANAGEMENT_CONSOLE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-management-console', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Sonataflow Quarkus Dev UI') { + when { + expression { env.SONATAFLOW_QUARKUS_DEVUI == 'true' } + } + steps { + script { + env.SONATAFLOW_QUARKUS_DEVUI_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/sonataflow-quarkus-devui', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}") + ] + ).result + } + } } - } - } - stage('Kogito SWF Builder Image') { - when { - expression { env.KOGITO_SWF_BUILDER == 'true' } - } - steps { - script { - env.KOGITO_SWF_BUILDER_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-swf-builder', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Dependant build #1') { + stages { + stage('Dev Deployment Base Image') { + when { + expression { env.DEV_DEPLOYMENT_BASE_IMAGE == 'true' } + } + steps { + script { + env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-base-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + + stage('Dev Deployment DMN Form Webapp Image') { + when { + expression { env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE == 'true' } + } + steps { + script { + env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/dev-deployment-dmn-form-webapp-image', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + + stage('Extendend Services') { + when { + expression { env.EXTENDED_SERVICES == 'true' } + } + steps { + script { + env.EXTENDED_SERVICES_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/extended-services', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + + stage('Online Editor') { + when { + expression { env.ONLINE_EDITOR == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') } + } + steps { + script { + env.ONLINE_EDITOR_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/online-editor', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + + stage('Chrome Extensions') { + when { + expression { env.CHROME_EXTENSIONS == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') && (env.ONLINE_EDITOR_JOB_RESULT == 'SUCCESS' || env.ONLINE_EDITOR_JOB_RESULT == 'SKIPPED') } + } + steps { + script { + env.CHROME_EXTENSIONS_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/chrome-extensions', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${params.UPLOAD_ASSET_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + + stage('Standalone Editors CDN') { + when { + expression { env.STANDALONE_EDITORS_CDN == 'true' && (env.EXTENDED_SERVICES_JOB_RESULT == 'SUCCESS' || env.EXTENDED_SERVICES_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_BASE_IMAGE_JOB_RESULT == 'SKIPPED') && (env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SUCCESS' || env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE_JOB_RESULT == 'SKIPPED') && (env.ONLINE_EDITOR_JOB_RESULT == 'SUCCESS' || env.ONLINE_EDITOR_JOB_RESULT == 'SKIPPED') && (env.CHROME_EXTENSIONS_JOB_RESULT == 'SUCCESS' || env.CHROME_EXTENSIONS_JOB_RESULT == 'SKIPPED') } + } + steps { + script { + env.STANDALONE_EDITORS_CDN_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/standalone-editors-cdn', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + } } - } - } - stage('Kogito SWF Devmode Image') { - when { - expression { env.KOGITO_SWF_DEVMODE == 'true' } - } - steps { - script { - env.KOGITO_SWF_DEVMODE_JOB_RESULT = build( - wait: true, - job: 'KIE/kie-tools/kie-tools-release-jobs/kogito-swf-devmode', - parameters: [ - booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), - string(name: 'BASE_REF', value: "${params.BASE_REF}"), - string(name: 'TAG', value: "${params.TAG}") - ] - ).result + stage('Dependant build #2') { + stages { + stage('KIE Sandbox Extended Services Image') { + when { + expression { env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE == 'true' } + } + steps { + script { + env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox-extended-services', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + + env.KIE_SANDBOX_EXTENDED_SERVICES_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'kie-sandbox-extended-services', "${pipelineVars.openshiftCredentialsId}") + } + } + } + + stage('CORS Proxy Image') { + when { + expression { env.CORS_PROXY_IMAGE == 'true' } + } + steps { + script { + env.CORS_PROXY_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/cors-proxy', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + + env.KIE_SANDBOX_CORS_PROXY_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'cors-proxy', "${pipelineVars.openshiftCredentialsId}") + } + } + } + + stage('KIE Sandbox Image') { + when { + expression { env.KIE_SANDBOX_IMAGE == 'true' && (env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE_JOB_RESULT == 'SUCCESS' || env.KIE_SANDBOX_EXTENDED_SERVICES_IMAGE_JOB_RESULT == 'SKIPPED') && (env.CORS_PROXY_IMAGE_JOB_RESULT == 'SUCCESS' || env.CORS_PROXY_IMAGE_JOB_RESULT == 'SKIPPED') } + } + steps { + script { + env.KIE_SANDBOX_IMAGE_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'KIE_SANDBOX_EXTENDED_SERVICES_URL', value: "${env.KIE_SANDBOX_EXTENDED_SERVICES_URL}"), + string(name: 'KIE_SANDBOX_CORS_PROXY_URL', value: "${env.KIE_SANDBOX_CORS_PROXY_URL}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + + env.KIE_SANDBOX_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", 'kie-sandbox', "${pipelineVars.openshiftCredentialsId}") + } + } + } + + stage('KIE Sandbox Helm Chart') { + when { + expression { env.KIE_SANDBOX_HELM_CHART == 'true' } + } + steps { + script { + env.KIE_SANDBOX_HELM_CHART_JOB_RESULT = build( + wait: true, + job: 'KIE/kie-tools/kie-tools-release-jobs/kie-sandbox-helm-chart', + parameters: [ + booleanParam(name: 'DRY_RUN', value: "${params.DRY_RUN}"), + string(name: 'BASE_REF', value: "${params.BASE_REF}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: "${params.RELEASE_CANDIDATE}"), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${params.RELEASE_CANDIDATE_VERSION}") + ] + ).result + } + } + } + } } } } diff --git a/.ci/jenkins/Jenkinsfile.release-candidate b/.ci/jenkins/Jenkinsfile.release-candidate new file mode 100644 index 00000000000..8476f256969 --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.release-candidate @@ -0,0 +1,192 @@ +/* + * 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. + */ + +@Library('jenkins-pipeline-shared-libraries')_ + +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + label util.avoidFaultyNodes() + } + } + + options { + timeout(time: 180, unit: 'MINUTES') + } + + parameters { + string(name: 'BRANCH_NAME', description: 'Set the Git branch to checkout (0.0.X)', trim: true) + string(name: 'RELEASE_VERSION', description: 'Release version', trim: true) + string(name: 'TAG_NAME', description: 'Tag name to be created', trim: true) + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BRANCH_NAME}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Setup Git repository') { + steps { + dir('kie-tools') { + script { + sh """#!/bin/bash -el + git config user.email asf-ci-kie@jenkins.kie.apache.org + git config user.name asf-ci-kie + git checkout ${params.BRANCH_NAME} + """.trim() + } + } + } + } + + stage('Update project version') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateProjectVersion(params.RELEASE_VERSION) + } + } + } + } + + stage('Update kogito version') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateKogitoVersion(params.RELEASE_VERSION, params.RELEASE_VERSION) + } + } + } + } + + stage('Update stream name') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateStreamName(params.RELEASE_VERSION) + } + } + } + } + + stage('Commit and Push changes') { + steps { + dir('kie-tools') { + script { + sh """#!/bin/bash -el + git add . + git commit --allow-empty -am "Apache KIE ${params.RELEASE_VERSION} release" + """.trim() + githubUtils.pushObject('origin', "${params.NEW_BRANCH_NAME}", "${pipelineVars.asfGithubPushCredentialsId}") + } + } + } + } + + stage('Check `release version` against `package.json.version`') { + steps { + dir('kie-tools') { + script { + packageVersion = sh(returnStdout: true, script: "#!/bin/bash -el \n node -p \"require('./package.json').version\"").trim() + sh """#!/bin/bash -el + [[ "${params.RELEASE_VERSION}" == "${packageVersion}" ]] + """.trim() + } + } + } + } + + stage('Create a new tag') { + steps { + dir('kie-tools') { + script { + githubUtils.createTag("${params.TAG_NAME}") + githubUtils.pushObject('origin', "${params.TAG_NAME}", "${pipelineVars.asfGithubPushCredentialsId}") + } + } + } + } + + stage('Build and Publish release candidate artifacts') { + steps { + build job: 'KIE/kie-tools/kie-tools-release-build', parameters: [ + booleanParam(name: 'DRY_RUN', value: false), + string(name: 'BASE_REF', value: "${env.BRANCH_NAME}"), + string(name: 'RELEASE_VERSION', value: "${params.RELEASE_VERSION}"), + string(name: 'UPLOAD_ASSET_URL', value: "${env.RELEASE_UPLOAD_ASSET_URL}"), + string(name: 'RUNNERS', value: "${params.RUNNERS}"), + booleanParam(name: 'RELEASE_CANDIDATE', value: true), + string(name: 'RELEASE_CANDIDATE_VERSION', value: "${TAG_NAME}") + ] + } + } + } + + post { + always { + cleanWs(deleteDirs: true) + } + } +} diff --git a/.ci/jenkins/Jenkinsfile.release-dry-run b/.ci/jenkins/Jenkinsfile.release-dry-run index bad2dc0e926..81252dbc6e3 100644 --- a/.ci/jenkins/Jenkinsfile.release-dry-run +++ b/.ci/jenkins/Jenkinsfile.release-dry-run @@ -20,7 +20,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' label util.avoidFaultyNodes() } } @@ -35,7 +35,7 @@ pipeline { build job: 'KIE/kie-tools/kie-tools-release-build', parameters: [ booleanParam(name: 'DRY_RUN', value: true), string(name: 'BASE_REF', value: 'main'), - string(name: 'RUNNERS', value: '{"dev_deployment_base_image":"true","dev_deployment_kogito_quakus_blank_app_image":"true","dev_deployment_dmn_form_webapp_image":"true","dev_deployment_upload_service":"true","kie_sandbox_image":"true","kie_sandbox_extended_services_image":"true","cors_proxy_image":"true","online_editor":"true","chrome_extensions":"true","vscode_extensions_dev":"true","vscode_extensions_prod":"true","npm_packages":"true","standalone_editors_cdn":"true","extended_services":"true","serverless_logic_web_tools":"true","serverless_logic_web_tools_swf_builder_image":"true","serverless_logic_web_tools_base_builder_image":"true","serverless_logic_web_tools_swf_dev_mode_image":"true","dashbuilder_viewer_image":"true","kn_plugin_workflow":"true","kie_sandbox_helm_chart":"true","kogito_task_console":"true","kogito_management_console":"true","kogito_swf_builder":"true","kogito_swf_devmode":"true"}') + string(name: 'RUNNERS', value: '{"dev_deployment_base_image":"true","dev_deployment_kogito_quakus_blank_app_image":"true","dev_deployment_dmn_form_webapp_image":"true","dev_deployment_upload_service":"true","kie_sandbox_image":"true","kie_sandbox_extended_services_image":"true","cors_proxy_image":"true","online_editor":"true","chrome_extensions":"true","vscode_extensions_dev":"true","vscode_extensions_prod":"true","npm_packages":"true","standalone_editors_cdn":"true","extended_services":"true","serverless_logic_web_tools":"true","serverless_logic_web_tools_swf_builder_image":"true","serverless_logic_web_tools_base_builder_image":"true","serverless_logic_web_tools_swf_dev_mode_image":"true","dashbuilder_viewer_image":"true","kn_plugin_workflow":"true","kie_sandbox_helm_chart":"true","kogito_task_console":"true","kogito_management_console":"true","kogito_swf_builder":"true","kogito_swf_devmode":"true","kogito_serverless_operator":"true","jbpm_quarkus_devui":"false","sonataflow_quarkus_devui":"false"}') ] } } diff --git a/.ci/jenkins/Jenkinsfile.release-publish b/.ci/jenkins/Jenkinsfile.release-publish index d3d3c75e5fd..8e920d7cb20 100644 --- a/.ci/jenkins/Jenkinsfile.release-publish +++ b/.ci/jenkins/Jenkinsfile.release-publish @@ -20,7 +20,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' label util.avoidFaultyNodes() } } @@ -30,8 +30,8 @@ pipeline { } parameters { - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') - string(description: 'Runners', name: 'RUNNERS', defaultValue: '{"dev_deployment_base_image":"true","dev_deployment_kogito_quakus_blank_app_image":"true","dev_deployment_dmn_form_webapp_image":"true","dev_deployment_upload_service":"true","kie_sandbox_image":"true","kie_sandbox_extended_services_image":"true","cors_proxy_image":"true","online_editor":"true","chrome_extensions":"true","vscode_extensions_dev":"true","vscode_extensions_prod":"true","npm_packages":"true","standalone_editors_cdn":"true","extended_services":"true","serverless_logic_web_tools":"true","serverless_logic_web_tools_swf_builder_image":"true","serverless_logic_web_tools_base_builder_image":"true","serverless_logic_web_tools_swf_dev_mode_image":"true","dashbuilder_viewer_image":"true","kn_plugin_workflow":"true","kie_sandbox_helm_chart":"true","kogito_task_console":"true","kogito_management_console":"true","kogito_swf_builder":"true","kogito_swf_devmode":"true"}') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') + string(description: 'Runners', name: 'RUNNERS', defaultValue: '{"dev_deployment_base_image":"true","dev_deployment_kogito_quakus_blank_app_image":"true","dev_deployment_dmn_form_webapp_image":"true","dev_deployment_upload_service":"true","kie_sandbox_image":"true","kie_sandbox_extended_services_image":"true","cors_proxy_image":"true","online_editor":"true","chrome_extensions":"true","vscode_extensions_dev":"true","vscode_extensions_prod":"true","npm_packages":"true","standalone_editors_cdn":"true","extended_services":"true","serverless_logic_web_tools":"true","serverless_logic_web_tools_swf_builder_image":"true","serverless_logic_web_tools_base_builder_image":"true","serverless_logic_web_tools_swf_dev_mode_image":"true","dashbuilder_viewer_image":"true","kn_plugin_workflow":"true","kie_sandbox_helm_chart":"true","kogito_task_console":"true","kogito_management_console":"true","kogito_swf_builder":"true","kogito_serverless_operator":"true","jbpm_quarkus_devui":"true","sonataflow_quarkus_devui":"true"}') } stages { diff --git a/.ci/jenkins/Jenkinsfile.setup-branch b/.ci/jenkins/Jenkinsfile.setup-branch new file mode 100644 index 00000000000..4a6463ba41b --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.setup-branch @@ -0,0 +1,190 @@ +/* + * 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. + */ + +@Library('jenkins-pipeline-shared-libraries')_ + +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + label util.avoidFaultyNodes() + } + } + + options { + timeout(time: 60, unit: 'MINUTES') + } + + parameters { + string(name: 'CHECKOUT_BRANCH_NAME', defaultValue: 'main', description: 'Set the Git branch to checkout', trim: true) + string(name: 'NEW_BRANCH_NAME', description: 'Set the Git branch to be created', trim: true) + string(name: 'KIE_TOOLS_VERSION', description: 'KIE Tools version to set', trim: true) + string(name: 'KOGITO_MAVEN_VERSION', description: 'Kogito maven version to set', trim: true) + string(name: 'KOGITO_IMAGES_TAG', description: 'Kogito images tag to set', trim: true) + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + } + } + } + + stage('Initialize') { + steps { + script { + currentBuild.displayName = params.KIE_TOOLS_VERSION + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.CHECKOUT_BRANCH_NAME}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('CI Image Build') { + steps { + script { + build( + job: "KIE/kie-tools/kie-tools-ci-jobs/kie-tools-ci-image-build/${params.CHECKOUT_BRANCH_NAME}", + wait: false, + parameters: [ + string(name: 'IMAGE_TAG', value: "${params.KIE_TOOLS_VERSION}") + ] + ) + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap() + } + } + } + } + + stage('Setup new branch') { + steps { + dir('kie-tools') { + script { + sh """#!/bin/bash -el + git config user.email asf-ci-kie@jenkins.kie.apache.org + git config user.name asf-ci-kie + git checkout -b ${params.NEW_BRANCH_NAME} + """.trim() + } + } + } + } + + stage('Update project version') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateProjectVersion(params.KIE_TOOLS_VERSION) + } + } + } + } + + stage('Update kogito version') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateKogitoVersion(params.KOGITO_MAVEN_VERSION, params.KOGITO_IMAGES_TAG) + } + } + } + } + + stage('Update stream name') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmUpdateStreamName(params.NEW_BRANCH_NAME) + } + } + } + } + + stage('Replace CI image tag') { + steps { + dir('kie-tools/.ci/jenkins') { + script { + sh """#!/bin/bash -el + find . -type f -name 'Jenkinsfile.*' -exec sed -E -i "s%(incubator-kie-tools-ci-build:).*%\1${params.KIE_TOOLS_VERSION}% {} \;" + """.trim() + } + } + } + } + + stage('Commit and Push changes') { + steps { + dir('kie-tools') { + script { + sh """#!/bin/bash -el + git add . + git commit --allow-empty -am "Update development version to ${params.KIE_TOOLS_VERSION}" + """.trim() + githubUtils.pushObject('origin', "${params.NEW_BRANCH_NAME}", "${pipelineVars.asfGithubPushCredentialsId}") + } + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } +} diff --git a/.ci/jenkins/Jenkinsfile.setup-branch.quarkus-accelerator b/.ci/jenkins/Jenkinsfile.setup-branch.quarkus-accelerator new file mode 100644 index 00000000000..813e1582c39 --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.setup-branch.quarkus-accelerator @@ -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. + */ + +@Library('jenkins-pipeline-shared-libraries')_ + +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + label util.avoidFaultyNodes() + } + } + + options { + timeout(time: 60, unit: 'MINUTES') + } + + parameters { + string(name: 'CHECKOUT_BRANCH_NAME', defaultValue: '0.0.0', description: 'Set the Git branch to checkout', trim: true) + string(name: 'NEW_BRANCH_NAME', description: 'Set the Git branch to be created. (x.x.999)', trim: true) + string(name: 'KOGITO_VERSION', description: 'Kogito version to set', trim: true) + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + } + } + } + + stage('Clean workspace') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-sandbox-quarkus-accelerator') { + steps { + dir('kie-sandbox-quarkus-accelerator') { + script { + githubUtils.checkoutRepo( + "http://github.com/apache/incubator-kie-sandbox-quarkus-accelerator.git", + "${params.CHECKOUT_BRANCH_NAME}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('Setup new branch') { + steps { + dir('kie-sandbox-quarkus-accelerator') { + script { + sh """#!/bin/bash -el + git config user.email asf-ci-kie@jenkins.kie.apache.org + git config user.name asf-ci-kie + git checkout -b ${params.NEW_BRANCH_NAME} + """.trim() + } + } + } + } + + stage('Update kogito BOM version') { + steps { + dir('kie-sandbox-quarkus-accelerator') { + script { + sh """#!/bin/bash -el + mvn versions:set-property -Dproperty=kogito.bom.version -DnewVersion=${params.KOGITO_VERSION} + """.trim() + } + } + } + } + + stage('Commit and Push changes') { + steps { + dir('kie-sandbox-quarkus-accelerator') { + script { + sh """#!/bin/bash -el + git add . + git commit --allow-empty -am "Update kogito version to ${params.KOGITO_VERSION}" + """.trim() + githubUtils.pushObject('origin', "${params.NEW_BRANCH_NAME}", "${pipelineVars.asfCIGithubCredentialsId}") + } + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } +} diff --git a/.ci/jenkins/Jenkinsfile.staging-build b/.ci/jenkins/Jenkinsfile.staging-build deleted file mode 100644 index c1254b6d484..00000000000 --- a/.ci/jenkins/Jenkinsfile.staging-build +++ /dev/null @@ -1,1173 +0,0 @@ -/* - * 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. - */ - -@Library('jenkins-pipeline-shared-libraries')_ - -pipeline { - agent { - docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' - args '--shm-size=2g --privileged --group-add docker' - label util.avoidFaultyNodes() - } - } - - options { - timeout(time: 600, unit: 'MINUTES') - } - - parameters { - booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Base Ref', name: 'BASE_REF') - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') - string(description: 'Commit SHA', name: 'COMMIT_SHA', defaultValue: 'main') - string(description: 'Download asset url', name: 'DOWNLOAD_ASSET_URL', defaultValue: '') - string(description: 'Upload asset url', name: 'UPLOAD_ASSET_URL', defaultValue: '') - } - - environment { - OPENSHIFT_NAMESPACE = 'kie-sandbox' - OPENSHIFT_PART_OF = 'staging-kie-sandbox-app' - - DEV_DEPLOYMENT_BASE_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_BASE_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_BASE_IMAGE__name = 'dev-deployment-base-image' - DEV_DEPLOYMENT_BASE_IMAGE__buildTags = "${params.TAG}-prerelease" - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'dev-deployment-dmn-form-webapp-image' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags = "${params.TAG}-prerelease" - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name = 'dev-deployment-kogito-quarkus-blank-app-image' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags = "${params.TAG}-prerelease" - ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentBaseImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentBaseImageName = 'dev-deployment-base-image' - ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.TAG}-prerelease" - ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'dev-deployment-dmn-form-webapp-image' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.TAG}-prerelease" - - KIE_SANDBOX__imageRegistry = 'quay.io' - KIE_SANDBOX__imageAccount = 'kie-tools' - KIE_SANDBOX__imageName = 'kie-sandbox-image' - KIE_SANDBOX__imageBuildTags = "${params.TAG}-prerelease" - - KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry = 'quay.io' - KIE_SANDBOX_EXTENDED_SERVICES__imageAccount = 'kie-tools' - KIE_SANDBOX_EXTENDED_SERVICES__imageName = 'kie-sandbox-extended-services-image' - KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags = "${params.TAG}-prerelease" - - CORS_PROXY_IMAGE__imageRegistry = 'quay.io' - CORS_PROXY_IMAGE__imageAccount = 'kie-tools' - CORS_PROXY_IMAGE__imageName = 'cors-proxy-image' - CORS_PROXY_IMAGE__imageBuildTags = "${params.TAG}-prerelease" - - DEPLOY_TAG = "${params.TAG}-prerelease" - - DASHBUILDER__viewerImageRegistry = 'quay.io' - DASHBUILDER__viewerImageAccount = 'kie-tools' - DASHBUILDER__viewerImageName = 'dashbuilder-viewer-image' - DASHBUILDER__viewerImageBuildTags = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__dashbuilderViewerImageTag = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'serverless-logic-web-tools-swf-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageTag = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'serverless-logic-web-tools-base-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageTag = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'serverless-logic-web-tools-swf-dev-mode-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageTag = "${params.TAG}-prerelease" - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags = "${params.TAG}-prerelease" - - KIE_SANDBOX_HELM_CHART__registry = 'quay.io' - KIE_SANDBOX_HELM_CHART__account = 'kie-tools' - KIE_SANDBOX_HELM_CHART__name = 'kie-sandbox-helm-chart' - KIE_SANDBOX_HELM_CHART__tag = "${params.TAG}-prerelease" - - KOGITO_TASK_CONSOLE__registry = 'quay.io' - KOGITO_TASK_CONSOLE__account = 'kie-tools' - KOGITO_TASK_CONSOLE__name = 'kogito-task-console' - KOGITO_TASK_CONSOLE__buildTags = "${params.TAG}-prerelease" - - KOGITO_MANAGEMENT_CONSOLE__registry = 'quay.io' - KOGITO_MANAGEMENT_CONSOLE__account = 'kie-tools' - KOGITO_MANAGEMENT_CONSOLE__name = 'kogito-management-console' - KOGITO_MANAGEMENT_CONSOLE__buildTags = "${params.TAG}-prerelease" - - KOGITO_SWF_BUILDER_IMAGE__registry = 'quay.io' - KOGITO_SWF_BUILDER_IMAGE__account = 'kiegroup' - KOGITO_SWF_BUILDER_IMAGE__name = 'kogito-swf-builder' - KOGITO_SWF_BUILDER_IMAGE__buildTag = "${params.TAG}-prerelease" - - KOGITO_SWF_DEVMODE_IMAGE__registry = 'quay.io' - KOGITO_SWF_DEVMODE_IMAGE__account = 'kiegroup' - KOGITO_SWF_DEVMODE_IMAGE__name = 'kogito-swf-devmode' - KOGITO_SWF_DEVMODE_IMAGE__buildTag = "${params.TAG}-prerelease" - - DOCKER_CONFIG = "${WORKSPACE}/.docker" - } - - stages { - stage('Load local shared scripts') { - steps { - script { - loadLocalSharedScripts() - } - } - } - - stage('Start required services for build and tests (DinD, Xvfb, Fluxbox)') { - steps { - script { - buildUtils.startRequiredServices() - } - } - } - - stage('Clean workspace before build') { - steps { - cleanWs(deleteDirs: true, disableDeferredWipeout: true) - } - } - - stage('Checkout kie-tools') { - steps { - dir('kie-tools') { - script { - githubUtils.checkoutRepo( - "http://github.com/${pipelineVars.githubRepositorySlug}.git", - "${params.BASE_REF}", - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) - } - } - } - } - - stage('Checkout kogito-online-staging') { - when { - expression { !params.DRY_RUN } - } - steps { - dir('kogito-online-staging') { - script { - githubUtils.checkoutRepo( - 'https://github.com/apache/incubator-kie-kogito-online-staging.git', - 'main', - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) - } - } - } - } - - stage('Setup PNPM') { - steps { - dir('kie-tools') { - script { - buildUtils.setupPnpm() - } - } - } - } - - stage('PNPM Bootstrap') { - steps { - dir('kie-tools') { - script { - buildUtils.pnpmBootstrap() - } - } - } - } - - stage('Build (without some images)') { - steps { - dir('kie-tools') { - script { - buildPartial() - } - } - } - } - - stage('STAGING: Push serverless-logic-web-tools-swf-builder-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushServerlessLogicWebToolsSWFBuilderImageToQuay() - } - } - } - - stage('STAGING: Deploy Online Editor to kogito-online-staging') { - when { - expression { !params.DRY_RUN } - } - steps { - dir('kogito-online-staging') { - script { - deployOnlineEditor("${params.TAG}", "${pipelineVars.asfGithubPushCredentialsId}") - } - } - } - } - - stage('STAGING: Compress Online Editor (Ubuntu only)') { - steps { - dir('kie-tools/packages/online-editor') { - script { - zipUtils.zipArtifact('dist.zip', 'dist/*') - } - } - } - } - - stage('STAGING: Compress Serverless Logic Web Tools (Ubuntu only)') { - steps { - dir('kie-tools/packages/serverless-logic-web-tools') { - script { - zipUtils.zipArtifact('dist.zip', 'dist/*') - } - } - } - } - - stage('STAGING: Upload Online Editor') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadOnlineEditor() - } - } - } - - stage('STAGING: Upload Serverless Logic Web Tools') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadServerlessLogicWebTools() - } - } - } - - stage('STAGING: Upload VS Code Extension (dev)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtension() - } - } - } - - stage('STAGING: Upload VS Code Extension - BPMN Editor (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionBPMNEditor() - } - } - } - - stage('STAGING: Upload VS Code Extension - DMN Editor (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionDMNEditor() - } - } - } - - stage('STAGING: Upload VS Code Extension - PMML Editor (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionPMMLEditor() - } - } - } - - stage('STAGING: Upload VS Code Extension - Serverless Workflow Editor - KIE (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionServerlessWorkflowEditor() - } - } - } - - stage('STAGING: Upload VS Code Extension - Dashbuilder Editor (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionDashbuilderEditor() - } - } - } - - stage('STAGING: Upload VS Code Extension - Kogito Bundle (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionKogitoBundle() - } - } - } - - stage('STAGING: Upload VS Code Extension - KIE Business Automation Bundle (prod)') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadVSCodeExtensionKieBusinessAutomationBundle() - } - } - } - - stage('STAGING: Upload Chrome Extension for KIE Editors') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadChromeExtensionForKieEditors() - } - } - } - - stage('STAGING: Upload Chrome Extension for Serverless Workflow Editor') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadChromeExtensionForServerlessWorkflowEditor() - } - } - } - - stage('STAGING: Upload Extended Services for Linux') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadExtendedServicesForLinux() - } - } - } - - stage('STAGING: Upload Knative CLI Workflow Plugin for Linux') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadKnativeCliWorkflowPluginForLinux() - } - } - } - - stage('STAGING: Upload Knative CLI Workflow Plugin for macOS') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadKnativeCliWorkflowPluginForMacOs() - } - } - } - - stage('STAGING: Upload Knative CLI Workflow Plugin for macOS M1') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadKnativeCliWorkflowPluginForMacOsM1() - } - } - } - - stage('STAGING: Upload Knative CLI Workflow Plugin for Windows') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - uploadKnativeCliWorkflowPluginForWindows() - } - } - } - - stage('STAGING: Push kie-sandbox-extended-services-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKieSandboxExtendedServicesImageToQuay() - } - } - } - - stage('STAGING: Deploy kie-sandbox-extended-services-image to OpenShift') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - deployKieSandboxExtendedServicesImageToOpenshift() - } - } - } - - stage('STAGING: Push cors-proxy-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushCorsProxyImageToQuay() - } - } - } - - stage('STAGING: Deploy cors-proxy-image to OpenShift') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - deployCorsProxyImageToOpenshift() - } - } - } - - stage('STAGING: Push kie-sandbox-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKieSandboxImageToQuay() - } - } - } - - stage('STAGING: Prepare environment variables for OpenShift deployment') { - when { - expression { !params.DRY_RUN } - } - steps { - sh """#!/bin/bash -el - echo "KIE_SANDBOX_EXTENDED_SERVICES_URL=${env.KIE_SANDBOX_EXTENDED_SERVICES_URL}" >> deployment.env - echo "KIE_SANDBOX_CORS_PROXY_URL=${env.KIE_SANDBOX_CORS_PROXY_URL}" >> deployment.env - """.trim() - } - } - - stage('STAGING: Deploy kie-sandbox-image to OpenShift') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - deployKieSandboxImageToOpenshift() - } - } - } - - stage('STAGING: Build (serverless-logic-web-tools-swf-dev-mode-image)') { - steps { - dir('kie-tools') { - script { - buildImage('serverless-logic-web-tools-swf-dev-mode-image') - } - } - } - } - - stage('STAGING: Push serverless-logic-web-tools-swf-dev-mode-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushServerlessLogicWebToolsSWFDevModeImageToQuay() - } - } - } - - stage('Build (dev-deployment-base-image and dev-deployment-kogito-quarkus-blank-app-image)') { - steps { - dir('kie-tools') { - script { - buildImage('dev-deployment-base-image') - buildImage('dev-deployment-kogito-quarkus-blank-app-image') - } - } - } - } - - stage('STAGING: Push dev-deployment-base-image and dev-deployment-kogito-quarkus-blank-app-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushDevDeploymentBaseImageToQuay() - pushDevDeploymentKogitoQuarkusBlankAppImageToQuay() - } - } - } - - stage('STAGING: Build (dev-deployment-dmn-form-webapp-image)') { - steps { - dir('kie-tools') { - script { - buildImage('dev-deployment-dmn-form-webapp-image') - } - } - } - } - - stage('STAGING: Push dev-deployment-dmn-form-webapp-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushDevDeploymentDmnFormWebappImageToQuay() - } - } - } - - stage('STAGING: Build (serverless-logic-web-tools-base-builder-image)') { - steps { - dir('kie-tools') { - script { - buildImage('serverless-logic-web-tools-base-builder-image') - } - } - } - } - - stage('STAGING: Push serverless-logic-web-tools-base-builder-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushServerlessLogicWebToolsBaseBuilderImageToQuay() - } - } - } - - stage('STAGING: Build (dashbuilder-viewer-image)') { - steps { - dir('kie-tools') { - script { - buildImage('dashbuilder-viewer-image') - } - } - } - } - - stage('STAGING: Push dashbuilder-viewer-image to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushDashbuilderViewerImageToQuay() - } - } - } - - stage('STAGING: Push kie-sandbox-helm-chart to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - dir('kie-tools') { - script { - pushKieSandboxHelmChartToQuay() - } - } - } - } - - stage('STAGING: Build (kogito-task-console)') { - steps { - dir('kie-tools') { - script { - buildImage('kogito-task-console') - } - } - } - } - - stage('STAGING: Push kogito-task-console to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKogitoTaskConsoleToQuay() - } - } - } - - stage('STAGING: Build (kogito-management-console)') { - steps { - dir('kie-tools') { - script { - buildImage('kogito-management-console') - } - } - } - } - - stage('STAGING: Push kogito-management-console to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKogitoManagementConsoleToQuay() - } - } - } - - stage('STAGING: Build (kogito-swf-builder)') { - steps { - dir('kie-tools') { - script { - buildImage('kogito-swf-builder') - } - } - } - } - - stage('STAGING: Push kogito-swf-builder to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKogitoSwfBuilderToQuay() - } - } - } - - stage('STAGING: Build (kogito-swf-devmode)') { - steps { - dir('kie-tools') { - script { - buildImage('kogito-swf-devmode') - } - } - } - } - - stage('STAGING: Push kogito-swf-devmode to quay.io') { - when { - expression { !params.DRY_RUN } - } - steps { - script { - pushKogitoSwfDevModeToQuay() - } - } - } - } - - post { - always { - cleanWs(deleteDirs: true) - } - } -} - -def buildPartial() { - sh """#!/bin/bash -el - export KIE_TOOLS_BUILD__runEndToEndTests=false - export KIE_TOOLS_BUILD__runTests=false - export KIE_TOOLS_BUILD__runLinters=false - export KIE_TOOLS_BUILD__buildContainerImages=true - export WEBPACK__minimize=true - export WEBPACK__tsLoaderTranspileOnly=false - export CHROME_EXTENSION__routerTargetOrigin=https://apache.github.io - export CHROME_EXTENSION__routerRelativePath=incubator-kie-kogito-online-staging/${params.TAG}-prerelease/chrome-extension - export CHROME_EXTENSION__onlineEditorUrl=https://apache.github.io/incubator-kie-kogito-online-staging/${params.TAG}-prerelease - export CHROME_EXTENSION__manifestFile=manifest.prod.json - export SWF_CHROME_EXTENSION__routerTargetOrigin=https://apache.github.io - export SWF_CHROME_EXTENSION__routerRelativePath=incubator-kie-kogito-online-staging/${params.TAG}-prerelease/swf-chrome-extension - export SWF_CHROME_EXTENSION__manifestFile=manifest.prod.json - export ONLINE_EDITOR__buildInfo="${params.TAG} (staging) @ ${params.COMMIT_SHA}" - export ONLINE_EDITOR__extendedServicesDownloadUrlLinux=${params.DOWNLOAD_ASSET_URL}/STAGING__kie_sandbox_extended_services_linux_${params.TAG}.tar.gz - export ONLINE_EDITOR__extendedServicesDownloadUrlMacOs=${params.DOWNLOAD_ASSET_URL}/STAGING__kie_sandbox_extended_services_macos_${params.TAG}.dmg - export ONLINE_EDITOR__extendedServicesDownloadUrlWindows=${params.DOWNLOAD_ASSET_URL}/STAGING__kie_sandbox_extended_services_windows_${params.TAG}.exe - export ONLINE_EDITOR__extendedServicesCompatibleVersion=${params.TAG} - export ONLINE_EDITOR__gtmId="" - export ONLINE_EDITOR__corsProxyUrl=https://staging-cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud - export EXTENDED_SERVICES__kieSandboxUrl=https://apache.github.io/incubator-kie-kogito-online-staging/${params.TAG}-prerelease - export SERVERLESS_LOGIC_WEB_TOOLS__version=${params.TAG}-prerelease - export SERVERLESS_LOGIC_WEB_TOOLS__samplesRepositoryRef=${params.TAG} - export SERVERLESS_LOGIC_WEB_TOOLS__buildInfo="${params.TAG} (staging) @ ${params.COMMIT_SHA}" - export SERVERLESS_LOGIC_WEB_TOOLS__corsProxyUrl=https://staging-cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud - - pnpm -F='!@kie-tools/serverless-logic-web-tools-swf-dev-mode-image' \ - -F='!@kie-tools/dev-deployment-base-image' \ - -F='!@kie-tools/dev-deployment-dmn-form-webapp-image' \ - -F='!@kie-tools/dev-deployment-kogito-quarkus-blank-app-image' \ - -F='!@kie-tools/serverless-logic-web-tools-base-builder-image' \ - -F='!@kie-tools/dashbuilder-viewer-image' \ - -F='!@kie-tools/kogito-task-console' \ - -F='!@kie-tools/kogito-management-console' \ - -r --workspace-concurrency=1 build:prod - """.trim() -} - -def uploadKnativeCliWorkflowPluginForWindows() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-windows-amd64.exe', - "STAGING__kn-workflow-windows-amd64-${params.TAG}.exe", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadKnativeCliWorkflowPluginForMacOsM1() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-darwin-arm64', - "STAGING__kn-workflow-darwin-arm64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadKnativeCliWorkflowPluginForMacOs() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-darwin-amd64', - "STAGING__kn-workflow-darwin-amd64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadKnativeCliWorkflowPluginForLinux() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-linux-amd64', - "STAGING__kn-workflow-linux-amd64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadExtendedServicesForLinux() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/extended-services/dist/linux/kie_sandbox_extended_services.tar.gz', - "STAGING__kie_sandbox_extended_services_linux_${params.TAG}.tar.gz", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadChromeExtensionForServerlessWorkflowEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.TAG}.zip", - "STAGING__chrome_extension_serverless_workflow_editor_${params.TAG}.zip", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadChromeExtensionForKieEditors() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.TAG}.zip", - "STAGING__chrome_extension_kogito_kie_editors_${params.TAG}.zip", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionKieBusinessAutomationBundle() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/vscode-extension-kie-ba-bundle/dist/vscode_extension_kie_ba_bundle_${params.TAG}.vsix", - "STAGING__vscode_extension_kie_ba_bundle_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionKogitoBundle() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/vscode-extension-kogito-bundle/dist/vscode_extension_kogito_bundle_${params.TAG}.vsix", - "STAGING__vscode_extension_kogito_bundle_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionDashbuilderEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/vscode-extension-dashbuilder-editor/dist/vscode_extension_dashbuilder_editor_${params.TAG}.vsix", - "STAGING__vscode_extension_dashbuilder_editor_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionServerlessWorkflowEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/serverless-workflow-vscode-extension/dist/serverless_workflow_vscode_extension_${params.TAG}.vsix", - "STAGING__serverless_workflow_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionPMMLEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/pmml-vscode-extension/dist/pmml_vscode_extension_${params.TAG}.vsix", - "STAGING__pmml_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtensionDMNEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/dmn-vscode-extension/dist/dmn_vscode_extension_${params.TAG}.vsix", - "STAGING__dmn_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadVSCodeExtension() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/kie-editors-dev-vscode-extension/dist/kie_editors_dev_vscode_extension_${params.TAG}.vsix", - "STAGING__vscode_extension_dev_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadServerlessLogicWebTools() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/serverless-logic-web-tools/dist.zip', - "STAGING__serverless_logic_web_tools_${params.TAG}.zip", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def uploadOnlineEditor() { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'kie-tools/packages/online-editor/dist.zip', - "STAGING__online_editor_${params.TAG}.zip", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) -} - -def pushKieSandboxHelmChartToQuay() { - helmUtils.pushChartToRegistry( - "${env.KIE_SANDBOX_HELM_CHART__registry}/${env.KIE_SANDBOX_HELM_CHART__account}", - "packages/kie-sandbox-helm-chart/dist/${env.KIE_SANDBOX_HELM_CHART__name}-${env.KIE_SANDBOX_HELM_CHART__tag}.tgz", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def deployOnlineEditor(String tag, String credentialsId) { - DEPLOYMENT_DIR = "${tag}-prerelease" - - sh """#!/bin/bash -el - git config user.email asf-ci-kie@jenkins.kie.apache.org - git config user.name asf-ci-kie - - echo "Switch to main branch" - git checkout main - - echo "Reset deployment dir" - rm -rf ${DEPLOYMENT_DIR} - - echo "Copy Online Editor resources to the deployment dir" - cp -r ${WORKSPACE}/kie-tools/packages/online-editor/dist ${DEPLOYMENT_DIR} - - echo "Create dir for deploying KIE Editors Chrome Extension resources" - mkdir ${DEPLOYMENT_DIR}/chrome-extension - - echo "Create dir for deploying SWF Editor Chrome Extension resources" - mkdir ${DEPLOYMENT_DIR}/swf-chrome-extension - - echo "Copy KIE Editors Chrome Extension resources to the deployment directory" - cp -r ${WORKSPACE}/kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/{bpmn,dmn,scesim,fonts,*-envelope.*} ${DEPLOYMENT_DIR}/chrome-extension - - echo "Copy SWF Editor Chrome Extension resources to the deployment directory" - cp -r ${WORKSPACE}/kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/{fonts,diagram,*-envelope.*,*.bundle.js,*.worker.js} ${DEPLOYMENT_DIR}/swf-chrome-extension - - echo "Remove gwt-editors from the Online Editor deployment" - rm -rf ${DEPLOYMENT_DIR}/gwt-editors - - echo "Make the Online Editor use the resources deployed by the Chrome Extension (to save space)" - ln -s chrome-extension ${DEPLOYMENT_DIR}/gwt-editors - - echo "Commit changes and push" - git add ${tag}-prerelease - git commit -m "Deploy ${tag}-prerelease (staging)" - """.trim() - - githubUtils.pushObject('origin', 'main', "${credentialsId}") -} - -def deployKieSandboxExtendedServicesImageToOpenshift() { - appName = 'staging-kie-sandbox-extended-services' - openShiftUtils.createOrUpdateApp( - "${env.OPENSHIFT_NAMESPACE}", - "${appName}", - "${env.DEPLOY_TAG}", - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}:${env.DEPLOY_TAG}", - "${env.OPENSHIFT_PART_OF}", - 'golang', - "${pipelineVars.openshiftCredentialsId}" - ) - env.KIE_SANDBOX_EXTENDED_SERVICES_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", "${appName}", "${pipelineVars.openshiftCredentialsId}") -} - -def deployCorsProxyImageToOpenshift() { - appName = 'staging-cors-proxy' - openShiftUtils.createOrUpdateApp( - "${env.OPENSHIFT_NAMESPACE}", - "${appName}", - "${env.DEPLOY_TAG}", - "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}/${env.CORS_PROXY_IMAGE__imageName}:${env.DEPLOY_TAG}", - "${env.OPENSHIFT_PART_OF}", - 'nodejs', - "${pipelineVars.openshiftCredentialsId}" - ) - env.KIE_SANDBOX_CORS_PROXY_URL = openShiftUtils.getAppRoute("${env.OPENSHIFT_NAMESPACE}", "${appName}", "${pipelineVars.openshiftCredentialsId}") -} - -def deployKieSandboxImageToOpenshift() { - openShiftUtils.createOrUpdateApp( - "${env.OPENSHIFT_NAMESPACE}", - 'staging-kie-sandbox', - "${env.DEPLOY_TAG}", - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}/${env.KIE_SANDBOX__imageName}:${env.DEPLOY_TAG}", - "${env.OPENSHIFT_PART_OF}", - 'js', - "${pipelineVars.openshiftCredentialsId}", - './deployment.env' - ) -} - -def pushServerlessLogicWebToolsSWFBuilderImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushKieSandboxExtendedServicesImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}", - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}", - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushCorsProxyImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}", - "${env.CORS_PROXY_IMAGE__imageName}", - "${env.CORS_PROXY_IMAGE__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushKieSandboxImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}", - "${env.KIE_SANDBOX__imageName}", - "${env.KIE_SANDBOX__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushServerlessLogicWebToolsSWFDevModeImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushDevDeploymentBaseImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}/${env.DEV_DEPLOYMENT_BASE_IMAGE__account}", - "${env.DEV_DEPLOYMENT_BASE_IMAGE__name}", - "${env.DEV_DEPLOYMENT_BASE_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushDevDeploymentDmnFormWebappImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}/${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}", - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushDevDeploymentKogitoQuarkusBlankAppImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}/${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}", - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushDashbuilderViewerImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.DASHBUILDER__viewerImageRegistry}/${env.DASHBUILDER__viewerImageAccount}", - "${env.DASHBUILDER__viewerImageName}", - "${env.DASHBUILDER__viewerImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushServerlessLogicWebToolsBaseBuilderImageToQuay() { - dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName}", - "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushKogitoTaskConsoleToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KOGITO_TASK_CONSOLE__registry}/${env.KOGITO_TASK_CONSOLE__account}", - "${env.KOGITO_TASK_CONSOLE__name}", - "${env.KOGITO_TASK_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushKogitoManagementConsoleToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KOGITO_MANAGEMENT_CONSOLE__registry}/${env.KOGITO_MANAGEMENT_CONSOLE__account}", - "${env.KOGITO_MANAGEMENT_CONSOLE__name}", - "${env.KOGITO_MANAGEMENT_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" - ) -} - -def pushKogitoSwfBuilderToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_BUILDER_IMAGE__registry}/${env.KOGITO_SWF_BUILDER_IMAGE__account}", - "${env.KOGITO_SWF_BUILDER_IMAGE__name}", - "${env.KOGITO_SWF_BUILDER_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" - ) -} - -def pushKogitoSwfDevModeToQuay() { - dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_DEVMODE_IMAGE__registry}/${env.KOGITO_SWF_DEVMODE_IMAGE__account}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__name}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" - ) -} - -def buildImage(String packageName) { - sh """#!/bin/bash -el - export KIE_TOOLS_BUILD__runTests=true - export KIE_TOOLS_BUILD__buildContainerImages=true - docker system prune -af - echo "Build @kie-tools/${packageName}" - pnpm -F @kie-tools/${packageName}... --workspace-concurrency=1 build:prod - """.trim() -} - -def loadLocalSharedScripts() { - pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' - buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' - githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' - openShiftUtils = load '.ci/jenkins/shared-scripts/openShiftUtils.groovy' - dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' - helmUtils = load '.ci/jenkins/shared-scripts/helmUtils.groovy' - zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' -} diff --git a/.ci/jenkins/Jenkinsfile.staging-dry-run b/.ci/jenkins/Jenkinsfile.staging-dry-run deleted file mode 100644 index 17b651c9931..00000000000 --- a/.ci/jenkins/Jenkinsfile.staging-dry-run +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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. - */ - -@Library('jenkins-pipeline-shared-libraries')_ - -pipeline { - agent { - docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' - label util.avoidFaultyNodes() - } - } - - options { - timeout(time: 600, unit: 'MINUTES') - } - - stages { - stage('Staging Build (dry run)') { - steps { - build job: 'KIE/kie-tools/kie-tools-staging-build', parameters: [ - booleanParam(name: 'DRY_RUN', value: true), - string(name: 'BASE_REF', value: 'main') - ] - } - } - } - - post { - always { - cleanWs(deleteDirs: true) - } - } -} diff --git a/.ci/jenkins/Jenkinsfile.staging-publish b/.ci/jenkins/Jenkinsfile.staging-publish deleted file mode 100644 index 4d4e72d477a..00000000000 --- a/.ci/jenkins/Jenkinsfile.staging-publish +++ /dev/null @@ -1,127 +0,0 @@ -/* - * 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. - */ - -@Library('jenkins-pipeline-shared-libraries')_ - -pipeline { - agent { - docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' - label util.avoidFaultyNodes() - } - } - - options { - timeout(time: 600, unit: 'MINUTES') - } - - stages { - stage('Load local shared scripts') { - steps { - script { - pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' - githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' - } - } - } - - stage('Clean workspace before build') { - steps { - cleanWs(deleteDirs: true, disableDeferredWipeout: true) - } - } - - stage('Checkout kie-tools') { - steps { - dir('kie-tools') { - checkout scm - } - } - } - - stage('Parse `tag`') { - steps { - dir('kie-tools') { - script { - env.TAG = sh(returnStdout: true, script: "#!/bin/bash -el \n node -p \"'\${GIT_BRANCH}'.match(/(.+)-prerelease/)[1]\"").trim() - env.PACKAGE_VERSION = sh(returnStdout: true, script: "#!/bin/bash -el \n node -p \"require('./package.json').version\"").trim() - env.COMMIT_SHA = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() - } - } - } - } - - stage('Check `tag` against `package.json.version`') { - steps { - dir('kie-tools') { - sh """#!/bin/bash -el - [[ "${env.TAG}" == "${env.PACKAGE_VERSION}" ]] - """.trim() - } - } - } - - stage('Create Release (draft)') { - steps { - script { - response = githubUtils.createRelease( - "${pipelineVars.githubRepositorySlug}", - "${env.TAG} @ (alpha)", - "${env.TAG}", - "${env.COMMIT_SHA}", - true, - true, - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) - - respJSON = readJSON text: response - - env.RELEASE_ID = respJSON.id - env.RELEASE_HTML_URL = respJSON.html_url - env.RELEASE_UPLOAD_ASSET_URL = githubUtils.parseReleaseAssetUploadUrl(respJSON.upload_url) - } - } - } - - stage('Generate download URL for Release assets') { - steps { - script { - env.RELEASE_DOWNLOAD_ASSET_URL = sh(returnStdout: true, script: "echo ${env.RELEASE_HTML_URL} | sed \"s#/tag/#/download/#\"").trim() - } - } - } - - stage('Staging Build and Publish') { - steps { - build job: 'KIE/kie-tools/kie-tools-staging-build', parameters: [ - booleanParam(name: 'DRY_RUN', value: false), - string(name: 'BASE_REF', value: "${GIT_BRANCH}"), - string(name: 'TAG', value: "${env.TAG}"), - string(name: 'COMMIT_SHA', value: "${env.COMMIT_SHA}"), - string(name: 'DOWNLOAD_ASSET_URL', value: "${env.RELEASE_DOWNLOAD_ASSET_URL}"), - string(name: 'UPLOAD_ASSET_URL', value: "${env.RELEASE_UPLOAD_ASSET_URL}") - ] - } - } - } - - post { - always { - cleanWs(deleteDirs: true) - } - } -} diff --git a/.ci/jenkins/Jenkinsfile.weekly-publish b/.ci/jenkins/Jenkinsfile.weekly-publish new file mode 100644 index 00000000000..6ca9e6dc1d7 --- /dev/null +++ b/.ci/jenkins/Jenkinsfile.weekly-publish @@ -0,0 +1,173 @@ +/* + * 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. + */ + +import java.text.SimpleDateFormat + +def sdf = new SimpleDateFormat('yyyy-MM-dd') +def dateDefaultValue = sdf.format(new Date()) + +@Library('jenkins-pipeline-shared-libraries')_ + +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + label util.avoidFaultyNodes() + } + } + + options { + timeout(time: 240, unit: 'MINUTES') + } + + parameters { + string(name: 'BUILD_BRANCH_NAME', defaultValue: 'main', description: 'Set the Git branch to checkout', trim: true) + string(name: 'GIT_CHECKOUT_DATETIME', defaultValue: "${dateDefaultValue} 02:00", description: 'Git checkout date and time - (Y-m-d H:i)', trim: true) + } + + environment { + KIE_TOOLS_BUILD__runLinters = 'false' + KIE_TOOLS_BUILD__runTests = 'false' + KIE_TOOLS_BUILD__runEndToEndTests = 'false' + KIE_TOOLS_BUILD__buildContainerImages = 'false' + KIE_TOOLS_BUILD__mavenDeploySkip = 'false' + + PNPM_FILTER_STRING = '-F jbpm-quarkus-devui... -F sonataflow-quarkus-devui...' + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + checkout scm + sh """#!/bin/bash -el + git checkout ${params.BUILD_BRANCH_NAME} + git checkout `git rev-list -n 1 --before=\"${params.GIT_CHECKOUT_DATETIME}\" ${params.BUILD_BRANCH_NAME}` + """.trim() + } + } + } + } + + stage('Setup package version') { + steps { + dir('kie-tools') { + script { + env.PACKAGE_VERSION = sh(returnStdout: true, script: "#!/bin/bash -el \n node -p \"require('./package.json').version\"").trim() + } + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Build and Publish devui packages') { + steps { + dir('kie-tools') { + script { + withCredentials([usernamePassword(credentialsId: "${pipelineVars.mavenDeployRepositoryCredentialsId}", usernameVariable: 'REPOSITORY_USER', passwordVariable: 'REPOSITORY_TOKEN')]) { + configFileProvider([configFile(fileId: "${pipelineVars.mavenSettingsConfigFileId}", variable: 'MAVEN_SETTINGS_FILE')]) { + timestampedSnapshotVersion = getTimestampedSnapshotVersion() + sh """#!/bin/bash -el + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -e "\n--settings=${MAVEN_SETTINGS_FILE}" >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.username=${REPOSITORY_USER} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.password=${REPOSITORY_TOKEN} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Drevision=${timestampedSnapshotVersion} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} build:prod + """.trim() + } + } + } + } + } + } + + stage('Create a new tag') { + steps { + dir('kie-tools') { + script { + tagName = getTagName() + githubUtils.createTag("${tagName}") + githubUtils.pushObject('origin', "${tagName}", "${pipelineVars.asfGithubPushCredentialsId}") + } + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } +} + +String getDateFromCheckoutDateTime() { + /* groovylint-disable-next-line DuplicateNumberLiteral */ + def parsedDate = (params.GIT_CHECKOUT_DATETIME =~ /(\d{4}-\d{2}-\d{2})/)[0][0] + return parsedDate.replace('-', '') +} + +String getTagName() { + if (env.PACKAGE_VERSION == '0.0.0') { + return "999-${getDateFromCheckoutDateTime()}" + } + return "${env.PACKAGE_VERSION}-${getDateFromCheckoutDateTime()}" +} + +String getTimestampedSnapshotVersion() { + if (env.PACKAGE_VERSION == '0.0.0') { + return "999-${getDateFromCheckoutDateTime()}-SNAPSHOT" + } + return "${env.PACKAGE_VERSION}-${getDateFromCheckoutDateTime()}-SNAPSHOT" +} diff --git a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-build b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-build index 5495b13bc7c..1a432ec2963 100644 --- a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-build +++ b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-build @@ -22,7 +22,7 @@ pipeline { } parameters { - string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'latest') + string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'main') } environment { @@ -49,7 +49,7 @@ pipeline { agent { docker { label("${buildUtils.apacheAgentLabels()}") - image "quay.io/kie-tools/kie-tools-ci-build:${params.IMAGE_TAG}" + image "docker.io/apache/incubator-kie-tools-ci-build:${params.IMAGE_TAG}" args '--shm-size=2g --privileged --group-add docker' } } diff --git a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-code-fmt b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-code-fmt index 0214ec331b4..c58819b5cb5 100644 --- a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-code-fmt +++ b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-code-fmt @@ -22,7 +22,7 @@ pipeline { } parameters { - string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'latest') + string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'main') } stages { @@ -38,7 +38,7 @@ pipeline { stage('Check code formatting') { agent { docker { - image "quay.io/kie-tools/kie-tools-ci-build:${params.IMAGE_TAG}" + image "docker.io/apache/incubator-kie-tools-ci-build:${params.IMAGE_TAG}" } } diff --git a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-codeql b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-codeql index 386c3976295..e091d6e5461 100644 --- a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-codeql +++ b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-codeql @@ -22,7 +22,7 @@ pipeline { } parameters { - string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'latest') + string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'main') } environment { @@ -48,7 +48,7 @@ pipeline { stage('Check code quality') { agent { docker { - image "quay.io/kie-tools/kie-tools-ci-build:${params.IMAGE_TAG}" + image "docker.io/apache/incubator-kie-tools-ci-build:${params.IMAGE_TAG}" } } diff --git a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-dependencies b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-dependencies index 75f0c200148..a939aed9f16 100644 --- a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-dependencies +++ b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-check-dependencies @@ -22,7 +22,7 @@ pipeline { } parameters { - string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'latest') + string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'main') } stages { @@ -38,7 +38,7 @@ pipeline { stage('Check dependencies consistency') { agent { docker { - image "quay.io/kie-tools/kie-tools-ci-build:${params.IMAGE_TAG}" + image "docker.io/apache/incubator-kie-tools-ci-build:${params.IMAGE_TAG}" } } diff --git a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-image-build b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-image-build index eee942ddb27..15c4952fbe3 100644 --- a/.ci/jenkins/ci-jobs/Jenkinsfile.ci-image-build +++ b/.ci/jenkins/ci-jobs/Jenkinsfile.ci-image-build @@ -18,13 +18,14 @@ pipeline { agent any parameters { - string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'latest') + string(description: 'Image tag', name: 'IMAGE_TAG', defaultValue: 'main') } environment { - IMAGE_ACCOUNT = 'quay.io/kie-tools' - IMAGE_NAME = 'kie-tools-ci-build' - IMAGE_NAME_TAG = "${IMAGE_ACCOUNT}/${IMAGE_NAME}:${params.IMAGE_TAG}" + IMAGE_REGISTRY = 'docker.io' + IMAGE_ACCOUNT = 'apache' + IMAGE_NAME = 'incubator-kie-tools-ci-build' + IMAGE_NAME_TAG = "${IMAGE_REGISTRY}/${IMAGE_ACCOUNT}/${IMAGE_NAME}:${params.IMAGE_TAG}" DOCKER_CONFIG = "${WORKSPACE}/.docker" } @@ -70,10 +71,12 @@ pipeline { steps { script { IMAGE_EXISTS = dockerUtils.checkImageExistsInRegistry( + "${IMAGE_REGISTRY}", "${IMAGE_ACCOUNT}", "${IMAGE_NAME}", "${params.IMAGE_TAG}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -90,12 +93,12 @@ pipeline { script { if (!env.CHANGE_ID && env.BRANCH_NAME == 'main') { sh """ - docker build -t ${IMAGE_NAME_TAG} -f .ci/kie-tools-ci-build.Dockerfile . - docker tag ${IMAGE_NAME_TAG} ${IMAGE_ACCOUNT}/${IMAGE_NAME}:latest + docker build -t ${IMAGE_NAME_TAG} -f .ci/incubator-kie-tools-ci-build.Dockerfile . + docker tag ${IMAGE_NAME_TAG} ${IMAGE_ACCOUNT}/${IMAGE_NAME}:main """ IMAGE_TAGS = "${params.IMAGE_TAG} latest" } else { - sh "docker build -t ${IMAGE_NAME_TAG} --label 'quay.expires-after=1d' -f .ci/kie-tools-ci-build.Dockerfile ." + sh "docker build -t ${IMAGE_NAME_TAG} -f .ci/incubator-kie-tools-ci-build.Dockerfile ." IMAGE_TAGS = "${params.IMAGE_TAG}" } } @@ -110,10 +113,12 @@ pipeline { steps { script { dockerUtils.pushImageToRegistry( + "${IMAGE_REGISTRY}", "${IMAGE_ACCOUNT}", "${IMAGE_NAME}", "${IMAGE_TAGS}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -133,6 +138,9 @@ pipeline { node --version npm --version pnpm --version + python --version + docker --version + helm version go version '''.trim() } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.chrome-extensions b/.ci/jenkins/release-jobs/Jenkinsfile.chrome-extensions index dec6a9ba5f3..e7c5057519d 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.chrome-extensions +++ b/.ci/jenkins/release-jobs/Jenkinsfile.chrome-extensions @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,9 +28,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -40,13 +42,15 @@ pipeline { KIE_TOOLS_BUILD__buildContainerImages = 'true' CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io' - CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.TAG}" + CHROME_EXTENSION__routerRelativePath = "incubator-kie-kogito-online/chrome-extension/${params.RELEASE_VERSION}" CHROME_EXTENSION__manifestFile = 'manifest.prod.json' CHROME_EXTENSION__onlineEditorUrl = 'https://apache.github.io/incubator-kie-kogito-online' SWF_CHROME_EXTENSION__routerTargetOrigin = 'https://apache.github.io' - SWF_CHROME_EXTENSION__routerRelativePath = "kogito-online/swf-chrome-extension/${params.TAG}" + SWF_CHROME_EXTENSION__routerRelativePath = "kogito-online/swf-chrome-extension/${params.RELEASE_VERSION}" SWF_CHROME_EXTENSION__manifestFile = 'manifest.prod.json' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + PNPM_FILTER_STRING = '-F chrome-extension-pack-kogito-kie-editors... -F chrome-extension-serverless-workflow-editor...' } @@ -58,6 +62,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' chromeStoreUtils = load '.ci/jenkins/shared-scripts/chromeStoreUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -142,8 +147,8 @@ pipeline { script { githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.TAG}.zip", - "chrome_extension_kogito_kie_editors_${params.TAG}.zip", + "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.RELEASE_VERSION}.zip", + "chrome_extension_kogito_kie_editors_${params.RELEASE_VERSION}.zip", 'application/zip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) @@ -153,13 +158,13 @@ pipeline { stage('Deploy Chrome Extension for KIE Editors to GitHub Pages (kogito-online)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { dir('kogito-online') { script { - EDITORS_DIR = "editors/${params.TAG}" - CHROME_EXTENSION_DIR = "chrome-extension/${params.TAG}" + EDITORS_DIR = "editors/${params.RELEASE_VERSION}" + CHROME_EXTENSION_DIR = "chrome-extension/${params.RELEASE_VERSION}" sh """#!/bin/bash -el git config user.email asf-ci-kie@jenkins.kie.apache.org @@ -177,7 +182,7 @@ pipeline { echo "Commit changes and push" git add . - git commit -m "Deploy ${params.TAG} (Chrome Extension for Kogito KIE Editors)" + git commit -m "Deploy ${params.RELEASE_VERSION} (Chrome Extension for Kogito KIE Editors)" """.trim() githubUtils.pushObject('origin', 'gh-pages', "${pipelineVars.asfGithubPushCredentialsId}") @@ -188,14 +193,14 @@ pipeline { stage('Upload Chrome Extension for KIE Editors to the Chrome Store') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { env.CHROME_EXTESION_KIE_EDITORS_UPLOAD_STATUS = chromeStoreUtils.uploadExtension( "${pipelineVars.chromeStoreCredentialsId}", "${pipelineVars.chromeStoreRefreshTokenCredentialsId}", - "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.TAG}.zip", + "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.RELEASE_VERSION}.zip", "${pipelineVars.chromeExtensionIdCredentialsId}" ) } @@ -204,7 +209,7 @@ pipeline { stage('Check Upload - Chrome Extension for KIE Editors') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { sh """#!/bin/bash -el @@ -215,7 +220,7 @@ pipeline { stage('Publish Chrome Extension for KIE Editors for users') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { @@ -230,7 +235,7 @@ pipeline { stage('Check Publish - Chrome Extension for KIE Editors') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { sh """#!/bin/bash -el @@ -241,14 +246,14 @@ pipeline { stage('Upload Chrome Extension for Serverless Workflow Editor') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.TAG}.zip", - "chrome_extension_serverless_workflow_editor_${params.TAG}.zip", + "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.RELEASE_VERSION}.zip", + "chrome_extension_serverless_workflow_editor_${params.RELEASE_VERSION}.zip", 'application/zip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) @@ -258,17 +263,17 @@ pipeline { stage('Deploy Chrome Extension for Serverless Workflow Editor to GitHub Pages (kogito-online)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { dir('kogito-online') { script { - DEPLOYMENT_DIR = "swf-chrome-extension/${params.TAG}" + DEPLOYMENT_DIR = "swf-chrome-extension/${params.RELEASE_VERSION}" sh """#!/bin/bash -el git config user.email asf-ci-kie@jenkins.kie.apache.org git config user.name asf-ci-kie - + git checkout gh-pages echo "Copy Chrome Extension resources" @@ -278,7 +283,7 @@ pipeline { echo "Commit changes and push" git add . - git commit -m "Deploy ${params.TAG} (Chrome Extension for Serverless Workflow Editor)" + git commit -m "Deploy ${params.RELEASE_VERSION} (Chrome Extension for Serverless Workflow Editor)" """.trim() githubUtils.pushObject('origin', 'gh-pages', "${pipelineVars.asfGithubPushCredentialsId}") @@ -289,14 +294,14 @@ pipeline { stage('Upload Chrome Extension for Serverless Workflow Editor to the Chrome Store') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { env.CHROME_EXTESION_SERVERLESS_WORKFLOW_EDITOR_UPLOAD_STATUS = chromeStoreUtils.uploadExtension( "${pipelineVars.chromeStoreCredentialsId}", "${pipelineVars.chromeStoreRefreshTokenCredentialsId}", - "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.TAG}.zip", + "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.RELEASE_VERSION}.zip", "${pipelineVars.swfChromeExtensionIdCredentialsId}" ) } @@ -305,7 +310,7 @@ pipeline { stage('Check Upload - Chrome Extension for Serverless Workflow Editor') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { sh """#!/bin/bash -el @@ -316,7 +321,7 @@ pipeline { stage('Publish Chrome Extension for Serverless Workflow Editor for users') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { @@ -331,7 +336,7 @@ pipeline { stage('Check Publish - Chrome Extension for Serverless Workflow Editor') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { sh """#!/bin/bash -el @@ -339,6 +344,53 @@ pipeline { """.trim() } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.CHROME_EXTENSION_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-business-automation-chrome-extension.zip" + env.SWF_CHROME_EXTENSION_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-chrome-extension.zip" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + cp "kie-tools/packages/chrome-extension-pack-kogito-kie-editors/dist/chrome_extension_kogito_kie_editors_${params.RELEASE_VERSION}.zip" "${env.RELEASE_ARTIFACTS_DIR}/${CHROME_EXTENSION_RELEASE_ZIP_FILE}" + cp "kie-tools/packages/chrome-extension-serverless-workflow-editor/dist/chrome_extension_serverless_workflow_editor_${params.RELEASE_VERSION}.zip" "${env.RELEASE_ARTIFACTS_DIR}/${SWF_CHROME_EXTENSION_RELEASE_ZIP_FILE}" + """.trim() + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.CHROME_EXTENSION_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.SWF_CHROME_EXTENSION_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.cors-proxy b/.ci/jenkins/release-jobs/Jenkinsfile.cors-proxy index 3cf730edb60..69968eee694 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.cors-proxy +++ b/.ci/jenkins/release-jobs/Jenkinsfile.cors-proxy @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -42,10 +44,12 @@ pipeline { OPENSHIFT_PART_OF = 'kie-sandbox-app' OPENSHIFT_NAMESPACE = 'kie-sandbox' - CORS_PROXY_IMAGE__imageRegistry = 'quay.io' - CORS_PROXY_IMAGE__imageAccount = 'kie-tools' - CORS_PROXY_IMAGE__imageName = 'cors-proxy-image' - CORS_PROXY_IMAGE__imageBuildTags = "latest ${params.TAG}" + CORS_PROXY_IMAGE__imageRegistry = 'docker.io' + CORS_PROXY_IMAGE__imageAccount = 'apache' + CORS_PROXY_IMAGE__imageName = 'incubator-kie-cors-proxy' + CORS_PROXY_IMAGE__imageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -61,6 +65,7 @@ pipeline { githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' openShiftUtils = load '.ci/jenkins/shared-scripts/openShiftUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -79,6 +84,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -123,17 +138,19 @@ pipeline { } } - stage('Push cors-proxy-image to quay.io') { + stage('Push cors-proxy-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}", + "${env.CORS_PROXY_IMAGE__imageRegistry}", + "${env.CORS_PROXY_IMAGE__imageAccount}", "${env.CORS_PROXY_IMAGE__imageName}", "${env.CORS_PROXY_IMAGE__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -141,15 +158,15 @@ pipeline { stage('Deploy cors-proxy-image to OpenShift') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { openShiftUtils.createOrUpdateApp( "${env.OPENSHIFT_NAMESPACE}", "${env.OPENSHIFT_APP_NAME}", - "${params.TAG}", - "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}/${env.CORS_PROXY_IMAGE__imageName}:${params.TAG}", + "${params.RELEASE_VERSION}", + "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}/${env.CORS_PROXY_IMAGE__imageName}:${params.RELEASE_VERSION}", "${env.OPENSHIFT_PART_OF}", 'nodejs', "${pipelineVars.openshiftCredentialsId}" @@ -157,6 +174,50 @@ pipeline { } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${env.CORS_PROXY_IMAGE__imageName}.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.CORS_PROXY_IMAGE__imageRegistry}/${env.CORS_PROXY_IMAGE__imageAccount}/${env.CORS_PROXY_IMAGE__imageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.dashbuilder-viewer-image b/.ci/jenkins/release-jobs/Jenkinsfile.dashbuilder-viewer-image index 5b80a464460..6a2c368ce45 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.dashbuilder-viewer-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.dashbuilder-viewer-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - DASHBUILDER__viewerImageRegistry = 'quay.io' - DASHBUILDER__viewerImageAccount = 'kie-tools' - DASHBUILDER__viewerImageName = 'dashbuilder-viewer-image' - DASHBUILDER__viewerImageBuildTags = "latest ${params.TAG}" + DASHBUILDER__viewerImageRegistry = 'docker.io' + DASHBUILDER__viewerImageAccount = 'apache' + DASHBUILDER__viewerImageName = 'incubator-kie-dashbuilder-viewer' + DASHBUILDER__viewerImageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push dashbuilder-viewer-image to quay.io') { + stage('Push dashbuilder-viewer-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.DASHBUILDER__viewerImageRegistry}/${env.DASHBUILDER__viewerImageAccount}", + "${env.DASHBUILDER__viewerImageRegistry}", + "${env.DASHBUILDER__viewerImageAccount}", "${env.DASHBUILDER__viewerImageName}", "${env.DASHBUILDER__viewerImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${env.DASHBUILDER__viewerImageName}.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.DASHBUILDER__viewerImageRegistry}/${env.DASHBUILDER__viewerImageAccount}/${env.DASHBUILDER__viewerImageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-base-image b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-base-image index 14fe45be0ba..cd807574491 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-base-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-base-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - DEV_DEPLOYMENT_BASE_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_BASE_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_BASE_IMAGE__name = 'dev-deployment-base-image' - DEV_DEPLOYMENT_BASE_IMAGE__buildTags = "latest ${params.TAG}" + DEV_DEPLOYMENT_BASE_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_BASE_IMAGE__account = 'apache' + DEV_DEPLOYMENT_BASE_IMAGE__name = 'incubator-kie-sandbox-dev-deployment-base' + DEV_DEPLOYMENT_BASE_IMAGE__buildTag = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,22 +133,67 @@ pipeline { } } - stage('Push dev-deployment-base-image to quay.io') { + stage('Push dev-deployment-base-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}/${env.DEV_DEPLOYMENT_BASE_IMAGE__account}", + "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_BASE_IMAGE__account}", "${env.DEV_DEPLOYMENT_BASE_IMAGE__name}", - "${env.DEV_DEPLOYMENT_BASE_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_BASE_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-base-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.DEV_DEPLOYMENT_BASE_IMAGE__registry}/${env.DEV_DEPLOYMENT_BASE_IMAGE__account}/${env.DEV_DEPLOYMENT_BASE_IMAGE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-dmn-form-webapp-image b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-dmn-form-webapp-image index 7a2286b0651..af5f01b0f48 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-dmn-form-webapp-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-dmn-form-webapp-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'dev-deployment-dmn-form-webapp-image' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags = "latest ${params.TAG}" + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'apache' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'incubator-dev-deployment-dmn-form-webapp' + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push dev-deployment-dmn-form-webapp-image to quay.io') { + stage('Push dev-deployment-dmn-form-webapp-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}/${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}", + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}", "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-dmn-form-webapp-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry}/${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account}/${env.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-kogito-quarkus-blank-app-image b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-kogito-quarkus-blank-app-image index 356076b2725..2ea86b8fdeb 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-kogito-quarkus-blank-app-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-kogito-quarkus-blank-app-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name = 'dev-deployment-kogito-quarkus-blank-app-image' - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags = "latest ${params.TAG}" + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry = 'docker.io' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account = 'apache' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name = 'incubator-dev-deployment-kogito-quarkus-blank-app' + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,22 +133,67 @@ pipeline { } } - stage('Push dev-deployment-kogito-quarkus-blank-app-image to quay.io') { + stage('Push dev-deployment-kogito-quarkus-blank-app-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}/${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}", + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}", + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}", "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name}", - "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-quarkus-blank-app-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry}/${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account}/${env.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-upload-service b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-upload-service index a204373c347..0b9d5f6ac12 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-upload-service +++ b/.ci/jenkins/release-jobs/Jenkinsfile.dev-deployment-upload-service @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,9 +28,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -39,6 +41,8 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + PNPM_FILTER_STRING = '-F @kie-tools/dev-deployment-upload-service...' } @@ -49,6 +53,7 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -109,77 +114,130 @@ pipeline { stage('Upload Dev Deployment Upload Service assets') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { // macOS - amd64 - tar.gz githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-amd64-${params.TAG}.tar.gz", - "dev-deployment-upload-service-darwin-amd64-${params.TAG}.tar.gz", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-amd64-${params.RELEASE_VERSION}.tar.gz", + "dev-deployment-upload-service-darwin-amd64-${params.RELEASE_VERSION}.tar.gz", 'application/tar+gzip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // macOS - amd64 - tar.gz.sha256 githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-amd64-${params.TAG}.tar.gz.sha256", - "dev-deployment-upload-service-darwin-amd64-${params.TAG}.tar.gz.sha256", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", + "dev-deployment-upload-service-darwin-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", 'text/plain', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // macOS - arm64 - tar.gz githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-arm64-${params.TAG}.tar.gz", - "dev-deployment-upload-service-darwin-arm64-${params.TAG}.tar.gz", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-arm64-${params.RELEASE_VERSION}.tar.gz", + "dev-deployment-upload-service-darwin-arm64-${params.RELEASE_VERSION}.tar.gz", 'application/tar+gzip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // macOS - arm64 - tar.gz.sha256 githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-arm64-${params.TAG}.tar.gz.sha256", - "dev-deployment-upload-service-darwin-arm64-${params.TAG}.tar.gz.sha256", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-arm64-${params.RELEASE_VERSION}.tar.gz.sha256", + "dev-deployment-upload-service-darwin-arm64-${params.RELEASE_VERSION}.tar.gz.sha256", 'text/plain', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // linux - amd64 - tar.gz githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64-${params.TAG}.tar.gz", - "dev-deployment-upload-service-linux-amd64-${params.TAG}.tar.gz", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64-${params.RELEASE_VERSION}.tar.gz", + "dev-deployment-upload-service-linux-amd64-${params.RELEASE_VERSION}.tar.gz", 'application/tar+gzip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // linux - amd64 - tar.gz.sha256 githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64-${params.TAG}.tar.gz.sha256", - "dev-deployment-upload-service-linux-amd64-${params.TAG}.tar.gz.sha256", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", + "dev-deployment-upload-service-linux-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", 'text/plain', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // windows - amd64 - tar.gz githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-windows-amd64-${params.TAG}.tar.gz", - "dev-deployment-upload-service-windows-amd64-${params.TAG}.tar.gz", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-windows-amd64-${params.RELEASE_VERSION}.tar.gz", + "dev-deployment-upload-service-windows-amd64-${params.RELEASE_VERSION}.tar.gz", 'application/tar+gzip', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) // windows - amd64 - tar.gz.sha256 githubUtils.uploadReleaseAsset( "${params.UPLOAD_ASSET_URL}", - "packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-windows-amd64-${params.TAG}.tar.gz.sha256", - "dev-deployment-upload-service-windows-amd64-${params.TAG}.tar.gz.sha256", + "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-windows-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", + "dev-deployment-upload-service-windows-amd64-${params.RELEASE_VERSION}.tar.gz.sha256", 'text/plain', "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" ) } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_ARM64_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-upload-service-macOS-arm64.tar.gz" + env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_X86_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-upload-service-macOS-x86.tar.gz" + env.DEV_DEPLOYMENT_UPLOAD_SERVICE_LINUX_X86_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-upload-service-linux-x86.tar.gz" + env.DEV_DEPLOYMENT_UPLOAD_SERVICE_WINDOWS_X86_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-dev-deployment-upload-service-windows-x86.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + cp "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-amd64-${params.RELEASE_VERSION}.tar.gz" "${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_ARM64_RELEASE_FILE}" + cp "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-darwin-arm64-${params.RELEASE_VERSION}.tar.gz" "${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_X86_RELEASE_FILE}" + cp "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64-${params.RELEASE_VERSION}.tar.gz" "${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_LINUX_X86_RELEASE_FILE}" + cp "kie-tools/packages/dev-deployment-upload-service/dist/dev-deployment-upload-service-windows-amd64-${params.RELEASE_VERSION}.tar.gz" "${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_WINDOWS_X86_RELEASE_FILE}" + """.trim() + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_ARM64_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_MAC_X86_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_LINUX_X86_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DEV_DEPLOYMENT_UPLOAD_SERVICE_WINDOWS_X86_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.extended-services b/.ci/jenkins/release-jobs/Jenkinsfile.extended-services index 8679ea7fb80..c435fc20137 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.extended-services +++ b/.ci/jenkins/release-jobs/Jenkinsfile.extended-services @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,9 +28,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -39,6 +41,8 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + PNPM_FILTER_STRING = '-F @kie-tools/extended-services...' } @@ -49,6 +53,7 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -109,16 +114,62 @@ pipeline { stage('Upload Extended Services for Linux') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'packages/extended-services/dist/linux/kie_sandbox_extended_services.tar.gz', + "kie_sandbox_extended_services_linux_${params.RELEASE_VERSION}.tar.gz", + 'application/tar+gzip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-extended-services-linux-x86.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + cp kie-tools/packages/extended-services/dist/linux/kie_sandbox_extended_services.tar.gz "${env.RELEASE_ARTIFACTS_DIR}/${RELEASE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${RELEASE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } } steps { script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'packages/extended-services/dist/linux/kie_sandbox_extended_services.tar.gz', - "kie_sandbox_extended_services_linux_${params.TAG}.tar.gz", - 'application/tar+gzip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.jbpm-quarkus-devui b/.ci/jenkins/release-jobs/Jenkinsfile.jbpm-quarkus-devui new file mode 100644 index 00000000000..7c4dfd1070f --- /dev/null +++ b/.ci/jenkins/release-jobs/Jenkinsfile.jbpm-quarkus-devui @@ -0,0 +1,147 @@ +/* + * 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. + */ +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + } + } + + options { + timeout(time: 180, unit: 'MINUTES') + } + + parameters { + booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') + string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + } + + environment { + KIE_TOOLS_BUILD__runLinters = 'false' + KIE_TOOLS_BUILD__runTests = 'false' + KIE_TOOLS_BUILD__runEndToEndTests = 'false' + KIE_TOOLS_BUILD__mavenDeploySkip = 'false' + + DOCKER_CONFIG = "${WORKSPACE}/.docker" + + PNPM_FILTER_STRING = '-F jbpm-quarkus-devui...' + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + } + } + } + + stage('Start required services for build and tests (DinD, Xvfb, Fluxbox)') { + steps { + script { + buildUtils.startRequiredServices() + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Setup signing key') { + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Build and Deploy') { + steps { + dir('kie-tools') { + script { + if (params.DRY_RUN) { + env.KIE_TOOLS_BUILD__mavenDeploySkip = 'true' + } + withCredentials([usernamePassword(credentialsId: "${pipelineVars.mavenDeployRepositoryCredentialsId}", usernameVariable: 'REPOSITORY_USER', passwordVariable: 'REPOSITORY_TOKEN')]) { + withCredentials([string(credentialsId: "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}", variable: 'SIGNING_KEY_PASSWORD')]) { + configFileProvider([configFile(fileId: "${pipelineVars.mavenSettingsConfigFileId}", variable: 'MAVEN_SETTINGS_FILE')]) { + sh """#!/bin/bash -el + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -e "\n--settings=${MAVEN_SETTINGS_FILE}" >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.username=${REPOSITORY_USER} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.password=${REPOSITORY_TOKEN} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dgpg.passphrase=${SIGNING_KEY_PASSWORD} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Papache-release >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} build:prod + """.trim() + } + } + } + } + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true) + } + } +} diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox index a3cd66296ad..a2fed5c4f11 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,10 +28,12 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Kie Sandbox Extendend Services Url', name: 'KIE_SANDBOX_EXTENDED_SERVICES_URL') string(description: 'Kie Sandbox Cors Proxy Url', name: 'KIE_SANDBOX_CORS_PROXY_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -44,28 +46,30 @@ pipeline { OPENSHIFT_PART_OF = 'kie-sandbox-app' OPENSHIFT_NAMESPACE = 'kie-sandbox' - KIE_SANDBOX__imageRegistry = 'quay.io' - KIE_SANDBOX__imageAccount = 'kie-tools' - KIE_SANDBOX__imageName = 'kie-sandbox-image' - KIE_SANDBOX__imageBuildTags = "latest ${params.TAG}" - ONLINE_EDITOR__buildInfo = "${params.TAG}" - ONLINE_EDITOR__extendedServicesDownloadUrlLinux = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_linux_${params.TAG}.tar.gz" - ONLINE_EDITOR__extendedServicesDownloadUrlMacOs = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_macos_${params.TAG}.dmg" - ONLINE_EDITOR__extendedServicesDownloadUrlWindows = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_windows_${params.TAG}.exe" - ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.TAG}" - ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentBaseImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentBaseImageName = 'dev-deployment-base-image' - ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.TAG}" - ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'dev-deployment-dmn-form-webapp-image' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.TAG}" + KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry = 'docker.io' + KIE_SANDBOX_WEBAPP_IMAGE__imageAccount = 'apache' + KIE_SANDBOX_WEBAPP_IMAGE__imageName = 'incubator-kie-sandbox-webapp' + KIE_SANDBOX_WEBAPP_IMAGE__imageBuildTags = "latest ${params.RELEASE_VERSION}" + ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__extendedServicesDownloadUrlLinux = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_linux_${params.RELEASE_VERSION}.tar.gz" + ONLINE_EDITOR__extendedServicesDownloadUrlMacOs = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_macos_${params.RELEASE_VERSION}.dmg" + ONLINE_EDITOR__extendedServicesDownloadUrlWindows = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_windows_${params.RELEASE_VERSION}.exe" + ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base' + ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}" ONLINE_EDITOR__gtmId = 'GTM-PQGMKNW' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + DOCKER_CONFIG = "${WORKSPACE}/.docker" - PNPM_FILTER_STRING = '-F @kie-tools/kie-sandbox-image...' + PNPM_FILTER_STRING = '-F @kie-tools/kie-sandbox-webapp-image...' } stages { @@ -77,6 +81,7 @@ pipeline { githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' openShiftUtils = load '.ci/jenkins/shared-scripts/openShiftUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -95,6 +100,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -139,17 +154,19 @@ pipeline { } } - stage('Push kie-sandbox-image to quay.io') { + stage('Push kie-sandbox-image to Docker Hub') { when { expression { !params.DRY_RUN } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}", - "${env.KIE_SANDBOX__imageName}", - "${env.KIE_SANDBOX__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageAccount}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageName}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageBuildTags}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -157,7 +174,7 @@ pipeline { stage('Prepare environment variables for OpenShift deployment') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { sh """#!/bin/bash -el @@ -169,15 +186,15 @@ pipeline { stage('Deploy kie-sandbox-image to OpenShift') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { openShiftUtils.createOrUpdateApp( "${env.OPENSHIFT_NAMESPACE}", "${env.OPENSHIFT_APP_NAME}", - "${params.TAG}", - "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}/${env.KIE_SANDBOX__imageName}:${params.TAG}", + "${params.RELEASE_VERSION}", + "${env.KIE_SANDBOX_WEBAPP_IMAGE__imageRegistry}/${env.KIE_SANDBOX_WEBAPP_IMAGE__imageAccount}/${env.KIE_SANDBOX_WEBAPP_IMAGE__imageName}:${params.RELEASE_VERSION}", "${env.OPENSHIFT_PART_OF}", 'js', "${pipelineVars.openshiftCredentialsId}", @@ -186,6 +203,50 @@ pipeline { } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-webapp-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KIE_SANDBOX__imageRegistry}/${env.KIE_SANDBOX__imageAccount}/${env.KIE_SANDBOX__imageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-extended-services b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-extended-services index e229efc4782..80178ceff84 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-extended-services +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-extended-services @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -42,10 +44,12 @@ pipeline { OPENSHIFT_PART_OF = 'kie-sandbox-app' OPENSHIFT_NAMESPACE = 'kie-sandbox' - KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry = 'quay.io' - KIE_SANDBOX_EXTENDED_SERVICES__imageAccount = 'kie-tools' - KIE_SANDBOX_EXTENDED_SERVICES__imageName = 'kie-sandbox-extended-services-image' - KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags = "latest ${params.TAG}" + KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry = 'docker.io' + KIE_SANDBOX_EXTENDED_SERVICES__imageAccount = 'apache' + KIE_SANDBOX_EXTENDED_SERVICES__imageName = 'incubator-kie-sandbox-extended-services' + KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -61,6 +65,7 @@ pipeline { githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' openShiftUtils = load '.ci/jenkins/shared-scripts/openShiftUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -79,6 +84,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -123,17 +138,19 @@ pipeline { } } - stage('Push kie-sandbox-extended-services-image to quay.io') { + stage('Push kie-sandbox-extended-services-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}", + "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}", + "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}", "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}", "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } @@ -141,15 +158,15 @@ pipeline { stage('Deploy kie-sandbox-extended-services-image to OpenShift') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { openShiftUtils.createOrUpdateApp( "${env.OPENSHIFT_NAMESPACE}", "${env.OPENSHIFT_APP_NAME}", - "${params.TAG}", - "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}:${params.TAG}", + "${params.RELEASE_VERSION}", + "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}:${params.RELEASE_VERSION}", "${env.OPENSHIFT_PART_OF}", 'golang', "${pipelineVars.openshiftCredentialsId}" @@ -157,6 +174,50 @@ pipeline { } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-extended-services-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KIE_SANDBOX_EXTENDED_SERVICES__imageRegistry}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageAccount}/${env.KIE_SANDBOX_EXTENDED_SERVICES__imageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-helm-chart b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-helm-chart index 9a6531c4e59..1d3a8f9c919 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-helm-chart +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kie-sandbox-helm-chart @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - KIE_SANDBOX_HELM_CHART__registry = 'quay.io' - KIE_SANDBOX_HELM_CHART__account = 'kie-tools' - KIE_SANDBOX_HELM_CHART__name = 'kie-sandbox-helm-chart' - KIE_SANDBOX_HELM_CHART__tag = "${params.TAG}" + KIE_SANDBOX_HELM_CHART__registry = 'docker.io' + KIE_SANDBOX_HELM_CHART__account = 'apache' + KIE_SANDBOX_HELM_CHART__name = 'incubator-kie-sandbox-helm-chart' + KIE_SANDBOX_HELM_CHART__tag = "${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' helmUtils = load '.ci/jenkins/shared-scripts/helmUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,9 +133,9 @@ pipeline { } } - stage('Push kie-sandbox-helm-chart to quay.io') { + stage('Push kie-sandbox-helm-chart to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { dir('kie-tools') { @@ -128,12 +143,57 @@ pipeline { helmUtils.pushChartToRegistry( "${env.KIE_SANDBOX_HELM_CHART__registry}/${env.KIE_SANDBOX_HELM_CHART__account}", "packages/kie-sandbox-helm-chart/dist/${env.KIE_SANDBOX_HELM_CHART__name}-${env.KIE_SANDBOX_HELM_CHART__tag}.tgz", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" ) } } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-helm-chart.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + cp "kie-tools/packages/kie-sandbox-helm-chart/dist/${env.KIE_SANDBOX_HELM_CHART__name}-${env.KIE_SANDBOX_HELM_CHART__tag}.tgz" "${env.RELEASE_ARTIFACTS_DIR}/${RELEASE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${RELEASE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kn-plugin-workflow b/.ci/jenkins/release-jobs/Jenkinsfile.kn-plugin-workflow index 5342321fef5..93149eccd8f 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kn-plugin-workflow +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kn-plugin-workflow @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,9 +28,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -39,6 +41,8 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + PNPM_FILTER_STRING = '-F @kie-tools/kn-plugin-workflow...' } @@ -49,6 +53,8 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' } } } @@ -69,103 +75,170 @@ pipeline { stage('Checkout kie-tools') { steps { - script { - githubUtils.checkoutRepo( - "http://github.com/${pipelineVars.githubRepositorySlug}.git", - "${params.BASE_REF}", - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } } } } stage('Setup PNPM') { steps { - script { - buildUtils.setupPnpm() + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } } } } stage('PNPM Bootstrap') { steps { - script { - buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } } } } stage('Build') { steps { - script { - buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING}") + dir('kie-tools') { + script { + buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING}") + } } } } stage('Upload Knative CLI Workflow Plugin for Linux') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'packages/kn-plugin-workflow/dist/kn-workflow-linux-amd64', - "kn-workflow-linux-amd64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'packages/kn-plugin-workflow/dist/kn-workflow-linux-amd64', + "kn-workflow-linux-amd64-${params.RELEASE_VERSION}", + 'application/octet-stream', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload Knative CLI Workflow Plugin for macOS') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'packages/kn-plugin-workflow/dist/kn-workflow-darwin-amd64', - "kn-workflow-darwin-amd64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'packages/kn-plugin-workflow/dist/kn-workflow-darwin-amd64', + "kn-workflow-darwin-amd64-${params.RELEASE_VERSION}", + 'application/octet-stream', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload Knative CLI Workflow Plugin for macOS M1') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'packages/kn-plugin-workflow/dist/kn-workflow-darwin-arm64', - "kn-workflow-darwin-arm64-${params.TAG}", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'packages/kn-plugin-workflow/dist/kn-workflow-darwin-arm64', + "kn-workflow-darwin-arm64-${params.RELEASE_VERSION}", + 'application/octet-stream', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload Knative CLI Workflow Plugin for Windows') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'packages/kn-plugin-workflow/dist/kn-workflow-windows-amd64.exe', + "kn-workflow-windows-amd64-${params.RELEASE_VERSION}.exe", + 'application/octet-stream', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.KN_PLUGIN_LINUX_X86_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-knative-plugin-linux-x86.zip" + env.KN_PLUGIN_MACOS_ARM_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-knative-plugin-macOS-arm64.zip" + env.KN_PLUGIN_MACOS_X86_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-knative-plugin-macOS-x86.zip" + env.KN_PLUGIN_WINDOWS_X86_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-knative-plugin-windows-x86.zip" + + sh "mkdir ${env.RELEASE_ARTIFACTS_DIR}" + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_LINUX_X86_RELEASE_ZIP_FILE}", 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-linux-amd64') + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_MACOS_ARM_RELEASE_ZIP_FILE}", 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-darwin-arm64') + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_MACOS_X86_RELEASE_ZIP_FILE}", 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-darwin-amd64') + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_WINDOWS_X86_RELEASE_ZIP_FILE}", 'kie-tools/packages/kn-plugin-workflow/dist/kn-workflow-windows-amd64.exe') + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_LINUX_X86_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_MACOS_ARM_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_MACOS_X86_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KN_PLUGIN_WINDOWS_X86_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } } steps { script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - 'packages/kn-plugin-workflow/dist/kn-workflow-windows-amd64.exe', - "kn-workflow-windows-amd64-${params.TAG}.exe", - 'application/octet-stream', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-management-console b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-management-console index 9a8793410cc..86bd30540bf 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-management-console +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-management-console @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - KOGITO_MANAGEMENT_CONSOLE__registry = 'quay.io' - KOGITO_MANAGEMENT_CONSOLE__account = 'kie-tools' - KOGITO_MANAGEMENT_CONSOLE__name = 'kogito-management-console' - KOGITO_MANAGEMENT_CONSOLE__buildTags = "latest ${params.TAG}" + KOGITO_MANAGEMENT_CONSOLE__registry = 'docker.io' + KOGITO_MANAGEMENT_CONSOLE__account = 'apache' + KOGITO_MANAGEMENT_CONSOLE__name = 'incubator-kie-kogito-management-console' + KOGITO_MANAGEMENT_CONSOLE__buildTag = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push kogito-management-console to quay.io') { + stage('Push kogito-management-console to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_MANAGEMENT_CONSOLE__registry}/${env.KOGITO_MANAGEMENT_CONSOLE__account}", + "${env.KOGITO_MANAGEMENT_CONSOLE__registry}", + "${env.KOGITO_MANAGEMENT_CONSOLE__account}", "${env.KOGITO_MANAGEMENT_CONSOLE__name}", - "${env.KOGITO_MANAGEMENT_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KOGITO_MANAGEMENT_CONSOLE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${KOGITO_MANAGEMENT_CONSOLE__name}-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KOGITO_MANAGEMENT_CONSOLE__registry}/${env.KOGITO_MANAGEMENT_CONSOLE__account}/${env.KOGITO_MANAGEMENT_CONSOLE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-serverless-operator b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-serverless-operator new file mode 100644 index 00000000000..e744f9a8802 --- /dev/null +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-serverless-operator @@ -0,0 +1,239 @@ +/* + * 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. + */ +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + } + } + + options { + timeout(time: 180, unit: 'MINUTES') + } + + parameters { + booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') + string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') + } + + environment { + KIE_TOOLS_BUILD__runLinters = 'false' + KIE_TOOLS_BUILD__runTests = 'false' + KIE_TOOLS_BUILD__runEndToEndTests = 'false' + KIE_TOOLS_BUILD__buildContainerImages = 'true' + + SONATAFLOW_OPERATOR__registry = 'docker.io' + SONATAFLOW_OPERATOR__account = 'apache' + SONATAFLOW_OPERATOR__name = 'incubator-kie-sonataflow-operator' + SONATAFLOW_OPERATOR__buildTag = "${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + + DOCKER_CONFIG = "${WORKSPACE}/.docker" + + PNPM_FILTER_STRING = '-F @kie-tools/sonataflow-operator...' + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' + } + } + } + + stage('Start required services for build and tests (DinD, Xvfb, Fluxbox)') { + steps { + script { + buildUtils.startRequiredServices() + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Build') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Upload Kogito Serverless Operator YAML file') { + when { + expression { !params.DRY_RUN } + } + steps { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + 'kie-tools/packages/sonataflow-operator/operator.yaml', + "operator_${params.RELEASE_VERSION}.yaml", + 'application/yaml', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } + } + } + + stage('Create "latest" tag for sonataflow-operator') { + steps { + script { + dockerUtils.tagImage( + "${env.SONATAFLOW_OPERATOR__registry}/${env.SONATAFLOW_OPERATOR__account}", + "${env.SONATAFLOW_OPERATOR__name}", + "${env.SONATAFLOW_OPERATOR__buildTag}", + 'latest' + ) + } + } + } + + stage('Push sonataflow-operator to Docker Hub') { + when { + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + script { + dockerUtils.pushImageToRegistry( + "${env.SONATAFLOW_OPERATOR__registry}", + "${env.SONATAFLOW_OPERATOR__account}", + "${env.SONATAFLOW_OPERATOR__name}", + "${env.SONATAFLOW_OPERATOR__buildTag} latest", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-operator-image.tar.gz" + env.RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-operator.zip" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.SONATAFLOW_OPERATOR__registry}/${env.SONATAFLOW_OPERATOR__account}/${env.SONATAFLOW_OPERATOR__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_ZIP_FILE}", 'kie-tools/packages/kogito-serverless-operator/operator.yaml') + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true) + } + } +} diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-builder b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-builder index d18b2eda3d5..6acb81c0598 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-builder +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-builder @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,14 +40,16 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - KOGITO_SWF_BUILDER_IMAGE__registry = 'quay.io' - KOGITO_SWF_BUILDER_IMAGE__account = 'kiegroup' - KOGITO_SWF_BUILDER_IMAGE__name = 'kogito-swf-builder' - KOGITO_SWF_BUILDER_IMAGE__buildTag = "latest ${params.TAG}" + SONATAFLOW_BUILDER_IMAGE__registry = 'docker.io' + SONATAFLOW_BUILDER_IMAGE__account = 'apache' + SONATAFLOW_BUILDER_IMAGE__name = 'incubator-kie-sonataflow-builder' + SONATAFLOW_BUILDER_IMAGE__buildTag = "${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" - PNPM_FILTER_STRING = '-F @kie-tools/kogito-swf-builder...' + PNPM_FILTER_STRING = '-F @kie-tools/sonataflow-builder-image...' } stages { @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,76 @@ pipeline { } } - stage('Push kogito-swf-builder to quay.io') { + stage('Create "latest" tag for sonataflow-builder') { + steps { + script { + dockerUtils.tagImage( + "${env.SONATAFLOW_BUILDER_IMAGE__registry}/${env.SONATAFLOW_BUILDER_IMAGE__account}", + "${env.SONATAFLOW_BUILDER_IMAGE__name}", + "${env.SONATAFLOW_BUILDER_IMAGE__buildTag}", + 'latest' + ) + } + } + } + + stage('Push sonataflow-builder to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_BUILDER_IMAGE__registry}/${env.KOGITO_SWF_BUILDER_IMAGE__account}", - "${env.KOGITO_SWF_BUILDER_IMAGE__name}", - "${env.KOGITO_SWF_BUILDER_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" + "${env.SONATAFLOW_BUILDER_IMAGE__registry}", + "${env.SONATAFLOW_BUILDER_IMAGE__account}", + "${env.SONATAFLOW_BUILDER_IMAGE__name}", + "${env.SONATAFLOW_BUILDER_IMAGE__buildTag} latest", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-builder-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KOGITO_SWF_BUILDER_IMAGE__registry}/${env.KOGITO_SWF_BUILDER_IMAGE__account}/${env.KOGITO_SWF_BUILDER_IMAGE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-devmode b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-devmode index b6bbc431584..1180e625fd2 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-devmode +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-swf-devmode @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,14 +40,16 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - KOGITO_SWF_DEVMODE_IMAGE__registry = 'quay.io' - KOGITO_SWF_DEVMODE_IMAGE__account = 'kiegroup' - KOGITO_SWF_DEVMODE_IMAGE__name = 'kogito-swf-devmode' - KOGITO_SWF_DEVMODE_IMAGE__buildTag = "latest ${params.TAG}" + SONATAFLOW_DEVMODE_IMAGE__registry = 'docker.io' + SONATAFLOW_DEVMODE_IMAGE__account = 'apache' + SONATAFLOW_DEVMODE_IMAGE__name = 'incubator-kie-sonataflow-devmode' + SONATAFLOW_DEVMODE_IMAGE__buildTag = "${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" - PNPM_FILTER_STRING = '-F @kie-tools/kogito-swf-devmode...' + PNPM_FILTER_STRING = '-F @kie-tools/sonataflow-devmode-image...' } stages { @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,76 @@ pipeline { } } - stage('Push kogito-swf-devmode to quay.io') { + stage('Create "latest" tag for sonataflow-devmode') { + steps { + script { + dockerUtils.tagImage( + "${env.SONATAFLOW_DEVMODE_IMAGE__registry}/${env.SONATAFLOW_DEVMODE_IMAGE__account}", + "${env.SONATAFLOW_DEVMODE_IMAGE__name}", + "${env.SONATAFLOW_DEVMODE_IMAGE__buildTag}", + 'latest' + ) + } + } + } + + stage('Push sonataflow-devmode to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_SWF_DEVMODE_IMAGE__registry}/${env.KOGITO_SWF_DEVMODE_IMAGE__account}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__name}", - "${env.KOGITO_SWF_DEVMODE_IMAGE__buildTag}", - "${pipelineVars.quayKiegroupPushCredentialsId}" + "${env.SONATAFLOW_DEVMODE_IMAGE__registry}", + "${env.SONATAFLOW_DEVMODE_IMAGE__account}", + "${env.SONATAFLOW_DEVMODE_IMAGE__name}", + "${env.SONATAFLOW_DEVMODE_IMAGE__buildTag} latest", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-devmode-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KOGITO_SWF_DEVMODE_IMAGE__registry}/${env.KOGITO_SWF_DEVMODE_IMAGE__account}/${env.KOGITO_SWF_DEVMODE_IMAGE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-task-console b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-task-console index 9e288f166a2..3dc1c6174c3 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.kogito-task-console +++ b/.ci/jenkins/release-jobs/Jenkinsfile.kogito-task-console @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - KOGITO_TASK_CONSOLE__registry = 'quay.io' - KOGITO_TASK_CONSOLE__account = 'kie-tools' - KOGITO_TASK_CONSOLE__name = 'kogito-task-console' - KOGITO_TASK_CONSOLE__buildTags = "latest ${params.TAG}" + KOGITO_TASK_CONSOLE__registry = 'docker.io' + KOGITO_TASK_CONSOLE__account = 'apache' + KOGITO_TASK_CONSOLE__name = 'incubator-kie-kogito-task-console' + KOGITO_TASK_CONSOLE__buildTag = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push kogito-task-console to quay.io') { + stage('Push kogito-task-console to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.KOGITO_TASK_CONSOLE__registry}/${env.KOGITO_TASK_CONSOLE__account}", + "${env.KOGITO_TASK_CONSOLE__registry}", + "${env.KOGITO_TASK_CONSOLE__account}", "${env.KOGITO_TASK_CONSOLE__name}", - "${env.KOGITO_TASK_CONSOLE__buildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${env.KOGITO_TASK_CONSOLE__buildTag}", + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${env.KOGITO_TASK_CONSOLE__name}-image.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.KOGITO_TASK_CONSOLE__registry}/${env.KOGITO_TASK_CONSOLE__account}/${env.KOGITO_TASK_CONSOLE__name}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.npm-packages b/.ci/jenkins/release-jobs/Jenkinsfile.npm-packages index 7c994aa54cd..7231f1a564b 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.npm-packages +++ b/.ci/jenkins/release-jobs/Jenkinsfile.npm-packages @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -37,6 +39,8 @@ pipeline { KIE_TOOLS_BUILD__runTests = 'false' KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" } stages { @@ -46,6 +50,8 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' } } } @@ -66,64 +72,126 @@ pipeline { stage('Checkout kie-tools') { steps { - script { - githubUtils.checkoutRepo( - "http://github.com/${pipelineVars.githubRepositorySlug}.git", - "${params.BASE_REF}", - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } } } } stage('Setup PNPM') { steps { - script { - buildUtils.setupPnpm() + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } } } } stage('Create PNPM filter for building') { steps { - script { - pnpmFilter = sh returnStdout: true, script:"""#!/bin/bash -el - PNPM_FILTER_STRING_FOR_BUILDING=\$(pnpm -r exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json) PKG_IS_PVT=\$(jq -r ".private" package.json); if [[ "\$PKG_IS_PVT" != "true" ]]; then echo "-F \$PKG_NAME..."; fi') - echo \$PNPM_FILTER_STRING_FOR_BUILDING - """.trim() - env.PNPM_FILTER_STRING_FOR_BUILDING = pnpmFilter.trim() + dir('kie-tools') { + script { + pnpmFilter = sh returnStdout: true, script:"""#!/bin/bash -el + PNPM_FILTER_STRING_FOR_BUILDING=\$(pnpm -r exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json) PKG_IS_PVT=\$(jq -r ".private" package.json); if [[ "\$PKG_IS_PVT" != "true" ]]; then echo "-F \$PKG_NAME..."; fi') + echo \$PNPM_FILTER_STRING_FOR_BUILDING + """.trim() + env.PNPM_FILTER_STRING_FOR_BUILDING = pnpmFilter.trim() + } } } } stage('PNPM Bootstrap') { steps { - script { - buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING_FOR_BUILDING}") + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING_FOR_BUILDING}") + } } } } stage('Build') { steps { - script { - buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING_FOR_BUILDING}") + dir('kie-tools') { + script { + buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING_FOR_BUILDING}") + } } } } stage('Publish packages to the NPM registry') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + withCredentials([string(credentialsId: "${pipelineVars.npmTokenCredentialsId}", variable: 'NPM_TOKEN')]) { + sh """#!/bin/bash -el + echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc + PNPM_FILTER_STRING_FOR_PUBLISHING=\$(pnpm -r exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json) PKG_IS_PVT=\$(jq -r ".private" package.json); if [[ "\$PKG_IS_PVT" != "true" ]]; then echo "-F \$PKG_NAME"; fi') + echo \$PNPM_FILTER_STRING_FOR_PUBLISHING + pnpm \$PNPM_FILTER_STRING_FOR_PUBLISHING exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json); NPM_PKG_INFO=\$(npm view \$PKG_NAME@${params.RELEASE_VERSION} name || echo ""); if [[ -z \$NPM_PKG_INFO ]]; then pnpm publish --no-git-checks --access public; fi' + """.trim() + } + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + script { + env.RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-tools-npm-packages.zip" + env.TMP_RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-tools-npm-packages" + + sh """#!/bin/bash -el + mkdir ${env.TMP_RELEASE_ARTIFACTS_DIR} ${env.RELEASE_ARTIFACTS_DIR} + PNPM_FILTER_STRING_FOR_PUBLISHING=\$(pnpm -r exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json) PKG_IS_PVT=\$(jq -r ".private" package.json); if [[ "\$PKG_IS_PVT" != "true" ]]; then echo "-F \$PKG_NAME"; fi') + echo \$PNPM_FILTER_STRING_FOR_PUBLISHING + pnpm \$PNPM_FILTER_STRING_FOR_PUBLISHING exec 'bash' '-c' 'pnpm pack --pack-destination ${env.TMP_RELEASE_ARTIFACTS_DIR}' + """.trim() + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_ZIP_FILE}", "${env.TMP_RELEASE_ARTIFACTS_DIR}") + } + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } } steps { - withCredentials([string(credentialsId: "${pipelineVars.npmTokenCredentialsId}", variable: 'NPM_TOKEN')]) { - sh """#!/bin/bash -el - echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc - PNPM_FILTER_STRING_FOR_PUBLISHING=\$(pnpm -r exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json) PKG_IS_PVT=\$(jq -r ".private" package.json); if [[ "\$PKG_IS_PVT" != "true" ]]; then echo "-F \$PKG_NAME"; fi') - echo \$PNPM_FILTER_STRING_FOR_PUBLISHING - pnpm \$PNPM_FILTER_STRING_FOR_PUBLISHING exec 'bash' '-c' 'PKG_NAME=\$(jq -r ".name" package.json); NPM_PKG_INFO=\$(npm view \$PKG_NAME@${params.TAG} name || echo ""); if [[ -z \$NPM_PKG_INFO ]]; then pnpm publish --no-git-checks --access public; fi' - """.trim() + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) } } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.online-editor b/.ci/jenkins/release-jobs/Jenkinsfile.online-editor index d91e521f7f8..8b8a7e01b1c 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.online-editor +++ b/.ci/jenkins/release-jobs/Jenkinsfile.online-editor @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,29 +40,25 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - ONLINE_EDITOR__buildInfo = "${params.TAG}" - ONLINE_EDITOR__extendedServicesDownloadUrlLinux = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_linux_${params.TAG}.tar.gz" - ONLINE_EDITOR__extendedServicesDownloadUrlMacOs = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_macos_${params.TAG}.dmg" - ONLINE_EDITOR__extendedServicesDownloadUrlWindows = "https://github.com/apache/incubator-kie-tools/releases/download/${params.TAG}/kie_sandbox_extended_services_windows_${params.TAG}.exe" - ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.TAG}" - ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentBaseImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentBaseImageName = 'dev-deployment-base-image' - ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.TAG}" - ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'quay.io' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'kie-tools' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'dev-deployment-dmn-form-webapp-image' - ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.TAG}" + ONLINE_EDITOR__buildInfo = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__extendedServicesDownloadUrlLinux = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_linux_${params.RELEASE_VERSION}.tar.gz" + ONLINE_EDITOR__extendedServicesDownloadUrlMacOs = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_macos_${params.RELEASE_VERSION}.dmg" + ONLINE_EDITOR__extendedServicesDownloadUrlWindows = "https://github.com/apache/incubator-kie-tools/releases/download/${params.RELEASE_VERSION}/kie_sandbox_extended_services_windows_${params.RELEASE_VERSION}.exe" + ONLINE_EDITOR__extendedServicesCompatibleVersion = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__devDeploymentBaseImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentBaseImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentBaseImageName = 'incubator-kie-sandbox-dev-deployment-base' + ONLINE_EDITOR__devDeploymentBaseImageTag = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__devDeploymentDmnFormWebappImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageName = 'incubator-kie-sandbox-dev-deployment-dmn-form-webapp' + ONLINE_EDITOR__devDeploymentDmnFormWebappImageTag = "${params.RELEASE_VERSION}" + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageRegistry = 'docker.io' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageAccount = 'apache' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageName = 'incubator-kie-sandbox-dev-deployment-kogito-quarkus-blank-app' + ONLINE_EDITOR__devDeploymentKogitoQuarkusBlankAppImageTag = "${params.RELEASE_VERSION}" ONLINE_EDITOR__gtmId = 'GTM-PQGMKNW' ONLINE_EDITOR__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud' - DEV_DEPLOYMENT_BASE_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_BASE_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_BASE_IMAGE__name = 'dev-deployment-base-image' - DEV_DEPLOYMENT_BASE_IMAGE__buildTags = "latest ${params.TAG}" - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry = 'quay.io' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account = 'kie-tools' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name = 'dev-deployment-dmn-form-webapp-image' - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags = "latest ${params.TAG}" PNPM_FILTER_STRING = '-F @kie-tools/online-editor...' } @@ -72,6 +70,8 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' } } } @@ -150,17 +150,17 @@ pipeline { stage('Deploy to GitHub Pages (kogito-online)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { dir('kogito-online') { script { - EDITORS_DIR = "editors/${params.TAG}" + EDITORS_DIR = "editors/${params.RELEASE_VERSION}" sh """#!/bin/bash -el git config user.email asf-ci-kie@jenkins.kie.apache.org git config user.name asf-ci-kie - + git checkout gh-pages echo "Reset deployment dir" @@ -174,7 +174,7 @@ pipeline { cp -RL ${WORKSPACE}/kie-tools/packages/stunner-editors/dist/bpmn ${EDITORS_DIR} cp -RL ${WORKSPACE}/kie-tools/packages/stunner-editors/dist/scesim ${EDITORS_DIR} rm -rf ./editors/latest - ln -s ${params.TAG} ./editors/latest + ln -s ${params.RELEASE_VERSION} ./editors/latest echo "Copy Online Editor resources" rm -rf ./gwt-editors @@ -184,7 +184,7 @@ pipeline { echo "Commit changes and push" git add . - git commit -m "Deploy ${params.TAG} (Editors + Online Editor)" + git commit -m "Deploy ${params.RELEASE_VERSION} (Editors + Online Editor)" """.trim() githubUtils.pushObject('origin', 'gh-pages', "${pipelineVars.asfGithubPushCredentialsId}") @@ -192,6 +192,48 @@ pipeline { } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sandbox-webapp.zip" + + sh "mkdir ${env.RELEASE_ARTIFACTS_DIR}" + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.BUSINESS_AUTOMATION_STANDALONE_RELEASE_ZIP_FILE}", 'kie-tools/packages/online-editor/dist/') + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools index 757f703b912..f19d3ea5160 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools +++ b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,7 +28,7 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') } @@ -38,25 +38,25 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - SERVERLESS_LOGIC_WEB_TOOLS__version = "${params.TAG}" - SERVERLESS_LOGIC_WEB_TOOLS__samplesRepositoryRef = "${params.TAG}" + SERVERLESS_LOGIC_WEB_TOOLS__version = "${params.RELEASE_VERSION}" + SERVERLESS_LOGIC_WEB_TOOLS__samplesRepositoryRef = "${params.RELEASE_VERSION}" SERVERLESS_LOGIC_WEB_TOOLS__buildInfo = '' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'serverless-logic-web-tools-swf-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageTag = "${params.TAG}" - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'serverless-logic-web-tools-base-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageTag = "${params.TAG}" - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'serverless-logic-web-tools-swf-dev-mode-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageTag = "${params.TAG}" - DASHBUILDER__viewerImageRegistry = 'quay.io' - DASHBUILDER__viewerImageAccount = 'kie-tools' - DASHBUILDER__viewerImageName = 'dashbuilder-viewer-image' - SERVERLESS_LOGIC_WEB_TOOLS__dashbuilderViewerImageTag = "${params.TAG}" + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'incubator-serverless-logic-web-tools-swf-builder' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageTag = "${params.RELEASE_VERSION}" + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'incubator-kie-serverless-logic-web-tools-base-builder' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageTag = "${params.RELEASE_VERSION}" + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'incubator-kie-serverless-logic-web-tools-swf-dev-mode' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageTag = "${params.RELEASE_VERSION}" + DASHBUILDER__viewerImageRegistry = 'docker.io' + DASHBUILDER__viewerImageAccount = 'apache' + DASHBUILDER__viewerImageName = 'incubator-kie-dashbuilder-viewer' + SERVERLESS_LOGIC_WEB_TOOLS__dashbuilderViewerImageTag = "${params.RELEASE_VERSION}" SERVERLESS_LOGIC_WEB_TOOLS__corsProxyUrl = 'https://cors-proxy-kie-sandbox.rhba-0ad6762cc85bcef5745bb684498c2436-0000.us-south.containers.appdomain.cloud' PNPM_FILTER_STRING = '-F @kie-tools/serverless-logic-web-tools...' diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-base-builder-image b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-base-builder-image index ac86c5b80cb..2face5f95b4 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-base-builder-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-base-builder-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'serverless-logic-web-tools-base-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags = "latest ${params.TAG}" + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName = 'incubator-kie-serverless-logic-web-tools-base-builder' + SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push serverless-logic-web-tools-base-builder-image to quay.io') { + stage('Push serverless-logic-web-tools-base-builder-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName}.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageAccount}/${env.SERVERLESS_LOGIC_WEB_TOOLS__baseBuilderImageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-builder-image b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-builder-image index a8a8ea7a627..e1e7a904973 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-builder-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-builder-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'serverless-logic-web-tools-swf-builder-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags = "latest ${params.TAG}" + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName = 'incubator-serverless-logic-web-tools-swf-builder' + SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push serverless-logic-web-tools-swf-builder-image to quay.io') { + stage('Push serverless-logic-web-tools-swf-builder-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName}.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageAccount}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfBuilderImageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-dev-mode-image b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-dev-mode-image index e5459824c44..26208d02a10 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-dev-mode-image +++ b/.ci/jenkins/release-jobs/Jenkinsfile.serverless-logic-web-tools-swf-dev-mode-image @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,10 +40,12 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'quay.io' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'kie-tools' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'serverless-logic-web-tools-swf-dev-mode-image' - SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags = "latest ${params.TAG}" + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry = 'docker.io' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount = 'apache' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName = 'incubator-kie-serverless-logic-web-tools-swf-dev-mode' + SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags = "latest ${params.RELEASE_VERSION}" + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" DOCKER_CONFIG = "${WORKSPACE}/.docker" @@ -56,6 +60,7 @@ pipeline { buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' dockerUtils = load '.ci/jenkins/shared-scripts/dockerUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -74,6 +79,16 @@ pipeline { } } + stage('Load upstream images') { + steps { + script { + upstreamReleaseArtifactsDir = "${WORKSPACE}/upstream-release-artifacts" + releaseUtils.downloadReleaseArtifacts("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}") + dockerUtils.loadImages(releaseUtils.getUpstreamImagesArtifactsList("${upstreamReleaseArtifactsDir}", "${params.RELEASE_CANDIDATE_VERSION}")) + } + } + } + stage('Checkout kie-tools') { steps { dir('kie-tools') { @@ -118,17 +133,63 @@ pipeline { } } - stage('Push serverless-logic-web-tools-swf-dev-mode-image to quay.io') { + stage('Push serverless-logic-web-tools-swf-dev-mode-image to Docker Hub') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { script { dockerUtils.pushImageToRegistry( - "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}", + "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName}", "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageBuildTags}", - "${pipelineVars.quayPushCredentialsId}" + "${pipelineVars.dockerHubUserCredentialsId}", + "${pipelineVars.dockerHubTokenCredentialsId}" + ) + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.RELEASE_IMAGE_TAR_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-${SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName}.tar.gz" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + docker save "${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageRegistry}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageAccount}/${env.SERVERLESS_LOGIC_WEB_TOOLS__swfDevModeImageName}:${params.RELEASE_VERSION}" | gzip > "${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}" + """.trim() + } + } + } + + stage('Sign artifact for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.RELEASE_IMAGE_TAR_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.sonataflow-quarkus-devui b/.ci/jenkins/release-jobs/Jenkinsfile.sonataflow-quarkus-devui new file mode 100644 index 00000000000..d39c2a8dc0c --- /dev/null +++ b/.ci/jenkins/release-jobs/Jenkinsfile.sonataflow-quarkus-devui @@ -0,0 +1,147 @@ +/* + * 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. + */ +pipeline { + agent { + docker { + image 'docker.io/apache/incubator-kie-tools-ci-build:main' + args '--shm-size=2g --privileged --group-add docker' + } + } + + options { + timeout(time: 180, unit: 'MINUTES') + } + + parameters { + booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') + string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + } + + environment { + KIE_TOOLS_BUILD__runLinters = 'false' + KIE_TOOLS_BUILD__runTests = 'false' + KIE_TOOLS_BUILD__runEndToEndTests = 'false' + KIE_TOOLS_BUILD__mavenDeploySkip = 'false' + + DOCKER_CONFIG = "${WORKSPACE}/.docker" + + PNPM_FILTER_STRING = '-F sonataflow-quarkus-devui...' + } + + stages { + stage('Load local shared scripts') { + steps { + script { + pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' + buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' + githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + } + } + } + + stage('Start required services for build and tests (DinD, Xvfb, Fluxbox)') { + steps { + script { + buildUtils.startRequiredServices() + } + } + } + + stage('Clean workspace before build') { + steps { + cleanWs(deleteDirs: true, disableDeferredWipeout: true) + } + } + + stage('Checkout kie-tools') { + steps { + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } + } + } + } + + stage('Setup PNPM') { + steps { + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } + } + } + } + + stage('PNPM Bootstrap') { + steps { + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } + } + } + } + + stage('Setup signing key') { + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Build and Deploy') { + steps { + dir('kie-tools') { + script { + if (params.DRY_RUN) { + env.KIE_TOOLS_BUILD__mavenDeploySkip = 'true' + } + withCredentials([usernamePassword(credentialsId: "${pipelineVars.mavenDeployRepositoryCredentialsId}", usernameVariable: 'REPOSITORY_USER', passwordVariable: 'REPOSITORY_TOKEN')]) { + withCredentials([string(credentialsId: "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}", variable: 'SIGNING_KEY_PASSWORD')]) { + configFileProvider([configFile(fileId: "${pipelineVars.mavenSettingsConfigFileId}", variable: 'MAVEN_SETTINGS_FILE')]) { + sh """#!/bin/bash -el + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -e "\n--settings=${MAVEN_SETTINGS_FILE}" >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.username=${REPOSITORY_USER} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dapache.repository.password=${REPOSITORY_TOKEN} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Dgpg.passphrase=${SIGNING_KEY_PASSWORD} >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} exec 'bash' '-c' 'echo -Papache-release >> .mvn/maven.config' + pnpm ${env.PNPM_FILTER_STRING} build:prod + """.trim() + } + } + } + } + } + } + } + } + + post { + always { + cleanWs(deleteDirs: true) + } + } +} diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.standalone-editors-cdn b/.ci/jenkins/release-jobs/Jenkinsfile.standalone-editors-cdn index 3f185a0deb7..4d8450efbea 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.standalone-editors-cdn +++ b/.ci/jenkins/release-jobs/Jenkinsfile.standalone-editors-cdn @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,8 +28,10 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,6 +40,8 @@ pipeline { KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + PNPM_FILTER_STRING = '-F @kie-tools/kie-editors-standalone... -F @kie-tools/serverless-workflow-standalone-editor...' } @@ -48,6 +52,9 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' + zipUtils = load '.ci/jenkins/shared-scripts/zipUtils.groovy' } } } @@ -126,17 +133,17 @@ pipeline { stage('Deploy to GitHub Pages (kogito-online)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { dir('kogito-online') { script { - STANDALONE_EDITORS_DIR = "standalone/${params.TAG}" + STANDALONE_EDITORS_DIR = "standalone/${params.RELEASE_VERSION}" sh """#!/bin/bash -el git config user.email asf-ci-kie@jenkins.kie.apache.org git config user.name asf-ci-kie - + git checkout gh-pages echo "Copy standalone resources" @@ -148,13 +155,13 @@ pipeline { rm -f ./standalone/bpmn/index.js rm -f ./standalone/dmn/index.js rm -f ./standalone/swf/index.js - ln -s ../${params.TAG}/bpmn/index.js ./standalone/bpmn/index.js - ln -s ../${params.TAG}/dmn/index.js ./standalone/dmn/index.js - ln -s ../${params.TAG}/swf/index.js ./standalone/swf/index.js + ln -s ../${params.RELEASE_VERSION}/bpmn/index.js ./standalone/bpmn/index.js + ln -s ../${params.RELEASE_VERSION}/dmn/index.js ./standalone/dmn/index.js + ln -s ../${params.RELEASE_VERSION}/swf/index.js ./standalone/swf/index.js echo "Commit changes and push" git add . - git commit -m "Deploy ${params.TAG} (Standalone Editors)" + git commit -m "Deploy ${params.RELEASE_VERSION} (Standalone Editors)" """.trim() githubUtils.pushObject('origin', 'gh-pages', "${pipelineVars.asfGithubPushCredentialsId}") @@ -162,6 +169,51 @@ pipeline { } } } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.BUSINESS_AUTOMATION_STANDALONE_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-business-automation-standalone-editors.zip" + env.SONATAFLOW_STANDALONE_RELEASE_ZIP_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-standalone-editors.zip" + + sh "mkdir ${env.RELEASE_ARTIFACTS_DIR}" + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.BUSINESS_AUTOMATION_STANDALONE_RELEASE_ZIP_FILE}", 'kie-tools/packages/kie-editors-standalone/dist') + zipUtils.zipArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.SONATAFLOW_STANDALONE_RELEASE_ZIP_FILE}", 'kie-tools/packages/serverless-workflow-standalone-editor/dist') + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.BUSINESS_AUTOMATION_STANDALONE_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.SONATAFLOW_STANDALONE_RELEASE_ZIP_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" + ) + } + } + } } post { diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-dev b/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-dev index 05667e88378..c80ed3f0756 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-dev +++ b/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-dev @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,7 +28,7 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') } @@ -69,36 +69,44 @@ pipeline { stage('Checkout kie-tools') { steps { - script { - githubUtils.checkoutRepo( - "http://github.com/${pipelineVars.githubRepositorySlug}.git", - "${params.BASE_REF}", - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } } } } stage('Setup PNPM') { steps { - script { - buildUtils.setupPnpm() + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } } } } stage('PNPM Bootstrap') { steps { - script { - buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + dir('kie-tools') { + script { + buildUtils.pnpmBootstrap("${env.PNPM_FILTER_STRING}") + } } } } stage('Build') { steps { - script { - buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING}") + dir('kie-tools') { + script { + buildUtils.pnpmBuild("${env.PNPM_FILTER_STRING}") + } } } } @@ -108,14 +116,16 @@ pipeline { expression { !params.DRY_RUN } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/kie-editors-dev-vscode-extension/dist/kie_editors_dev_vscode_extension_${params.TAG}.vsix", - "vscode_extension_dev_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/kie-editors-dev-vscode-extension/dist/kie_editors_dev_vscode_extension_${params.RELEASE_VERSION}.vsix", + "vscode_extension_dev_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } diff --git a/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-prod b/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-prod index 8d65b16274a..e76a5afacfa 100644 --- a/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-prod +++ b/.ci/jenkins/release-jobs/Jenkinsfile.vscode-extensions-prod @@ -17,7 +17,7 @@ pipeline { agent { docker { - image 'quay.io/kie-tools/kie-tools-ci-build:latest' + image 'docker.io/apache/incubator-kie-tools-ci-build:main' args '--shm-size=2g --privileged --group-add docker' } } @@ -28,9 +28,11 @@ pipeline { parameters { booleanParam(description: 'Dry run', name: 'DRY_RUN', defaultValue: true) - string(description: 'Tag', name: 'TAG', defaultValue: '0.0.0') + string(description: 'Release Version', name: 'RELEASE_VERSION', defaultValue: '0.0.0') string(description: 'Base Ref', name: 'BASE_REF') string(description: 'Upload Asset Url', name: 'UPLOAD_ASSET_URL') + booleanParam(description: 'Release Candidate', name: 'RELEASE_CANDIDATE', defaultValue: false) + string(description: 'Release Candidate Version', name: 'RELEASE_CANDIDATE_VERSION', defaultValue: '') } environment { @@ -38,6 +40,17 @@ pipeline { KIE_TOOLS_BUILD__runTests = 'false' KIE_TOOLS_BUILD__runEndToEndTests = 'false' KIE_TOOLS_BUILD__buildContainerImages = 'true' + + RELEASE_ARTIFACTS_DIR = "${WORKSPACE}/release-artifacts" + + BPMN_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-bpmn-vscode-extension.vsix" + DMN_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-dmn-vscode-extension.vsix" + PMML_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-pmml-vscode-extension.vsix" + DASHBUILDER_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-dashbuilder-vscode-extension.vsix" + SONATAFLOW_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-vscode-extension.vsix" + YARD_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-yard-vscode-extension.vsix" + KOGITO_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-kogito-bundle-vscode-extension.vsix" + BUSINESS_AUTOMATION_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-business-automation-bundle-vscode-extension.vsix" } stages { @@ -47,6 +60,7 @@ pipeline { pipelineVars = load '.ci/jenkins/shared-scripts/pipelineVars.groovy' buildUtils = load '.ci/jenkins/shared-scripts/buildUtils.groovy' githubUtils = load '.ci/jenkins/shared-scripts/githubUtils.groovy' + releaseUtils = load '.ci/jenkins/shared-scripts/releaseUtils.groovy' } } } @@ -67,167 +81,275 @@ pipeline { stage('Checkout kie-tools') { steps { - script { - githubUtils.checkoutRepo( - "http://github.com/${pipelineVars.githubRepositorySlug}.git", - "${params.BASE_REF}", - "${pipelineVars.kieToolsBotGithubCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.checkoutRepo( + "http://github.com/${pipelineVars.githubRepositorySlug}.git", + "${params.BASE_REF}", + "${pipelineVars.kieToolsBotGithubCredentialsId}" + ) + } } } } stage('Setup PNPM') { steps { - script { - buildUtils.setupPnpm() + dir('kie-tools') { + script { + buildUtils.setupPnpm() + } } } } stage('PNPM Bootstrap') { steps { - sh '''#!/bin/bash -el - pnpm bootstrap -F bpmn-vscode-extension... \ - -F dmn-vscode-extension... \ - -F pmml-vscode-extension... \ - -F vscode-extension-kogito-bundle... \ - -F swf-vscode-extension... \ - -F vscode-extension-kie-ba-bundle... \ - -F vscode-extension-dashbuilder-editor... - '''.trim() + dir('kie-tools') { + sh '''#!/bin/bash -el + pnpm bootstrap -F bpmn-vscode-extension... \ + -F dmn-vscode-extension... \ + -F pmml-vscode-extension... \ + -F vscode-extension-kogito-bundle... \ + -F swf-vscode-extension... \ + -F vscode-extension-kie-ba-bundle... \ + -F vscode-extension-dashbuilder-editor... \ + -F yard-vscode-extension... + '''.trim() + } } } stage('Build') { steps { - sh '''#!/bin/bash -el - pnpm -F bpmn-vscode-extension... \ - -F dmn-vscode-extension... \ - -F pmml-vscode-extension... \ - -F vscode-extension-kogito-bundle... \ - -F swf-vscode-extension... \ - -F vscode-extension-kie-ba-bundle... \ - -F vscode-extension-dashbuilder-editor... \ - build:prod - '''.trim() + dir('kie-tools') { + sh '''#!/bin/bash -el + pnpm -F bpmn-vscode-extension... \ + -F dmn-vscode-extension... \ + -F pmml-vscode-extension... \ + -F vscode-extension-kogito-bundle... \ + -F swf-vscode-extension... \ + -F vscode-extension-kie-ba-bundle... \ + -F vscode-extension-dashbuilder-editor... \ + -F yard-vscode-extension... \ + build:prod + '''.trim() + } } } stage('Upload VS Code Extension - BPMN Editor (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/bpmn-vscode-extension/dist/bpmn_vscode_extension_${params.TAG}.vsix", - "bpmn_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/bpmn-vscode-extension/dist/bpmn_vscode_extension_${params.RELEASE_VERSION}.vsix", + "bpmn_vscode_extension_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - DMN Editor (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/dmn-vscode-extension/dist/dmn_vscode_extension_${params.TAG}.vsix", - "dmn_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/dmn-vscode-extension/dist/dmn_vscode_extension_${params.RELEASE_VERSION}.vsix", + "dmn_vscode_extension_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - PMML Editor (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/pmml-vscode-extension/dist/pmml_vscode_extension_${params.TAG}.vsix", - "pmml_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/pmml-vscode-extension/dist/pmml_vscode_extension_${params.RELEASE_VERSION}.vsix", + "pmml_vscode_extension_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - Kogito Bundle (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/vscode-extension-kogito-bundle/dist/vscode_extension_kogito_bundle_${params.TAG}.vsix", - "vscode_extension_kogito_bundle_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/vscode-extension-kogito-bundle/dist/vscode_extension_kogito_bundle_${params.RELEASE_VERSION}.vsix", + "vscode_extension_kogito_bundle_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - KIE Business Automation Bundle (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/vscode-extension-kie-ba-bundle/dist/vscode_extension_kie_ba_bundle_${params.TAG}.vsix", - "vscode_extension_kie_ba_bundle_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/vscode-extension-kie-ba-bundle/dist/vscode_extension_kie_ba_bundle_${params.RELEASE_VERSION}.vsix", + "vscode_extension_kie_ba_bundle_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - Serverless Workflow Editor - KIE (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } } steps { - script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/serverless-workflow-vscode-extension/dist/serverless_workflow_vscode_extension_${params.TAG}.vsix", - "serverless_workflow_vscode_extension_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" - ) + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/serverless-workflow-vscode-extension/dist/serverless_workflow_vscode_extension_${params.RELEASE_VERSION}.vsix", + "serverless_workflow_vscode_extension_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } } } } stage('Upload VS Code Extension - Dashbuilder Editor (prod)') { when { - expression { !params.DRY_RUN } + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/vscode-extension-dashbuilder-editor/dist/vscode_extension_dashbuilder_editor_${params.RELEASE_VERSION}.vsix", + "vscode_extension_dashbuilder_editor_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } + } + } + } + + stage('Upload VS Code Extension - Yard (prod)') { + when { + expression { !params.DRY_RUN && !params.RELEASE_CANDIDATE } + } + steps { + dir('kie-tools') { + script { + githubUtils.uploadReleaseAsset( + "${params.UPLOAD_ASSET_URL}", + "packages/yard-vscode-extension/dist/yard_vscode_extension_${params.RELEASE_VERSION}.vsix", + "yard_vscode_extension_${params.RELEASE_VERSION}.vsix", + 'application/zip', + "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + ) + } + } + } + } + + stage('Setup release candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + env.BPMN_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-bpmn-vscode-extension.vsix" + env.DMN_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-dmn-vscode-extension.vsix" + env.PMML_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-pmml-vscode-extension.vsix" + env.DASHBUILDER_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-dashbuilder-vscode-extension.vsix" + env.SONATAFLOW_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-sonataflow-vscode-extension.vsix" + env.YARD_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-yard-vscode-extension.vsix" + env.KOGITO_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-kogito-bundle-vscode-extension.vsix" + env.BUSINESS_AUTOMATION_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE = "incubator-kie-${params.RELEASE_CANDIDATE_VERSION}-business-automation-bundle-vscode-extension.vsix" + + sh """#!/bin/bash -el + mkdir ${env.RELEASE_ARTIFACTS_DIR} + cp "kie-tools/packages/bpmn-vscode-extension/dist/bpmn_vscode_extension_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.BPMN_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/dmn-vscode-extension/dist/dmn_vscode_extension_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.DMN_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/ackages/pmml-vscode-extension/dist/pmml_vscode_extension_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.PMML_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/vscode-extension-dashbuilder-editor/dist/vscode_extension_dashbuilder_editor_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.DASHBUILDER_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/serverless-workflow-vscode-extension/dist/serverless_workflow_vscode_extension_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.SONATAFLOW_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/yard-vscode-extension/dist/yard_vscode_extension_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.YARD_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/vscode-extension-kogito-bundle/dist/vscode_extension_kogito_bundle_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.KOGITO_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE}" + cp "kie-tools/packages/vscode-extension-kie-ba-bundle/dist/vscode_extension_kie_ba_bundle_${params.RELEASE_VERSION}.vsix" "${env.RELEASE_ARTIFACTS_DIR}/${env.BUSINESS_AUTOMATION_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE}" + """.trim() + } + } + } + + stage('Sign artifacts for Apache release') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } + } + steps { + script { + releaseUtils.setupSigningKey("${pipelineVars.asfReleaseGPGKeyCredentialsId}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.BPMN_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DMN_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.PMML_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.DASHBUILDER_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.SONATAFLOW_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.YARD_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.KOGITO_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + releaseUtils.signArtifact("${env.RELEASE_ARTIFACTS_DIR}/${env.BUSINESS_AUTOMATION_BUNDLE_VSCODE_EXTENSION_RELEASE_FILE}", "${pipelineVars.asfReleaseGPGKeyPasswordCredentialsId}") + } + } + } + + stage('Publish Release Candidate artifacts') { + when { + expression { !params.DRY_RUN && params.RELEASE_CANDIDATE } } steps { script { - githubUtils.uploadReleaseAsset( - "${params.UPLOAD_ASSET_URL}", - "packages/vscode-extension-dashbuilder-editor/dist/vscode_extension_dashbuilder_editor_${params.TAG}.vsix", - "vscode_extension_dashbuilder_editor_${params.TAG}.vsix", - 'application/zip', - "${pipelineVars.kieToolsBotGithubTokenCredentialsId}" + releaseUtils.publishArtifacts( + "${env.RELEASE_ARTIFACTS_DIR}", + "${pipelineVars.asfReleaseStagingRepository}", + "${params.RELEASE_CANDIDATE_VERSION}", + "${pipelineVars.asfReleaseSVNStagingCredentialsId}" ) } } diff --git a/.ci/jenkins/shared-scripts/buildUtils.groovy b/.ci/jenkins/shared-scripts/buildUtils.groovy index a7da3b1f91f..01902ab9a29 100644 --- a/.ci/jenkins/shared-scripts/buildUtils.groovy +++ b/.ci/jenkins/shared-scripts/buildUtils.groovy @@ -84,6 +84,33 @@ def pnpmBuild(String filters, Integer workspaceConcurrency = 1) { """.trim() } +/** +* PNPM update project version to +*/ +def pnpmUpdateProjectVersion(String projectVersion) { + sh """#!/bin/bash -el + pnpm update-version-to ${projectVersion} + """.trim() +} + +/** +* PNPM update kogito version to +*/ +def pnpmUpdateKogitoVersion(String kogitoVersion, String imagesTag) { + sh """#!/bin/bash -el + pnpm update-kogito-version-to --maven ${kogitoVersion} --images-tag ${imagesTag} + """.trim() +} + +/** +* PNPM update stream name to +*/ +def pnpmUpdateStreamName(String streamName) { + sh """#!/bin/bash -el + pnpm update-stream-name-to ${streamName} + """.trim() +} + /** * Start KIE-Tools required services for build and test */ @@ -104,7 +131,7 @@ def buildDateTime() { * @return String the Apache Jenkins agent nodes with higher capacity (builds22 to builds30) **/ def apacheAgentLabels() { - return (22..30).collect{"builds$it"}.join(' || ') + return (22..30).collect { "builds$it" }.join(' || ') } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/chromeStoreUtils.groovy b/.ci/jenkins/shared-scripts/chromeStoreUtils.groovy index d1f64d50a66..209b50dacdf 100644 --- a/.ci/jenkins/shared-scripts/chromeStoreUtils.groovy +++ b/.ci/jenkins/shared-scripts/chromeStoreUtils.groovy @@ -39,7 +39,7 @@ def uploadExtension(String chromeStoreCredentialsId, String chromeStoreRefreshTo * @return String status */ def publishExtension(String chromeStoreCredentialsId, String chromeStoreRefreshTokenCredentialsId, String chromeExtensionIdCredentialsId) { - withCredentials([usernamePassword(credentialsId: "${chromeStoreCredentialsId}", usernameVariable: 'CLIENT_ID', passwordVariable: 'CLIENT_SECRET')]) { + withCredentials([usernamePassword(credentialsId: "${chromeStoreCredentialsId}", usernameVariable: 'CLIENT_ID', passwordVariable: 'CLIENT_SECRET')]) { withCredentials([string(credentialsId: "${chromeStoreRefreshTokenCredentialsId}", variable: 'REFRESH_TOKEN')]) { withCredentials([string(credentialsId: "${chromeExtensionIdCredentialsId}", variable: 'EXTENSION_ID')]) { script { @@ -53,4 +53,4 @@ def publishExtension(String chromeStoreCredentialsId, String chromeStoreRefreshT } } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/dockerUtils.groovy b/.ci/jenkins/shared-scripts/dockerUtils.groovy index 2e97c4da0b0..9631777b42e 100644 --- a/.ci/jenkins/shared-scripts/dockerUtils.groovy +++ b/.ci/jenkins/shared-scripts/dockerUtils.groovy @@ -18,29 +18,60 @@ /** * Push an image to a given registry */ -def pushImageToRegistry(String registry, String image, String tags, String credentialsId) { - withCredentials([usernamePassword(credentialsId: credentialsId, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD')]) { - sh "set +x && docker login -u $REGISTRY_USER -p $REGISTRY_PWD $registry" - tagList = tags.split(' ') - for (tag in tagList) { - sh "docker push $registry/$image:$tag" +def pushImageToRegistry(String registry, String account, String image, String tags, String userCredentialsId, String tokenCredentialsId) { + withCredentials([string(credentialsId: userCredentialsId, variable: 'DOCKER_USER')]) { + withCredentials([string(credentialsId: tokenCredentialsId, variable: 'DOCKER_TOKEN')]) { + sh """ + echo "${DOCKER_TOKEN}" | docker login -u "${DOCKER_USER}" --password-stdin $registry + """.trim() + tagList = tags.split(' ') + for (tag in tagList) { + sh "docker push $registry/$account/$image:$tag" + } + sh 'docker logout' } - sh 'docker logout' } } /** * @return bool image exists in a given registry */ -def checkImageExistsInRegistry(String registry, String image, String tag, String credentialsId) { - withCredentials([usernamePassword(credentialsId: credentialsId, usernameVariable: 'REGISTRY_USER', passwordVariable: 'REGISTRY_PWD')]) { - sh "set +x && docker login -u $REGISTRY_USER -p $REGISTRY_PWD $registry" - result = sh returnStatus: true, script: """ - docker manifest inspect $registry/$image:$tag > /dev/null 2>&1 - """.trim() - sh 'docker logout' - return result == 0 +def checkImageExistsInRegistry(String registry, String account, String image, String tag, String userCredentialsId, String tokenCredentialsId) { + withCredentials([string(credentialsId: userCredentialsId, variable: 'DOCKER_USER')]) { + withCredentials([string(credentialsId: tokenCredentialsId, variable: 'DOCKER_TOKEN')]) { + sh """ + echo "${DOCKER_TOKEN}" | docker login -u "${DOCKER_USER}" --password-stdin $registry + """.trim() + result = sh returnStatus: true, script: """ + docker manifest inspect $registry/$account/$image:$tag > /dev/null 2>&1 + """.trim() + sh 'docker logout' + return result == 0 + } + } +} + +/** +* Tag an image +*/ +def tagImage(String registry, String image, String oldTag, String newTag) { + sh "docker tag ${registry}/${image}:${oldTag} ${registry}/${image}:${newTag}" +} + +/** +* Load an image +*/ +def loadImage(String imageFile) { + sh "docker load < ${imageFile}" +} + +/** +* Load multiple images +*/ +def loadImages(String... imagesFiles) { + for (imageFile in imagesFiles) { + loadImage(imageFile) } } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/githubUtils.groovy b/.ci/jenkins/shared-scripts/githubUtils.groovy index 31f4a76bb1c..bbf28eade07 100644 --- a/.ci/jenkins/shared-scripts/githubUtils.groovy +++ b/.ci/jenkins/shared-scripts/githubUtils.groovy @@ -114,6 +114,17 @@ def squashedMerge(String author, String branch, String url) { """.trim() } +/** +* Create a new tag +*/ +def createTag(String tagName) { + sh """#!/bin/bash -el + git config user.email asf-ci-kie@jenkins.kie.apache.org + git config user.name asf-ci-kie + git tag "${tagName}" + """.trim() +} + /** * Checkout a github repository and perform a squashed merge on a local repository */ @@ -141,8 +152,8 @@ def pushObject(String remote, String object, String credentialsId) { */ def getRepoSlug(String url) { tokens = url.tokenize('/') - org = tokens[tokens.size()-4] - repo = tokens[tokens.size()-3] + org = tokens[tokens.size() - 4] + repo = tokens[tokens.size() - 3] return "${org}/${repo}" } @@ -167,4 +178,4 @@ def fileIsInChangeset(String file) { return changeset.contains(file) } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/helmUtils.groovy b/.ci/jenkins/shared-scripts/helmUtils.groovy index 7c45e38505a..3081c0c7768 100644 --- a/.ci/jenkins/shared-scripts/helmUtils.groovy +++ b/.ci/jenkins/shared-scripts/helmUtils.groovy @@ -26,4 +26,4 @@ def pushChartToRegistry(String registry, String chart, String credentialsId) { } } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/openShiftUtils.groovy b/.ci/jenkins/shared-scripts/openShiftUtils.groovy index 729c4d7671a..2707a40dd30 100644 --- a/.ci/jenkins/shared-scripts/openShiftUtils.groovy +++ b/.ci/jenkins/shared-scripts/openShiftUtils.groovy @@ -74,4 +74,4 @@ def getAppRoute(String project, String appName, String credentialsId) { } } -return this; +return this diff --git a/.ci/jenkins/shared-scripts/pipelineVars.groovy b/.ci/jenkins/shared-scripts/pipelineVars.groovy index e3ed412710a..eeaa6096e01 100644 --- a/.ci/jenkins/shared-scripts/pipelineVars.groovy +++ b/.ci/jenkins/shared-scripts/pipelineVars.groovy @@ -16,27 +16,33 @@ */ class PipelineVars implements Serializable { - String githubRepositoryOrg = 'apache'; - String githubRepositoryName = 'incubator-kie-tools'; - String githubRepositorySlug = 'apache/incubator-kie-tools'; - - String quayPushCredentialsId = 'quay-io-kie-tools-token'; - String quayKiegroupPushCredentialsId = 'quay_kiegroup_registry_token'; - String openshiftCredentialsId = 'openshift-kie-tools-token'; - String kieToolsBotGithubCredentialsId = 'kie-tools-bot-gh'; - String kieToolsBotGithubTokenCredentialsId = 'kie-tools-bot-gh-token'; - String kieToolsGithubCodeQLTokenCredentialsId = 'kie-tools-gh-codeql-token'; - String chromeStoreCredentialsId = 'kie-tools-chrome-store'; - String chromeStoreRefreshTokenCredentialsId = 'kie-tools-chrome-store-refresh-token'; - String chromeExtensionIdCredentialsId = 'kie-tools-chrome-extension-id'; - String swfChromeExtensionIdCredentialsId = 'kie-tools-swf-chrome-extension-id'; - String npmTokenCredentialsId = 'kie-tools-npm-token'; - String buildKiteTokenCredentialsId = 'kie-tools-build-kite-token'; - String asfGithubPushCredentialsId = '84811880-2025-45b6-a44c-2f33bef30ad2'; - String asfGithubTokenPushCredentialsId = '41128c14-bb63-4708-9074-d20a318ee630'; - - String defaultArtifactsTempDir = 'artifacts-tmp'; + String githubRepositoryOrg = 'apache' + String githubRepositoryName = 'incubator-kie-tools' + String githubRepositorySlug = 'apache/incubator-kie-tools' + String quayPushCredentialsId = 'quay-io-kie-tools-token' + String dockerHubUserCredentialsId = 'DOCKERHUB_USER' + String dockerHubTokenCredentialsId = 'DOCKERHUB_TOKEN' + String openshiftCredentialsId = 'openshift-kie-tools-token' + String kieToolsBotGithubCredentialsId = 'kie-tools-bot-gh' + String kieToolsBotGithubTokenCredentialsId = 'kie-tools-bot-gh-token' + String kieToolsGithubCodeQLTokenCredentialsId = 'kie-tools-gh-codeql-token' + String chromeStoreCredentialsId = 'kie-tools-chrome-store' + String chromeStoreRefreshTokenCredentialsId = 'kie-tools-chrome-store-refresh-token' + String chromeExtensionIdCredentialsId = 'kie-tools-chrome-extension-id' + String swfChromeExtensionIdCredentialsId = 'kie-tools-swf-chrome-extension-id' + String npmTokenCredentialsId = 'kie-tools-npm-token' + String buildKiteTokenCredentialsId = 'kie-tools-build-kite-token' + String asfCIGithubCredentialsId = '399061d0-5ab5-4142-a186-a52081fef742' + String asfGithubPushCredentialsId = '84811880-2025-45b6-a44c-2f33bef30ad2' + String asfGithubTokenPushCredentialsId = '41128c14-bb63-4708-9074-d20a318ee630' + String mavenSettingsConfigFileId = 'kie-release-settings' + String mavenDeployRepositoryCredentialsId = 'apache-nexus-kie-deploy-credentials' + String defaultArtifactsTempDir = 'artifacts-tmp' + String asfReleaseStagingRepository = 'https://dist.apache.org/repos/dist/dev/incubator/kie' + String asfReleaseGPGKeyCredentialsId = 'asf-release-gpg-signing-key' + String asfReleaseGPGKeyPasswordCredentialsId = 'asf-release-gpg-signing-key-passphrase' + String asfReleaseSVNStagingCredentialsId = 'asf-release-svn-staging' } -return new PipelineVars(); +return new PipelineVars() diff --git a/.ci/jenkins/shared-scripts/releaseUtils.groovy b/.ci/jenkins/shared-scripts/releaseUtils.groovy new file mode 100644 index 00000000000..2d4b018317c --- /dev/null +++ b/.ci/jenkins/shared-scripts/releaseUtils.groovy @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** +* Setup the GPG Key to sign release artifacts +*/ +def setupSigningKey(String gpgKeyCredentialsId, String gpgKeyPasswordCredentialsId) { + withCredentials([file(credentialsId: gpgKeyCredentialsId, variable: 'SIGNING_KEY')]) { + withCredentials([string(credentialsId: gpgKeyPasswordCredentialsId, variable: 'SIGNING_KEY_PASSWORD')]) { + sh """#!/bin/bash -el + cat ${SIGNING_KEY} > ${WORKSPACE}/signkey.gpg + gpg --list-keys + gpg --batch --pinentry-mode loopback --passphrase "${SIGNING_KEY_PASSWORD}" --import ${WORKSPACE}/signkey.gpg + rm ${WORKSPACE}/signkey.gpg + """.trim() + } + } +} + +/** +* Sign an artifact using GPG +*/ +def signArtifact(String artifactFileName, String gpgKeyPasswordCredentialsId) { + withCredentials([string(credentialsId: gpgKeyPasswordCredentialsId, variable: 'SIGNING_KEY_PASSWORD')]) { + sh """#!/bin/bash -el + echo ${SIGNING_KEY_PASSWORD} | gpg --no-tty --batch --sign --pinentry-mode loopback --passphrase-fd 0 --output ${artifactFileName}.asc --detach-sig ${artifactFileName} + shasum -a 512 ${artifactFileName} > ${artifactFileName}.sha512 + """.trim() + } +} + +/** +* Publish release artifacts to a SVN repository +*/ +def publishArtifacts(String artifactsDir, String releaseRepository, String releaseVersion, String credentialsId) { + withCredentials([usernamePassword(credentialsId: credentialsId, usernameVariable: 'ASF_USERNAME', passwordVariable: 'ASF_PASSWORD')]) { + sh """#!/bin/bash -el + svn co --depth=empty ${releaseRepository} svn-kie + cp ${artifactsDir}/* svn-kie/${releaseVersion}/ + svn add "svn-kie/${releaseVersion}" + cd svn-kie + svn ci --non-interactive --no-auth-cache --username ${ASF_USERNAME} --password '${ASF_PASSWORD}' -m "Apache KIE ${releaseVersion} artifacts" + rm -rf svn-kie + """.trim() + } +} + +/** +* Download release artifacts from a specific release +*/ +def downloadReleaseArtifacts(String artifactsDir, String releaseVersion) { + sh """#!/bin/bash -el + svn co "${releaseRepository}/${releaseVersion}" "${artifactsDir}/${releaseVersion}" + """.trim() +} + +/** +* Return a list of upstream images artifacts +*/ +def getUpstreamImagesArtifactsList(String artifactsDir, String releaseVersion) { + return [ + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-base-builder-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-data-index-ephemeral-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-data-index-postgresql-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-jit-runner-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-jobs-service-allinone-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-jobs-service-ephemeral-image.tar.gz", + "${artifactsDir}/incubator-kie-${releaseVersion}-kogito-jobs-service-postgresql-image.tar.gz" + ] +} diff --git a/.ci/jenkins/shared-scripts/zipUtils.groovy b/.ci/jenkins/shared-scripts/zipUtils.groovy index 1521ce5312e..9b1be26d90c 100644 --- a/.ci/jenkins/shared-scripts/zipUtils.groovy +++ b/.ci/jenkins/shared-scripts/zipUtils.groovy @@ -34,4 +34,4 @@ def unzipArtifact(String filePath, String targetDir) { """.trim() } -return this; +return this diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index cd1be787b9a..9f6b7f73a89 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -24,4 +24,4 @@ packages/stunner-editors/lienzo-tests/ @romartin packages/stunner-editors/lienzo-webapp/ @romartin -* @ederign @tiagobento @paulovmr +* @tiagobento diff --git a/.github/actions/.gitignore b/.github/actions/.gitignore index 50d10e91fe5..b0c755f1190 100644 --- a/.github/actions/.gitignore +++ b/.github/actions/.gitignore @@ -1 +1,19 @@ +### +# 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. + !**/node_modules \ No newline at end of file diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index c6aea0f6f07..d794ae9b323 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -1,3 +1,22 @@ +# +# 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: "Bootstrap Apache KIE Tools" description: "" diff --git a/.github/actions/checkout-pr/action.yml b/.github/actions/checkout-pr/action.yml index 4b1b8d1be0a..f420441b27f 100644 --- a/.github/actions/checkout-pr/action.yml +++ b/.github/actions/checkout-pr/action.yml @@ -1,3 +1,22 @@ +# +# 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: "Checkout PR" description: "" diff --git a/.github/actions/deploy-openshift/action.yml b/.github/actions/deploy-openshift/action.yml index a66eaf7650e..ecb377c23f5 100644 --- a/.github/actions/deploy-openshift/action.yml +++ b/.github/actions/deploy-openshift/action.yml @@ -1,3 +1,22 @@ +# +# 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: "Deploy to OpenShift" description: "" diff --git a/.github/actions/setup-ci-patterns/action.yml b/.github/actions/setup-ci-patterns/action.yml index 6261751e347..f69d1254ace 100644 --- a/.github/actions/setup-ci-patterns/action.yml +++ b/.github/actions/setup-ci-patterns/action.yml @@ -1,3 +1,22 @@ +# +# 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: "Setup CI patterns" description: "" @@ -54,9 +73,9 @@ runs: const pnpmWorkspacePackagesRootPaths = ["packages", "examples"].map(p => path.join(cwd, p)).join("\n"); - const nonSourceFilesPatterns = fs.readFileSync(path.join(cwd, "./.github/supporting-files/ci/non-source-files-patterns.txt"), "utf-8"); + const nonSourceFilesPatterns = fs.readFileSync(path.join(cwd, "./.github/supporting-files/ci/patterns/non-source-files-patterns.txt"), "utf-8"); const nonSourceFilesPatternsForGitDiff = nonSourceFilesPatterns.split("\n").filter(p => p.trim() !== "").map(p => `':!${p}'`).join(" "); - const testSourceFilesPatterns = fs.readFileSync(path.join(cwd, "./.github/supporting-files/ci/tests-source-files-patterns.txt"), "utf-8"); + const testSourceFilesPatterns = fs.readFileSync(path.join(cwd, "./.github/supporting-files/ci/patterns/tests-source-files-patterns.txt"), "utf-8"); core.setOutput("pnpm_workspace_packages_root_paths", pnpmWorkspacePackagesRootPaths); core.setOutput("non_source_files_patterns", nonSourceFilesPatterns); @@ -78,14 +97,14 @@ runs: core.setOutput(outputName, patterns); } - await outputPatternsPrefixedWithRoots("tests_reports_patterns", path.join(cwd, "./.github/supporting-files/ci/tests-reports-patterns.txt")); - await outputPatternsPrefixedWithRoots("end_to_end_tests_reports_patterns", path.join(cwd, "./.github/supporting-files/ci/end-to-end-tests-reports-patterns.txt")); - await outputPatternsPrefixedWithRoots("end_to_end_tests_artifacts_patterns", path.join(cwd, "./.github/supporting-files/ci/end-to-end-tests-artifacts-patterns.txt")); - await outputPatternsPrefixedWithRoots("build_artifacts_patterns", path.join(cwd, "./.github/supporting-files/ci/build-artifacts-patterns.txt")); + await outputPatternsPrefixedWithRoots("tests_reports_patterns", path.join(cwd, "./.github/supporting-files/ci/patterns/tests-reports-patterns.txt")); + await outputPatternsPrefixedWithRoots("end_to_end_tests_reports_patterns", path.join(cwd, "./.github/supporting-files/ci/patterns/end-to-end-tests-reports-patterns.txt")); + await outputPatternsPrefixedWithRoots("end_to_end_tests_artifacts_patterns", path.join(cwd, "./.github/supporting-files/ci/patterns/end-to-end-tests-artifacts-patterns.txt")); + await outputPatternsPrefixedWithRoots("build_artifacts_patterns", path.join(cwd, "./.github/supporting-files/ci/patterns/build-artifacts-patterns.txt")); core.setOutput( "end_to_end_tests_reports_patterns_for_find", - prefixWithRoots(fs.readFileSync("./.github/supporting-files/ci/end-to-end-tests-reports-patterns.txt", "utf-8")) + prefixWithRoots(fs.readFileSync("./.github/supporting-files/ci/patterns/end-to-end-tests-reports-patterns.txt", "utf-8")) .split("\n") .map(p => `-path '${p}'`) .join(" -o ") diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml index 4a945c04f2f..394ffd0cb06 100644 --- a/.github/actions/setup-env/action.yml +++ b/.github/actions/setup-env/action.yml @@ -1,3 +1,22 @@ +# +# 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: "Setup Apache KIE Tools build environment" description: "" @@ -43,28 +62,28 @@ runs: mkdir tmp - name: "Setup pnpm" - uses: pnpm/action-setup@v2 + uses: pnpm/action-setup@v3 with: - version: 8.7.0 + version: 9.3.0 - name: "Setup Node" - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: 18.14.0 + node-version: 20.14.0 - name: "Setup JDK 17" - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: "zulu" - - name: "Set up GOLANG 1.21.5" - uses: actions/setup-go@v3 + - name: "Set up GOLANG 1.21.9" + uses: actions/setup-go@v5 with: - go-version: "1.21.5" + go-version: "1.21.9" - name: "Set up Maven" - uses: stCarolas/setup-maven@v4.5 + uses: stCarolas/setup-maven@v5 with: maven-version: 3.9.6 @@ -135,7 +154,6 @@ runs: cd ${{ inputs.working_dir }} pnpm -r exec 'bash' '-c' 'mkdir .mvn' pnpm -r exec 'bash' '-c' 'echo -B > .mvn/maven.config' - pnpm -r exec 'bash' '-c' 'echo -ntp >> .mvn/maven.config' pnpm -r exec 'bash' '-c' 'echo -Xmx2g > .mvn/jvm.config' - name: "Install s2i pre-reqs (Ubuntu only)" diff --git a/.github/actions/upload-ci-reports-and-artifacts/action.yml b/.github/actions/upload-ci-reports-and-artifacts/action.yml index 359d842968b..90dbbeb42a3 100644 --- a/.github/actions/upload-ci-reports-and-artifacts/action.yml +++ b/.github/actions/upload-ci-reports-and-artifacts/action.yml @@ -1,11 +1,29 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + name: "Upload CI reports and artifacts" description: "" inputs: - working_dir: - description: "Dir path of kie-tools" - required: false - default: "." + partition_index: + description: "Index of the partition whose artifacts are being uploaded." + required: true runs: using: "composite" @@ -44,7 +62,7 @@ runs: uses: actions/upload-artifact@v3 if: always() with: - name: ${{ runner.os }}__tests-reports + name: ${{ runner.os }}_${{ inputs.partition_index }}__tests-reports path: | ${{ runner.temp }}/tests-reports.zip @@ -52,7 +70,7 @@ runs: uses: actions/upload-artifact@v3 if: always() with: - name: ${{ runner.os }}__end-to-end-tests-reports + name: ${{ runner.os }}_${{ inputs.partition_index }}__end-to-end-tests-reports path: | ${{ runner.temp }}/end-to-end-tests-reports.zip @@ -60,7 +78,7 @@ runs: uses: actions/upload-artifact@v3 if: always() with: - name: ${{ runner.os }}__end-to-end-tests-artifacts + name: ${{ runner.os }}_${{ inputs.partition_index }}__end-to-end-tests-artifacts path: | ${{ runner.temp }}/end-to-end-tests-artifacts.zip @@ -68,6 +86,6 @@ runs: uses: actions/upload-artifact@v3 if: always() with: - name: ${{ runner.os }}__build-artifacts + name: ${{ runner.os }}_${{ inputs.partition_index }}__build-artifacts path: | ${{ runner.temp }}/build-artifacts.zip diff --git a/.github/supporting-files/ci/build-partitioning/README.md b/.github/supporting-files/ci/build-partitioning/README.md new file mode 100644 index 00000000000..fdae2ba2607 --- /dev/null +++ b/.github/supporting-files/ci/build-partitioning/README.md @@ -0,0 +1,48 @@ + + +# Build partitioning + +This script uses `bun` as runtime and has the purpose of splitting the monorepo build in N partitions that, when built, will ensure the entire monorepo has been covered. + +Each partition is completely independent from each other, and partition builds will be assigned a mode: + +- `none`: No source code changes or none of the changes affect this partition. +- `full`: All packages in the partition need to be built and tested. +- `partial`: Only selected packages of the partition were affected by the changes, so only this subset needs to be rebuilt and tested. + +### Usage + +At the root of the monorepo, you can run + +```bash +npx bun .github/supporting-files/ci/build-partitioning/build_partitioning.ts \ + --outputPath='/tmp/partitions.json' \ + --forceFull="false" \ + --baseSha="$(git rev-parse HEAD~1)" \ + --headSha="$(git rev-parse HEAD~0)" \ + --graphJsonPath='./repo/graph.json' \ + --partition='.github/supporting-files/ci/partitions/partition0.txt' \ + --partition='.github/supporting-files/ci/partitions/partition1.txt' +``` + +- `--outputPath`: Where the resulting JSON should be written to. +- `--forceFull`: Wheter or not to force `full` mode +- `--baseSha`: Base Git commit SHA. +- `--headSha`: HEAD Git commit SHA. +- `--graphJsonPath`: Where to find the `graph.json` of the monorepo in Datavis graph format. +- `--partition`: Files containing new-line-separated leaf package names that define a partition. Accepts multiple values. diff --git a/.github/supporting-files/ci/build-partitioning/assertions.ts b/.github/supporting-files/ci/build-partitioning/assertions.ts new file mode 100644 index 00000000000..4d21bb5cedb --- /dev/null +++ b/.github/supporting-files/ci/build-partitioning/assertions.ts @@ -0,0 +1,119 @@ +/* + * 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. + */ + +import { PartitionDefinition } from "./types"; + +export async function assertLeafPackagesInPartitionsExist({ + packageNames, + allLeafPackages, +}: { + packageNames: string[]; + allLeafPackages: Set; +}) { + const nonLeafPackagesInPartitions = new Set(packageNames.filter((l) => !allLeafPackages.has(l))); + console.log( + `[build-partitioning] Partition definitions only use leaf packages (${ + nonLeafPackagesInPartitions.size > 0 ? "❌" : "✅" + }):` + ); + if (nonLeafPackagesInPartitions.size > 0) { + console.error(`[build-partitioning] Non-leaf packages found in partition definitions. Aborting.`); + console.error(nonLeafPackagesInPartitions); + process.exit(1); + } +} + +export async function assertLeafPackagesInPartitionDefinitionsDontOverlap({ + allLeafPackages, + partitionDefinitions, +}: { + allLeafPackages: Set; + partitionDefinitions: PartitionDefinition[]; +}) { + const leafPackagesOverlap = partitionDefinitions.flatMap((s) => [...s.leafPackageNames]); + const hasPartitionOverlap = allLeafPackages.size !== leafPackagesOverlap.length; + + console.log(`[build-partitioning] Partition definitions don't overlap (${hasPartitionOverlap ? "❌" : "✅"}):`); + if (hasPartitionOverlap) { + console.error(`[build-partitioning] Partitions definitions declare overlapping leaf packages. Aborting.`); + console.error(leafPackagesOverlap); + process.exit(1); + } +} + +export async function assertCompleteness({ + packageDirsByName, + partitions, + allPackageDirs, +}: { + packageDirsByName: Map; + partitions: PartitionDefinition[]; + allPackageDirs: Set; +}) { + const partitionsByPkgDir = new Map(); + for (const p of partitions) { + for (const pkgDir of p.dirs) { + partitionsByPkgDir.set(pkgDir, [...(partitionsByPkgDir.get(pkgDir) ?? []), p.name]); + } + } + + const redundancyOnPartitionCombinations = new Map(); + partitionsByPkgDir.forEach((partitions, pkgDir) => { + if (partitions.length > 1) { + // We're only interested in packages that are going to be built as part of more than one partition. + const key = partitions.sort().join(" & "); + redundancyOnPartitionCombinations.set(key, [...(redundancyOnPartitionCombinations.get(key) ?? []), pkgDir]); + } + }); + + console.log(`[build-partitioning] Packages that will be built by more than one partition:`); + console.log(redundancyOnPartitionCombinations); + + const completenessCheck = [...allPackageDirs].filter((pkgDir) => !partitionsByPkgDir.has(pkgDir)).length <= 0; + console.log(`[build-partitioning] Partition definitions completeness (${!completenessCheck ? "❌" : "✅"}):`); + if (!completenessCheck) { + console.error(`[build-partitioning] All packages count: ${allPackageDirs.size}`); + for (const p of partitions) { + console.error(`[build-partitioning] ${p.name} packages count: ${p.dirs.size}`); + } + process.exit(1); + } + return allPackageDirs; +} + +export async function assertOptimalPartialBuild(args: { + partition: PartitionDefinition; + upstreamPackageNamesInPartition: Set; + affectedPackageNamesInPartition: Set; + relevantPackageNamesInPartition: Set; +}) { + const isOptimalPartialPartitionBuild = + args.upstreamPackageNamesInPartition.size + args.affectedPackageNamesInPartition.size === + args.relevantPackageNamesInPartition.size; + + console.log( + `[build-partitioning] 'Partial' build of '${args.partition.name}': Optimal build check ((${ + !isOptimalPartialPartitionBuild ? "❌" : "✅" + }))` + ); + if (!isOptimalPartialPartitionBuild) { + console.error(`[build-partitioning] Non-optimal 'Partial' build. Aborting.`); + process.exit(1); + } +} diff --git a/.github/supporting-files/ci/build-partitioning/build_partitioning.ts b/.github/supporting-files/ci/build-partitioning/build_partitioning.ts new file mode 100644 index 00000000000..ccb9c42e811 --- /dev/null +++ b/.github/supporting-files/ci/build-partitioning/build_partitioning.ts @@ -0,0 +1,247 @@ +#!/usr/bin/env bun + +/* + * 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. + */ + +import { Partial, None, Full, PartitionDefinition } from "./types"; +import { __ROOT_PKG_NAME, __NON_SOURCE_FILES_PATTERNS, __PACKAGES_ROOT_DIRS, stdoutArray } from "./globals"; +import { + assertLeafPackagesInPartitionDefinitionsDontOverlap, + assertLeafPackagesInPartitionsExist, + assertCompleteness, + assertOptimalPartialBuild, +} from "./assertions"; + +import * as path from "path"; +import * as fs from "fs"; +import { execSync } from "child_process"; +import { parseArgs } from "util"; + +const { + values: { + outputPath: __ARG_outputPath, + forceFull: forceFull, + baseSha: __ARG_baseSha, + headSha: __ARG_headSha, + graphJsonPath: __ARG_graphJsonPath, + partition: __ARG_partitionFilePaths, + }, +} = parseArgs({ + args: Bun.argv, + options: { + graphJsonPath: { type: "string" }, + baseSha: { type: "string" }, + headSha: { type: "string" }, + forceFull: { type: "string" }, + outputPath: { type: "string" }, + partition: { type: "string", multiple: true }, + }, + strict: true, + allowPositionals: true, +}); + +const cwd = execSync("bash -c pwd").toString().trim(); + +const __ARG_forceFull = forceFull === "true"; + +const partitions = await getPartitions(); +const partitionsJson = JSON.stringify(partitions, null, 2); +console.log(``); +console.log(`[build-partitioning] --- PARTITIONS JSON ---`); +console.log(partitionsJson); + +const resolvedOutputPath = path.resolve(".", __ARG_outputPath!); +fs.writeFileSync(resolvedOutputPath, partitionsJson); +console.log(`[build-partitioning] --> Written to '${resolvedOutputPath}'`); +console.log(`[build-partitioning] Done.`); + +process.exit(0); + +// + +async function getPartitions(): Promise> { + console.log(``); + console.log(`[build-partitioning] --- Summary ---`); + console.log(`[build-partitioning] graphJsonPath: ${__ARG_graphJsonPath}`); + console.log(`[build-partitioning] baseSha: ${__ARG_baseSha}`); + console.log(`[build-partitioning] headSha: ${__ARG_headSha}`); + console.log(`[build-partitioning] forceFull: ${forceFull}`); + console.log(`[build-partitioning] outputPath: ${__ARG_outputPath}`); + console.log(`[build-partitioning] ---------------`); + console.log(``); + + const graphJson = await import(path.resolve(".", __ARG_graphJsonPath!)); + const packageDirsByName = new Map(graphJson.serializedPackagesLocationByName); + const packageNamesByDir = new Map([...packageDirsByName.entries()].map(([k, v]) => [v, k])); + + const allLeafPackages = new Set(packageDirsByName.keys()); + for (const link of graphJson.serializedDatavisGraph.links) { + allLeafPackages.delete(link.target); + } + allLeafPackages.delete(__ROOT_PKG_NAME); + + console.log(`[build-partitioning] All leaf packages:`); + console.log(allLeafPackages); + + let leafPackagesDefinedByPartitions = new Set(); + const partitionDefinitions: PartitionDefinition[] = await Promise.all( + (__ARG_partitionFilePaths ?? []).map(async (partitionFile) => { + const leafPackageNames = new Set( + fs + .readFileSync(path.resolve(".", partitionFile), "utf-8") + .trim() + .split("\n") + .map((pkgName) => pkgName.trim()) + ); + + leafPackagesDefinedByPartitions = new Set([...leafPackagesDefinedByPartitions, ...leafPackageNames]); + + return { + name: partitionFile, + leafPackageNames: leafPackageNames, + dirs: await getDirsOfDependencies(leafPackageNames), + }; + }) + ); + + // Adds the remaining packages as a last partition + const remainingLeafPackages = new Set( + [...allLeafPackages].filter((leaf) => !leafPackagesDefinedByPartitions.has(leaf)) + ); + partitionDefinitions.push({ + name: "Partition N (remaining leaf packages)", + leafPackageNames: remainingLeafPackages, + dirs: await getDirsOfDependencies(remainingLeafPackages), + }); + + for (const p of partitionDefinitions) { + console.log(`[build-partitioning] ${p.name}:`); + console.log(p.leafPackageNames); + } + + await assertLeafPackagesInPartitionDefinitionsDontOverlap({ allLeafPackages, partitionDefinitions }); + await assertLeafPackagesInPartitionsExist({ + packageNames: partitionDefinitions.flatMap((p) => [...p.leafPackageNames]), + allLeafPackages, + }); + + const allPackageDirs = new Set( + stdoutArray(await execSync(`bash -c "pnpm -F !${__ROOT_PKG_NAME}... exec bash -c pwd"`).toString()) + .map((pkgDir) => path.relative(cwd, pkgDir)) + .map((pkgDir) => pkgDir.split(path.sep).join(path.posix.sep)) + ); + + await assertCompleteness({ packageDirsByName, partitions: partitionDefinitions, allPackageDirs }); + + const nonSourceFilesPatternsForGitDiff = __NON_SOURCE_FILES_PATTERNS.map((p) => `':!${p}'`).join(" "); + const changedSourcePaths = stdoutArray( + execSync( + `bash -c "git diff --name-only ${__ARG_baseSha} ${__ARG_headSha} -- ${nonSourceFilesPatternsForGitDiff}"` + ).toString() + ); + console.log("[build-partitioning] Changed source paths:"); + console.log(new Set(changedSourcePaths)); + + const changedSourcePathsInRoot = changedSourcePaths.filter((path) => + __PACKAGES_ROOT_DIRS.every((pkgDir) => !path.startsWith(`${pkgDir}/`)) + ); + + const affectedPackageDirsInAllPartitions = stdoutArray( + await execSync(`pnpm -F ...[${__ARG_baseSha}] exec bash -c pwd`).toString() + ) + .map((pkgDir) => path.relative(cwd, pkgDir)) + .map((pkgDir) => pkgDir.split(path.sep).join(path.posix.sep)); + + return await Promise.all( + partitionDefinitions.map(async (partition) => { + if (__ARG_forceFull || changedSourcePathsInRoot.length > 0) { + console.log(`[build-partitioning] 'Full' build of '${partition.name}'.`); + console.log( + `[build-partitioning] Building ${partition.dirs.size}/${partition.dirs.size}/${allPackageDirs.size} packages.` + ); + return { + mode: "full", + name: partition.name, + bootstrapPnpmFilterString: [...partition.leafPackageNames].map((l) => `-F '${l}...'`).join(" "), + fullBuildPnpmFilterString: [...partition.leafPackageNames].map((l) => `-F '${l}...'`).join(" "), + }; + } + + const changedSourcePathsInPartition = changedSourcePaths.filter((path) => + [...partition.dirs].some((partitionDir) => path.startsWith(`${partitionDir}/`)) + ); + + if (changedSourcePathsInPartition.length === 0) { + console.log(`[build-partitioning] 'None' build of '${partition.name}'.`); + console.log(`[build-partitioning] Building 0/${partition.dirs.size}/${allPackageDirs.size} packages.`); + console.log(``); + return { + mode: "none", + name: partition.name, + }; + } + + const affectedPackageNamesInPartition = new Set( + affectedPackageDirsInAllPartitions + .filter((pkgDir) => partition.dirs.has(pkgDir)) + .map((packageDir) => packageNamesByDir.get(packageDir)!) + ); + + const relevantPackageNamesInPartition = new Set( + [...(await getDirsOfDependencies(affectedPackageNamesInPartition, partition.name))].map( + (pkgDir) => packageNamesByDir.get(pkgDir)! + ) + ); + + console.log(`[build-partitioning] 'Partial' build of '${partition.name}'`); + console.log( + `[build-partitioning] Building ${relevantPackageNamesInPartition.size}/${partition.dirs.size}/${allPackageDirs.size} packages.` + ); + console.log(relevantPackageNamesInPartition); + + const upstreamPackageNamesInPartition = new Set( + [...relevantPackageNamesInPartition].filter((pkgName) => !affectedPackageNamesInPartition.has(pkgName)) + ); + + await assertOptimalPartialBuild({ + partition, + relevantPackageNamesInPartition, + upstreamPackageNamesInPartition, + affectedPackageNamesInPartition, + }); + + return { + mode: "partial", + name: partition.name, + bootstrapPnpmFilterString: [...relevantPackageNamesInPartition].map((p) => `-F '${p}'`).join(" "), + upstreamPnpmFilterString: [...upstreamPackageNamesInPartition].map((p) => `-F '${p}'`).join(" "), + affectedPnpmFilterString: [...affectedPackageNamesInPartition].map((p) => `-F '${p}'`).join(" "), + }; + }) + ); +} + +async function getDirsOfDependencies(leafPackageNames: Set) { + const packagesFilter = [...leafPackageNames].map((pkgName) => `-F ${pkgName}...`).join(" "); + return new Set( + stdoutArray(execSync(`pnpm ${packagesFilter} exec bash -c pwd`).toString()) // + .map((pkgDir) => path.relative(cwd, pkgDir)) + .map((pkgDir) => pkgDir.split(path.sep).join(path.posix.sep)) + ); +} diff --git a/.github/supporting-files/ci/build-partitioning/globals.ts b/.github/supporting-files/ci/build-partitioning/globals.ts new file mode 100644 index 00000000000..c052e0da5a7 --- /dev/null +++ b/.github/supporting-files/ci/build-partitioning/globals.ts @@ -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. + */ + +import * as path from "path"; +import * as fs from "fs"; + +export const __ROOT_PKG_NAME = "kie-tools-root"; + +export const __PACKAGES_ROOT_DIRS = ["packages", "examples"]; + +export const __NON_SOURCE_FILES_PATTERNS = stdoutArray( + fs.readFileSync(path.resolve(__dirname, "../patterns/non-source-files-patterns.txt"), "utf-8") +); + +export function stdoutArray(output: string) { + return output + .trim() + .split(/\s/) + .filter((s) => !!s); +} diff --git a/.github/supporting-files/ci/build-partitioning/types.ts b/.github/supporting-files/ci/build-partitioning/types.ts new file mode 100644 index 00000000000..63f996b2848 --- /dev/null +++ b/.github/supporting-files/ci/build-partitioning/types.ts @@ -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. + */ + +export type None = { + mode: "none"; + name: string; +}; + +export type Full = { + mode: "full"; + name: string; + bootstrapPnpmFilterString: string; + fullBuildPnpmFilterString: string; +}; + +export type Partial = { + mode: "partial"; + name: string; + bootstrapPnpmFilterString: string; + upstreamPnpmFilterString: string; + affectedPnpmFilterString: string; +}; + +export type PartitionDefinition = { + name: string; + leafPackageNames: Set; + dirs: Set; +}; diff --git a/.github/supporting-files/ci/partitions/partition0.txt b/.github/supporting-files/ci/partitions/partition0.txt new file mode 100644 index 00000000000..3792439c1ac --- /dev/null +++ b/.github/supporting-files/ci/partitions/partition0.txt @@ -0,0 +1 @@ +@kie-tools/kn-plugin-workflow \ No newline at end of file diff --git a/.github/supporting-files/ci/partitions/partition1.txt b/.github/supporting-files/ci/partitions/partition1.txt new file mode 100644 index 00000000000..c654e2afab4 --- /dev/null +++ b/.github/supporting-files/ci/partitions/partition1.txt @@ -0,0 +1,11 @@ +@kie-tools-examples/sonataflow-greeting-quarkus-example +@kie-tools/serverless-logic-web-tools-swf-builder-image +@kie-tools/serverless-logic-web-tools +@kie-tools/serverless-logic-web-tools-base-builder-image +@kie-tools/serverless-logic-web-tools-swf-dev-mode-image +@kie-tools/dashbuilder-swf-monitoring-dashboard +@kie-tools/dashbuilder-viewer-image +chrome-extension-serverless-workflow-editor +vscode-extension-dashbuilder-editor +yard-vscode-extension +swf-vscode-extension diff --git a/.github/supporting-files/ci/build-artifacts-patterns.txt b/.github/supporting-files/ci/patterns/build-artifacts-patterns.txt similarity index 100% rename from .github/supporting-files/ci/build-artifacts-patterns.txt rename to .github/supporting-files/ci/patterns/build-artifacts-patterns.txt diff --git a/.github/supporting-files/ci/end-to-end-tests-artifacts-patterns.txt b/.github/supporting-files/ci/patterns/end-to-end-tests-artifacts-patterns.txt similarity index 100% rename from .github/supporting-files/ci/end-to-end-tests-artifacts-patterns.txt rename to .github/supporting-files/ci/patterns/end-to-end-tests-artifacts-patterns.txt diff --git a/.github/supporting-files/ci/end-to-end-tests-reports-patterns.txt b/.github/supporting-files/ci/patterns/end-to-end-tests-reports-patterns.txt similarity index 71% rename from .github/supporting-files/ci/end-to-end-tests-reports-patterns.txt rename to .github/supporting-files/ci/patterns/end-to-end-tests-reports-patterns.txt index d04f8208c4e..72a3e252371 100644 --- a/.github/supporting-files/ci/end-to-end-tests-reports-patterns.txt +++ b/.github/supporting-files/ci/patterns/end-to-end-tests-reports-patterns.txt @@ -1,2 +1,3 @@ **/dist-e2e-tests/junit-report*.xml +**/dist-e2e-tests/TESTS-*.xml **/target/failsafe-reports/TEST-*.xml diff --git a/.github/supporting-files/ci/non-source-files-patterns.txt b/.github/supporting-files/ci/patterns/non-source-files-patterns.txt similarity index 96% rename from .github/supporting-files/ci/non-source-files-patterns.txt rename to .github/supporting-files/ci/patterns/non-source-files-patterns.txt index f4cec4c687a..625b6a1a574 100644 --- a/.github/supporting-files/ci/non-source-files-patterns.txt +++ b/.github/supporting-files/ci/patterns/non-source-files-patterns.txt @@ -11,4 +11,5 @@ gifs repo .vscode .idea +.ci .github/CODEOWNERS diff --git a/.github/supporting-files/ci/tests-reports-patterns.txt b/.github/supporting-files/ci/patterns/tests-reports-patterns.txt similarity index 100% rename from .github/supporting-files/ci/tests-reports-patterns.txt rename to .github/supporting-files/ci/patterns/tests-reports-patterns.txt diff --git a/.github/supporting-files/ci/tests-source-files-patterns.txt b/.github/supporting-files/ci/patterns/tests-source-files-patterns.txt similarity index 100% rename from .github/supporting-files/ci/tests-source-files-patterns.txt rename to .github/supporting-files/ci/patterns/tests-source-files-patterns.txt diff --git a/.github/supporting-files/publish_emscripten_fs/README.md b/.github/supporting-files/publish_emscripten_fs/README.md index 2abcddbdb30..489fbb9073d 100644 --- a/.github/supporting-files/publish_emscripten_fs/README.md +++ b/.github/supporting-files/publish_emscripten_fs/README.md @@ -1,3 +1,20 @@ + + # @kie-tools / Emscripten :: FS Simple `emscripten` build to expose its FS implementation at the `FS` variable. diff --git a/.github/supporting-files/publish_emscripten_fs/build.sh b/.github/supporting-files/publish_emscripten_fs/build.sh index 903725012e6..2c9cb18c78a 100755 --- a/.github/supporting-files/publish_emscripten_fs/build.sh +++ b/.github/supporting-files/publish_emscripten_fs/build.sh @@ -1,4 +1,22 @@ #!/bin/bash +# +# 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. +# rm -rf dist mkdir dist diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml index 33ae538796a..a4809c010ca 100644 --- a/.github/workflows/ci_build.yml +++ b/.github/workflows/ci_build.yml @@ -1,3 +1,22 @@ +# +# 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: "CI :: Build" on: @@ -13,6 +32,7 @@ concurrency: env: TMPDIR: "/tmp" + BUILD_ENV__accessErrorsLogFileAbsolutePath: "/tmp/build-env-access-errors.log" jobs: run: @@ -21,6 +41,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-13, windows-latest] + partition: [0, 1, 2] runs-on: ${{ matrix.os }} steps: - name: "Support longpaths" @@ -36,6 +57,16 @@ jobs: with: ref: ${{ github.base_ref }} + - name: "Setup pnpm" + uses: pnpm/action-setup@v3 + with: + version: 9.3.0 + + - name: "Setup Node" + uses: actions/setup-node@v4 + with: + node-version: 20.14.0 + - name: "Setup CI patterns" id: ci_patterns uses: ./.github/actions/setup-ci-patterns @@ -44,44 +75,42 @@ jobs: id: setup_build_mode shell: bash run: | - export CHANGED_SOURCE_PATHS=($(eval "git diff --name-only ${{ steps.checkout_pr.outputs.base_sha }} ${{ steps.checkout_pr.outputs.head_sha }} -- ${{ steps.ci_patterns.outputs.non_source_files_patterns_for_git_diff }}")) - echo "Changed source paths:" - echo ${#CHANGED_SOURCE_PATHS[@]} - printf '%s\n' "${CHANGED_SOURCE_PATHS[@]}" - export CHANGED_SOURCE_PATHS_IN_ROOT=($(printf '%s\n' "${CHANGED_SOURCE_PATHS[@]}" | grep -v -e "^packages" -e "^examples")) - echo "Changed source paths in root:" - echo ${#CHANGED_SOURCE_PATHS_IN_ROOT[@]} - printf '%s\n' "${CHANGED_SOURCE_PATHS_IN_ROOT[@]}" + npm -g install bun@1.1.6 + + bun ./.github/supporting-files/ci/build-partitioning/build_partitioning.ts \ + --outputPath='/tmp/partitions.json' \ + --forceFull='${{ !github.event.pull_request }}' \ + --baseSha='${{ steps.checkout_pr.outputs.base_sha }}' \ + --headSha='${{steps.checkout_pr.outputs.head_sha }}' \ + --graphJsonPath='./repo/graph.json' \ + --partition='./.github/supporting-files/ci/partitions/partition0.txt' \ + --partition='./.github/supporting-files/ci/partitions/partition1.txt' - if [ ${#CHANGED_SOURCE_PATHS[@]} -eq 0 ]; then - echo 'No source files changed; `CI :: Build` (none) will run.' - echo "mode=none" >> $GITHUB_OUTPUT - elif [ ! ${{ github.event.pull_request }} ]; then - echo 'Push to the `main` branch happened; `CI :: Build` (full) will run.' - echo "mode=full" >> $GITHUB_OUTPUT - elif [ ${#CHANGED_SOURCE_PATHS_IN_ROOT[@]} -eq 0 ]; then - echo 'No source files changed in root; `CI :: Build` (partial) will run.' - echo "mode=partial" >> $GITHUB_OUTPUT - else - echo 'Source files changed in root; `CI :: Build` (full) will run.' - echo "mode=full" >> $GITHUB_OUTPUT - fi + npm -g uninstall bun - echo "Done" + echo "mode=$(jq --raw-output '.[${{ matrix.partition }}].mode' /tmp/partitions.json)" >> $GITHUB_OUTPUT + echo "bootstrapPnpmFilterString=$(jq --raw-output '.[${{ matrix.partition }}].bootstrapPnpmFilterString' /tmp/partitions.json)" >> $GITHUB_OUTPUT + echo "fullBuildPnpmFilterString=$(jq --raw-output '.[${{ matrix.partition }}].fullBuildPnpmFilterString' /tmp/partitions.json)" >> $GITHUB_OUTPUT + echo "upstreamPnpmFilterString=$(jq --raw-output '.[${{ matrix.partition }}].upstreamPnpmFilterString' /tmp/partitions.json)" >> $GITHUB_OUTPUT + echo "affectedPnpmFilterString=$(jq --raw-output '.[${{ matrix.partition }}].affectedPnpmFilterString' /tmp/partitions.json)" >> $GITHUB_OUTPUT + echo "Done." - name: "Setup environment" if: steps.setup_build_mode.outputs.mode != 'none' uses: ./.github/actions/setup-env - - name: "FULL → Bootstrap" - if: steps.setup_build_mode.outputs.mode == 'full' + - name: "Bootstrap" + if: steps.setup_build_mode.outputs.mode != 'none' env: PLAYWRIGHT_BASE__installDeps: "true" uses: ./.github/actions/bootstrap + with: + pnpm_filter_string: ${{ steps.setup_build_mode.outputs.bootstrapPnpmFilterString }} - - name: "FULL → Build (without some images)" + - name: "FULL → Build" if: steps.setup_build_mode.outputs.mode == 'full' + shell: bash env: WEBPACK__minimize: "false" WEBPACK__tsLoaderTranspileOnly: "false" @@ -96,122 +125,19 @@ jobs: START_SERVER_AND_TEST_INSECURE: "true" NODE_OPTIONS: "--max_old_space_size=4096" run: >- - pnpm - -F='!@kie-tools/serverless-logic-web-tools-swf-dev-mode-image' - -F='!@kie-tools/dev-deployment-base-image' - -F='!@kie-tools/dev-deployment-kogito-quarkus-blank-app-image' - -F='!@kie-tools/dev-deployment-dmn-form-webapp-image' - -F='!@kie-tools/serverless-logic-web-tools-base-builder-image' - -F='!@kie-tools/dashbuilder-viewer-image' - -r --workspace-concurrency=1 build:prod - - - name: "FULL → Build → serverless-logic-web-tools-swf-dev-mode-image" - if: steps.setup_build_mode.outputs.mode == 'full' && runner.os == 'Linux' - env: - KIE_TOOLS_BUILD__runTests: "true" - KIE_TOOLS_BUILD__runEndToEndTests: "true" - KIE_TOOLS_BUILD__buildContainerImages: "true" - run: | - echo "Clean up container image resources" - if command -v docker &> /dev/null; then - docker system prune -af - fi - if command -v podman &> /dev/null; then - podman system prune --all --force - fi - echo "Build @kie-tools/serverless-logic-web-tools-swf-dev-mode-image" - pnpm -F @kie-tools/serverless-logic-web-tools-swf-dev-mode-image... --workspace-concurrency=1 build:prod - - - name: "FULL → Build → dev-deployment-base-image and dev-deployment-kogito-quarkus-blank-app" - if: steps.setup_build_mode.outputs.mode == 'full' && runner.os == 'Linux' - env: - KIE_TOOLS_BUILD__runTests: "true" - KIE_TOOLS_BUILD__runEndToEndTests: "true" - KIE_TOOLS_BUILD__buildContainerImages: "true" - run: | - echo "Clean up container image resources" - if command -v docker &> /dev/null; then - docker system prune -af - fi - if command -v podman &> /dev/null; then - podman system prune --all --force - fi - echo "Build @kie-tools/dev-deployment-base-image" - pnpm -F @kie-tools/dev-deployment-base-image... -F @kie-tools/dev-deployment-kogito-quarkus-blank-app... --workspace-concurrency=1 build:prod - - - name: "FULL → Build → dev-deployment-dmn-form-webapp-image" - if: steps.setup_build_mode.outputs.mode == 'full' && runner.os == 'Linux' - env: - KIE_TOOLS_BUILD__runTests: "true" - KIE_TOOLS_BUILD__runEndToEndTests: "true" - KIE_TOOLS_BUILD__buildContainerImages: "true" - run: | - echo "Clean up container image resources" - if command -v docker &> /dev/null; then - docker system prune -af - fi - if command -v podman &> /dev/null; then - podman system prune --all --force - fi - echo "Build @kie-tools/dev-deployment-dmn-form-webapp-image" - pnpm -F @kie-tools/dev-deployment-dmn-form-webapp-image... --workspace-concurrency=1 build:prod - - - name: "FULL → Build → serverless-logic-web-tools-base-builder-image" - if: steps.setup_build_mode.outputs.mode == 'full' && runner.os == 'Linux' - env: - KIE_TOOLS_BUILD__runTests: "true" - KIE_TOOLS_BUILD__runEndToEndTests: "true" - KIE_TOOLS_BUILD__buildContainerImages: "true" - run: | - echo "Clean up container image resources" - if command -v docker &> /dev/null; then - docker system prune -af - fi - if command -v podman &> /dev/null; then - podman system prune --all --force - fi - echo "Build @kie-tools/serverless-logic-web-tools-base-builder-image" - pnpm -F @kie-tools/serverless-logic-web-tools-base-builder-image... --workspace-concurrency=1 build:prod + eval "pnpm ${{ steps.setup_build_mode.outputs.fullBuildPnpmFilterString }} --workspace-concurrency=1 build:prod" - - name: "FULL → Build → dashbuilder-viewer-image" - if: steps.setup_build_mode.outputs.mode == 'full' && runner.os == 'Linux' - env: - KIE_TOOLS_BUILD__runTests: "true" - KIE_TOOLS_BUILD__runEndToEndTests: "true" - KIE_TOOLS_BUILD__buildContainerImages: "true" - run: | - echo "Clean up container image resources" - if command -v docker &> /dev/null; then - docker system prune -af - fi - if command -v podman &> /dev/null; then - podman system prune --all --force - fi - echo "Build @kie-tools/dashbuilder-viewer-image" - pnpm -F @kie-tools/dashbuilder-viewer-image... --workspace-concurrency=1 build:prod - - - name: "PARTIAL → Bootstrap" - if: steps.setup_build_mode.outputs.mode == 'partial' - env: - PLAYWRIGHT_BASE__installDeps: "true" - uses: ./.github/actions/bootstrap - with: - pnpm_filter_string: -F "...[${{ steps.checkout_pr.outputs.base_sha }}]..." - - - name: "PARTIAL → Build dependencies" + - name: "PARTIAL → Build upstream" if: steps.setup_build_mode.outputs.mode == 'partial' shell: bash env: KIE_TOOLS_BUILD__buildContainerImages: ${{ runner.os != 'Windows' }} KIE_TOOLS_BUILD__buildExamples: "true" run: | - export ALL_DEPENDENCIES_FILTER=$(pnpm -F="...[${{ steps.checkout_pr.outputs.base_sha }}]" exec bash -c 'echo -n " -F=$(jq --raw-output .name package.json)^..."') - export CHANGED_PKGS_EXCLUSION_FILTER=$(pnpm -F="...[${{ steps.checkout_pr.outputs.base_sha }}]" exec bash -c 'echo -n " -F='"'"'!$(jq --raw-output .name package.json)'"'"'"') - echo $ALL_DEPENDENCIES_FILTER - echo $CHANGED_PKGS_EXCLUSION_FILTER - eval "pnpm $ALL_DEPENDENCIES_FILTER $CHANGED_PKGS_EXCLUSION_FILTER build:dev" + eval "pnpm ${{ steps.setup_build_mode.outputs.upstreamPnpmFilterString }} build:dev" - - name: "PARTIAL → Build changed and dependents" + - name: "PARTIAL → Build changed and downstream" + shell: bash if: steps.setup_build_mode.outputs.mode == 'partial' env: WEBPACK__minimize: "false" @@ -224,7 +150,7 @@ jobs: START_SERVER_AND_TEST_INSECURE: "true" NODE_OPTIONS: "--max_old_space_size=4096" run: | - pnpm -F "...[${{ steps.checkout_pr.outputs.base_sha }}]" --workspace-concurrency=1 build:prod + eval "pnpm ${{ steps.setup_build_mode.outputs.affectedPnpmFilterString }} --workspace-concurrency=1 build:prod" - name: "Check tests result (`main` only)" if: always() && !cancelled() && steps.setup_build_mode.outputs.mode != 'none' @@ -244,6 +170,14 @@ jobs: const script = require("./scripts/check-junit-report-results/src/index.js"); await script({ core, glob, patterns }); + - name: "Check build-env access errors log" + if: always() && !cancelled() && steps.setup_build_mode.outputs.mode != 'none' + shell: bash + run: | + touch "$BUILD_ENV__accessErrorsLogFileAbsolutePath" + cat "$BUILD_ENV__accessErrorsLogFileAbsolutePath" + [ "0" == "$(cat "$BUILD_ENV__accessErrorsLogFileAbsolutePath" | wc -l | tr -d ' ')" ] + - name: "Check hanging uncommitted files (you should commit those!)" if: always() && !cancelled() && steps.setup_build_mode.outputs.mode != 'none' shell: bash @@ -254,6 +188,8 @@ jobs: - name: "Upload reports and artifacts" if: always() && !cancelled() && steps.setup_build_mode.outputs.mode != 'none' uses: ./.github/actions/upload-ci-reports-and-artifacts + with: + partition_index: ${{ matrix.partition }} - name: "Upload end-to-end tests results to Buildkite (`main` only)" if: always() && !cancelled() && steps.setup_build_mode.outputs.mode != 'none' && !github.event.pull_request diff --git a/.github/workflows/ci_check_code_formatting.yml b/.github/workflows/ci_check_code_formatting.yml index 6b39b7f13cc..3a37f7f3cee 100644 --- a/.github/workflows/ci_check_code_formatting.yml +++ b/.github/workflows/ci_check_code_formatting.yml @@ -1,3 +1,22 @@ +# +# 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: "CI :: Code formatting" on: diff --git a/.github/workflows/ci_check_dependencies_consistency.yaml b/.github/workflows/ci_check_dependencies_consistency.yaml index 1dcb2f170c5..c87f3dcd250 100644 --- a/.github/workflows/ci_check_dependencies_consistency.yaml +++ b/.github/workflows/ci_check_dependencies_consistency.yaml @@ -1,3 +1,22 @@ +# +# 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: "CI :: Dependencies consistency" on: diff --git a/.github/workflows/ci_codeql.yml b/.github/workflows/ci_codeql.yml index 36584f74748..42c436d3f67 100644 --- a/.github/workflows/ci_codeql.yml +++ b/.github/workflows/ci_codeql.yml @@ -1,3 +1,22 @@ +# +# 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. +# + # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # diff --git a/.github/workflows/publish_emscripten_fs.yml b/.github/workflows/publish_emscripten_fs.yml index 0f5b708c762..3a630f0a229 100644 --- a/.github/workflows/publish_emscripten_fs.yml +++ b/.github/workflows/publish_emscripten_fs.yml @@ -1,3 +1,22 @@ +# +# 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: "Publish emscripten-fs" on: diff --git a/.github/workflows/release_build_extended_services.yml b/.github/workflows/release_build_extended_services.yml index 108f616383f..80c7cb320a5 100644 --- a/.github/workflows/release_build_extended_services.yml +++ b/.github/workflows/release_build_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Release :: Build (Extended Services)" on: @@ -17,6 +36,14 @@ on: type: string required: false default: "" + release_candidate: + type: boolean + required: false + default: false + release_candidate_version: + type: string + required: false + default: "" secrets: gh_token: required: false @@ -64,8 +91,8 @@ jobs: run: | pnpm ${{ steps.bootstrap.outputs.pnpm_filter_string }} build:prod - - name: "Upload Extended Services for macOS (macOS only)" - if: ${{ runner.os == 'macOS' && !inputs.dry_run }} + - name: "Upload Extended Services for macOS" + if: ${{ runner.os == 'macOS' && !inputs.dry_run && !inputs.release_candidate }} uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.gh_token }} @@ -75,8 +102,8 @@ jobs: asset_name: kie_sandbox_extended_services_macos_${{ inputs.tag }}.dmg asset_content_type: application/octet-stream - - name: "Upload Extended Services for Windows (Windows only)" - if: ${{ runner.os == 'Windows' && !inputs.dry_run }} + - name: "Upload Extended Services for Windows" + if: ${{ runner.os == 'Windows' && !inputs.dry_run && !inputs.release_candidate }} uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.gh_token }} @@ -85,3 +112,89 @@ jobs: asset_path: "./packages/extended-services/dist/win32/kie_sandbox_extended_services.exe" asset_name: "kie_sandbox_extended_services_windows_${{ inputs.tag }}.exe" asset_content_type: application/octet-stream + + - name: "Setup Extended Services for macOS artifacts" + id: macos_setup_artifact + if: ${{ runner.os == 'macOS' && !inputs.dry_run && inputs.release_candidate }} + shell: bash + env: + PROJECT_VERSION: ${{ inputs.release_candidate_version }} + run: | + ARTIFACT_ZIP_FILE="./extended-services-release-artifacts/incubator-kie-$PROJECT_VERSION-sandbox-extended-services-macOS.zip" + echo "ARTIFACT_ZIP_FILE=$ARTIFACT_ZIP_FILE" >> "$GITHUB_OUTPUT" + mkdir ./extended-services-release-artifacts + zip $ARTIFACT_ZIP_FILE ./packages/extended-services/dist/darwin/Kogito.dmg + + - name: "Sign Extended Services for macOS artifact" + if: ${{ runner.os == 'macOS' && !inputs.dry_run && inputs.release_candidate }} + shell: bash + env: + GPG_KEY: ${{ secrets.GPG_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + ARTIFACT_ZIP_FILE: ${{ steps.macos_setup_artifact.outputs.ARTIFACT_ZIP_FILE }} + run: | + echo $GPG_KEY > ./signkey.gpg + gpg --list-keys + gpg --batch --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --import ./signkey.gpg + rm ./signkey.gpg + echo $GPG_PASSPHRASE | gpg --no-tty --batch --sign --pinentry-mode loopback --passphrase-fd 0 --output $ARTIFACT_ZIP_FILE.asc --detach-sig $ARTIFACT_ZIP_FILE + shasum -a 512 $ARTIFACT_ZIP_FILE > $ARTIFACT_ZIP_FILE.sha512 + + - name: "Upload Extended Services for macOS artifact" + if: ${{ runner.os == 'macOS' && !inputs.dry_run && inputs.release_candidate }} + shell: bash + env: + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + PROJECT_VERSION: ${{ inputs.release_candidate_version }} + ARTIFACT_ZIP_FILE: ${{ steps.macos_setup_artifact.outputs.ARTIFACT_ZIP_FILE }} + run: | + svn co --depth=empty https://dist.apache.org/repos/dist/dev/incubator/kie svn-kie + cp ./extended-services-release-artifacts/* svn-kie/$PROJECT_VERSION/ + svn add "svn-kie/$PROJECT_VERSION" + cd svn-kie + svn ci --non-interactive --no-auth-cache --username "$SVN_USERNAME" --password "$SVN_PASSWORD" -m "Apache KIE $PROJECT_VERSION Extended Services for macOS artifact" + rm -rf svn-kie + + - name: "Setup Extended Services for Windows artifacts" + id: windows_setup_artifact + if: ${{ runner.os == 'macOS' && !inputs.dry_run && inputs.release_candidate }} + shell: pwsh + env: + PROJECT_VERSION: ${{ inputs.release_candidate_version }} + run: | + ARTIFACT_ZIP_FILE="./extended-services-release-artifacts/incubator-kie-$PROJECT_VERSION-sandbox-extended-services-macOS.zip" + echo "ARTIFACT_ZIP_FILE=$ARTIFACT_ZIP_FILE" >> "$GITHUB_OUTPUT" + mkdir ./extended-services-release-artifacts + zip $ARTIFACT_ZIP_FILE ./packages/extended-services/dist/win32/kie_sandbox_extended_services.exe + + - name: "Sign Extended Services for Windows artifact" + if: ${{ runner.os == 'Windows' && !inputs.dry_run && inputs.release_candidate }} + shell: pwsh + env: + GPG_KEY: ${{ secrets.GPG_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + ARTIFACT_ZIP_FILE: ${{ steps.windows_setup_artifact.outputs.ARTIFACT_ZIP_FILE }} + run: | + echo $GPG_KEY > ./signkey.gpg + gpg --list-keys + gpg --batch --pinentry-mode loopback --passphrase "$GPG_PASSPHRASE" --import ./signkey.gpg + rm ./signkey.gpg + echo $GPG_PASSPHRASE | gpg --no-tty --batch --sign --pinentry-mode loopback --passphrase-fd 0 --output $ARTIFACT_ZIP_FILE.asc --detach-sig $ARTIFACT_ZIP_FILE + shasum -a 512 $ARTIFACT_ZIP_FILE > $ARTIFACT_ZIP_FILE.sha512 + + - name: "Upload Extended Services for Windows artifact" + if: ${{ runner.os == 'Windows' && !inputs.dry_run && inputs.release_candidate }} + shell: pwsh + env: + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + PROJECT_VERSION: ${{ inputs.release_candidate_version }} + ARTIFACT_ZIP_FILE: ${{ steps.windows_setup_artifact.outputs.ARTIFACT_ZIP_FILE }} + run: | + svn co --depth=empty https://dist.apache.org/repos/dist/dev/incubator/kie svn-kie + cp ./extended-services-release-artifacts/* svn-kie/$PROJECT_VERSION/ + svn add "svn-kie/$PROJECT_VERSION" + cd svn-kie + svn ci --non-interactive --no-auth-cache --username "$SVN_USERNAME" --password "$SVN_PASSWORD" -m "Apache KIE $PROJECT_VERSION Extended Services for Windows artifact" + rm -rf svn-kie diff --git a/.github/workflows/release_dry_run_extended_services.yml b/.github/workflows/release_dry_run_extended_services.yml index 1f681d830c8..3666753e647 100644 --- a/.github/workflows/release_dry_run_extended_services.yml +++ b/.github/workflows/release_dry_run_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Release :: Dry Run (Extended Services)" on: diff --git a/.github/workflows/release_publish_extended_services.yml b/.github/workflows/release_publish_extended_services.yml index 0dfe102d3a7..5b4b19cc193 100644 --- a/.github/workflows/release_publish_extended_services.yml +++ b/.github/workflows/release_publish_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Release :: Publish (Extended Services)" on: diff --git a/.github/workflows/staging_build_extended_services.yml b/.github/workflows/staging_build_extended_services.yml index ec56e8f7c76..d79640bbc54 100644 --- a/.github/workflows/staging_build_extended_services.yml +++ b/.github/workflows/staging_build_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Staging :: Build (Extended Services)" on: diff --git a/.github/workflows/staging_dry_run_extended_services.yml b/.github/workflows/staging_dry_run_extended_services.yml index 6b04f976e6a..5417cd4a480 100644 --- a/.github/workflows/staging_dry_run_extended_services.yml +++ b/.github/workflows/staging_dry_run_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Staging :: Dry Run (Extended Services)" on: diff --git a/.github/workflows/staging_publish_extended_services.yml b/.github/workflows/staging_publish_extended_services.yml index a644bf8db01..24a5e6df384 100644 --- a/.github/workflows/staging_publish_extended_services.yml +++ b/.github/workflows/staging_publish_extended_services.yml @@ -1,3 +1,22 @@ +# +# 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: "Staging :: Publish (Extended Services)" on: diff --git a/.gitignore b/.gitignore index 61eae7d040c..9b259d87f11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,21 @@ +### +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + ## ci !/.ci @@ -189,6 +207,19 @@ packages/kie-sandbox-fs/coverage packages/kie-sandbox-fs/junit packages/kie-sandbox-fs/*-0.0.0-development.tgz +# sonataflow-operator +# These files are generated by Cekit, we can ignore the operator-sdk ones. +packages/sonataflow-operator/bundle.Dockerfile +packages/sonataflow-operator/Dockerfile +# Test binary, built with `go test -c` +packages/sonataflow-operator/**/*.test +# Output of the go coverage tool, specifically when used with LiteIDE +packages/sonataflow-operator/**/*.out +# Build +packages/sonataflow-operator/bin/ +packages/sonataflow-operator/target/ +packages/sonataflow-operator/e2e-test-report.xml + #angular **/.angular @@ -282,12 +313,12 @@ packages/dmn-marshaller/**/ts-gen packages/scesim-marshaller/**/ts-gen # kogito-sf-{builder|devmode}-image: excluding module build folders -!packages/kogito-swf-common/resources/**/build -!packages/kogito-swf-builder/resources/**/build -!packages/kogito-swf-devmode/resources/**/build -!packages/kogito-swf-common/test-resources/ -!packages/kogito-swf-builder/test-resources/ -!packages/kogito-swf-devmode/test-resources/ +!packages/sonataflow-image-common/resources/**/build +!packages/sonataflow-builder-image/resources/**/build +!packages/sonataflow-devmode-image/resources/**/build +!packages/sonataflow-image-common/test-resources/ +!packages/sonataflow-builder-image/test-resources/ +!packages/sonataflow-devmode-image/test-resources/ __pycache__ packages/python-venv/venv diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml index 2319055c20a..bc2bd7823c2 100644 --- a/.idea/jsLinters/eslint.xml +++ b/.idea/jsLinters/eslint.xml @@ -1,4 +1,22 @@ + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 67f800dded4..565e3e5413b 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,4 +1,22 @@ + diff --git a/DISCLAIMER b/DISCLAIMER new file mode 100644 index 00000000000..08aef0bfcf4 --- /dev/null +++ b/DISCLAIMER @@ -0,0 +1,27 @@ +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +* Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +* Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +* + +* Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +* Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html \ No newline at end of file diff --git a/KOGITO_UPGRADE_PROCESS.md b/KOGITO_UPGRADE_PROCESS.md index f7052822622..4b74a31d4ef 100644 --- a/KOGITO_UPGRADE_PROCESS.md +++ b/KOGITO_UPGRADE_PROCESS.md @@ -1,3 +1,20 @@ + + This guide aims to assist you in the process of upgrading all Kogito dependencies versions present in the kie-tools repository. Typically, these dependencies point to backend-side modules or images @@ -56,7 +73,6 @@ You can find an example of the Java / Maven versions upgrade in [this PR](https: The Quarkus version is present in the following file categories: -- `install.js` files - `root-env/env/index.js` file - go test files @@ -70,16 +86,7 @@ You can find an example of the Quarkus upgrade in [this PR](https://github.com/a # Upgrading Kogito -The Kogito version is present in the following file categories: - -- `install.js` files -- `root-env/env/index.js` file -- `package.json` files (eg. jit-executor reference in `extended-service`) - -The best (and fastest) way to catch all the Kogito versions is to perform a search a grep (or the IDE integrated search) and replace it with the new version. So, as a key, you can use: - -- The version number: `X.Y.Z` or `X.Y.Z-YYYYMMDD-SNAPSHOT` format (eg. `10.0.0` or `10.1.0-20240424-SNAPSHOT`); -- Images references: `main-YYYY-MM-DD` (Daily builds) or `X.Y.Z-YYYYMMDD` (Weekly builds) format (eg. `main-2024-04-24` or `10.1.0-20240424` in case of snapshot version) +In the root directory, run `pnpm update-kogito-version-to --maven [version] --images-tag [tag]`. Of course, a new Kogito version may lead to incompatibilities in the code and with other dependencies. In such a case, an investigation and evetually a fix is required to complete the process. @@ -98,3 +105,14 @@ To test the `kie-sandbox-quarkus-accelerator` module with the updated version, p - Run `pnpm -F @kie-tools/online-editor... build:dev` and `pnpm -F @kie-tools/online-editor start` to test it. You can find an example of the Kogito version upgrade in [this PR](https://github.com/apache/incubator-kie-sandbox-quarkus-accelerator/pull/8) + +# Upgrading GraphQL schemas in `@kie-tools/runtime-tools-process-gateway-api`& `@kie-tools/runtime-tools-swf-gateway-api` + +The following commands will help to sync up the gateway apis the GraphQL schema with the new Kogito Data Index GraphQL schema: + +- Start a blank Data Index Container, for example `docker run -p8180:8080 docker.io/apache/incubator-kie-kogito-data-index-ephemeral:{$KOGITO_VERSION}` +- Run `pnpm -F @kie-tools/runtime-tools-process-gateway-api graphql:codegen` +- Run `pnpm -F @kie-tools/runtime-tools-swf-gateway-api graphql:codegen` + +After upgrading the GraphQL schemas it is recommended to verify that the incoming changes aren't breaking the consoles or +devui's and fix any possible conflict if needed. diff --git a/NIX_DEV_ENV.md b/NIX_DEV_ENV.md index ea3180e2d04..c18f8d30458 100644 --- a/NIX_DEV_ENV.md +++ b/NIX_DEV_ENV.md @@ -1,3 +1,20 @@ + + ## Nix-based development environment shell ### Installing diff --git a/NOTICE b/NOTICE index 9e1130eac3d..12c0968ce01 100644 --- a/NOTICE +++ b/NOTICE @@ -50,6 +50,10 @@ This product also includes the following third-party components: Copyright (C) 2013 GwtBootstrap3 +* highlight.js (https://highlightjs.org) + + Copyright (C) highlight.js + * Jakarta EE (https://jakarta.ee/) Copyright (C) Eclipse and contributors @@ -81,3 +85,7 @@ This product also includes the following third-party components: * SnakeYAML (https://bitbucket.org/snakeyaml/snakeyaml) Copyright (c) 2018, SnakeYAML + +* wysihtml (https://github.com/Voog/wysihtml) + + Copyright (C) 2012-2016 XING AG, Voog and contributors \ No newline at end of file diff --git a/README.md b/README.md index a6664f227db..1c000802054 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ + +

--- @@ -20,45 +37,85 @@ This repository contains tooling applications and libraries for KIE projects. ## Build from source -> **💡 Nix development environment**: A _devbox_ configuration is provided to automatically setup all the tools above, read more in [here](./NIX_DEV_ENV.md). +#### Step 0: Install the necessary tools + +> **💡 RECOMMENDED** +> +> **Nix development environment**: A _devbox_ configuration is provided to automatically setup all the tools below. Read more in [here](./NIX_DEV_ENV.md). -To start building the Apache KIE Tools project, you're going to need: +To build and test all packages of the Apache KIE Tools project, you're going to need: -- Node `18` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_ -- pnpm `8.7.0` _(To install, follow these instructions: https://pnpm.io/installation)_ +- Node `20` _(To install, follow these instructions: https://nodejs.org/en/download/package-manager/)_ +- pnpm `9.3.0` _(To install, follow these instructions: https://pnpm.io/installation#using-npm)_ - Maven `3.9.6` - Java `17` -- Go `1.21.5` _(To install, follow these instructions: https://go.dev/doc/install)_ +- Go `1.21.9` _(To install, follow these instructions: https://go.dev/doc/install)_ +- Python `3.12` _(To install, follow these instructions: https://www.python.org/downloads/)_ - Helm `3.13.3` _(To install, follow these instructions: https://helm.sh/docs/intro/install/)_ +- Make + +> **ℹ️ NOTE** +> +> If you plan on building container images, make sure you have a working Docker setup. Setting `KIE_TOOLS_BUILD__buildContainerImages=true` will also be necessary. + +#### Step 1: Bootstrap + +Bootstrapping installs the necessary dependencies for each package. + +- `pnpm bootstrap` --> Will bootstrap all packages +- `pnpm bootstrap [pnpm-filter]` --> Will bootstrap packages filtered by [`pnpm` filter](https://pnpm.io/filtering) +- > E.g., + > + > `pnpm bootstrap -F dmn-editor...` bootstraps the `dmn-editor` package and its dependencies. + +> **ℹ️ NOTE** +> +> If you plan on running Playwright tests, set the `PLAYWRIGHT_BASE__installDeps` environment variable to `true` before running the command above. +> +> `PLAYWRIGHT_BASE__installDeps=true pnpm bootstrap`. +> +> This will install all Playwright dependencies (such as browsers engines and OS-specific libraries). + +#### Step 2: Build + +- Dev + + - `pnpm -r build:dev` + - Will build all packages for development. Skipping linters, tests, minifiers etc. + - `pnpm [pnpm-filter] build:dev` + - Will build packages filtered by [`pnpm` filter](https://pnpm.io/filtering) + - > E.g., + > + > `pnpm -F dmn-editor... build:dev` builds the `dmn-editor` package and its dependencies. + +- Prod + + - `pnpm -r build:prod` + - Will build all packages for production. Optimizers will run, binaries will be produced for multiple architectures etc. + - `pnpm [pnpm-filter] build:prod` + - Will build packages filtered by [`pnpm` filter](https://pnpm.io/filtering) + - > E.g., + > + > `pnpm -F dmn-editor... build:prod` builds the `dmn-editor` package and its dependencies. + +- Changed + - `pnpm -F '...[HEAD]' build:dev`; or + - `pnpm -F '...[HEAD]' build:prod` + - Will build changed and affected packages based on your local changes. Useful for verifying that you didn't break anything. + +> **ℹ️ NOTE** +> +> The Apache KIE Tools build is parameterized by several Environment Variables. For an extensive list of these variables, please see the list printed by the `bootstrap` step. +> +> - To enable the examples build: `export KIE_TOOLS_BUILD__buildExamples=true` +> - To enable container images build: `export KIE_TOOLS_BUILD__buildContainerImages=true` +> - To enable E2E tests: `export KIE_TOOLS_BUILD__runEndToEndTests=true` + +> **ℹ️ NOTE** +> +> Final artifacts will be in `{packages,examples}/*/dist` directories. -> **ℹ️ NOTE:** Some packages will require that `make` is available as well. - -After installing the tools above, you'll need to download the dependencies and link the packages locally. Simply run: - -- `pnpm bootstrap` - - > **ℹ️ NOTE:** If you plan on running Playwright tests, set the `PLAYWRIGHT_BASE__installDeps` environment variable to `true` before running the command above: `PLAYWRIGHT_BASE__installDeps=true pnpm bootstrap`. This will install all Playwright dependencies (such as browsers engines and OS specific libraries). - -To install only the dependencies that are relevant to the package called `[pkg-name]`. - -- `pnpm bootstrap -F [pkg-name]...` - - > **ℹ️ NOTE:** Here, `...` is actually **necessary**! They're part of a [`pnpm` filter](https://pnpm.io/filtering#--filter-package_name-1). - -After dependencies are installed, you'll be able to build. To do so, you'll have two choices - `dev`, or `prod`. - -Note that it is recommended that you specify which package you want to build, so replace `[pkg-name]` with the name of the desired package on one of the commands below: - -- `pnpm -F [pkg-name]... build:dev` - This is fast, but not as strict. It skips tests, linters, and some type checks. Be prepared for the CI to fail on your PRs. -- `pnpm -F [pkg-name]... build:prod` - The default command to build production-ready packages. Use that to make sure your changes are correct. - -> **ℹ️ NOTE:** Here, `...` is actually **necessary**! They're part of a [`pnpm` filter](https://pnpm.io/filtering#--filter-package_name-1). - -> **ℹ️ NOTE:** If you want to build _everything_, run `pnpm -r build:dev` or `pnpm -r build:prod`. It's going to take a while, though :) - -> **ℹ️ NOTE:** The Apache KIE Tools build is parameterized by several Environment Variables. For an extensive list of these variables, please see the list printed by the `bootstrap` script. - -> **ℹ️ NOTE:** Final artifacts will be on `{packages,examples}/*/dist` directories. +--- ## Applications diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index d5da053ab8e..1ce6bffb4b3 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -1,3 +1,20 @@ + + Release workflow (post 0.13.0) Make sure an associated tag to the release is created on: diff --git a/devbox.json b/devbox.json index 108bc0d9e34..bd6d1bf34f2 100644 --- a/devbox.json +++ b/devbox.json @@ -2,14 +2,12 @@ "$schema": "https://raw.githubusercontent.com/jetpack-io/devbox/0.10.4/.schema/devbox.schema.json", "packages": { "temurin-bin-17": "17.0.9", - "nodejs": "18.19.1", + "nodejs": "20.12.2", "maven": "3.9.6", "kubernetes-helm": "3.13.3", "gnumake": "4.4.1", - "go": "1.21.8", - "python": "3.12.2", - "operator-sdk": "1.34.1", - "kubebuilder": "3.14.0" + "go": "1.21.9", + "python": "3.12.2" }, "env": { "PLAYWRIGHT_BROWSERS_PATH": "0", @@ -22,7 +20,7 @@ "shell": { "init_hook": [ ". $VENV_DIR/bin/activate", - "[[ $OSTYPE == 'darwin'* ]] && export PATH=$(echo $PATH | tr ':' '\n' | sed '\\|^/nix/store/|d' | paste -sd ':' -)" + "[[ $OSTYPE == 'darwin'* ]] && export PATH=$(echo $PATH | tr ':' '\n' | grep -vi 'xcode\\|clang\\|cctools' | paste -sd ':' -)" ], "scripts": { "versions": [ diff --git a/devbox.lock b/devbox.lock index e2408302f2a..810e96da462 100644 --- a/devbox.lock +++ b/devbox.lock @@ -93,99 +93,51 @@ } } }, - "go@1.21.8": { - "last_modified": "2024-03-22T11:26:23Z", - "resolved": "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351#go_1_21", - "source": "devbox-search", - "version": "1.21.8", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/zla9agv9h4w5byazjx2vhwnsqnk96cb5-go-1.21.8", - "default": true - } - ], - "store_path": "/nix/store/zla9agv9h4w5byazjx2vhwnsqnk96cb5-go-1.21.8" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/4mjpm4gxn04drnr2l83js185v66fw6nz-go-1.21.8", - "default": true - } - ], - "store_path": "/nix/store/4mjpm4gxn04drnr2l83js185v66fw6nz-go-1.21.8" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/isc1mgnbrfiqi3blr8glixdq1nmlap0h-go-1.21.8", - "default": true - } - ], - "store_path": "/nix/store/isc1mgnbrfiqi3blr8glixdq1nmlap0h-go-1.21.8" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/afv3zwqxyw062vg2j220658jq0g1yadv-go-1.21.8", - "default": true - } - ], - "store_path": "/nix/store/afv3zwqxyw062vg2j220658jq0g1yadv-go-1.21.8" - } - } - }, - "kubebuilder@3.14.0": { - "last_modified": "2024-03-22T07:26:23-04:00", - "resolved": "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351#kubebuilder", + "go@1.21.9": { + "last_modified": "2024-04-19T17:36:04-04:00", + "resolved": "github:NixOS/nixpkgs/92d295f588631b0db2da509f381b4fb1e74173c5#go_1_21", "source": "devbox-search", - "version": "3.14.0", + "version": "1.21.9", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/hdggigkq5ibqvk3ksmq7cwlyn1l2gdsv-kubebuilder-3.14.0", + "path": "/nix/store/hnqasgvlimar5j7k0kpbbbxbkn8zn1sj-go-1.21.9", "default": true } ], - "store_path": "/nix/store/hdggigkq5ibqvk3ksmq7cwlyn1l2gdsv-kubebuilder-3.14.0" + "store_path": "/nix/store/hnqasgvlimar5j7k0kpbbbxbkn8zn1sj-go-1.21.9" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/7ykpi9d6jd4zhxs5c6lfbb9p5kgm5m3p-kubebuilder-3.14.0", + "path": "/nix/store/1j8kg9v5zck8j2vnxng7qg9fd7kmmahm-go-1.21.9", "default": true } ], - "store_path": "/nix/store/7ykpi9d6jd4zhxs5c6lfbb9p5kgm5m3p-kubebuilder-3.14.0" + "store_path": "/nix/store/1j8kg9v5zck8j2vnxng7qg9fd7kmmahm-go-1.21.9" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/jpqwxd42bcfkxljy0pkqagbnpifdxvpx-kubebuilder-3.14.0", + "path": "/nix/store/hy3h296ckqcdrm4bq8gxwrrpgfi84lxm-go-1.21.9", "default": true } ], - "store_path": "/nix/store/jpqwxd42bcfkxljy0pkqagbnpifdxvpx-kubebuilder-3.14.0" + "store_path": "/nix/store/hy3h296ckqcdrm4bq8gxwrrpgfi84lxm-go-1.21.9" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/c492irdhgzr6wbl6zggn5bw23dlgxb9r-kubebuilder-3.14.0", + "path": "/nix/store/r9qdln83ffwmhjrl20byxxxav645mb5b-go-1.21.9", "default": true } ], - "store_path": "/nix/store/c492irdhgzr6wbl6zggn5bw23dlgxb9r-kubebuilder-3.14.0" + "store_path": "/nix/store/r9qdln83ffwmhjrl20byxxxav645mb5b-go-1.21.9" } } }, @@ -285,116 +237,68 @@ } } }, - "nodejs@18.19.1": { - "last_modified": "2024-03-29T03:06:03Z", + "nodejs@20.12.2": { + "last_modified": "2024-05-22T06:18:38Z", "plugin_version": "0.0.2", - "resolved": "github:NixOS/nixpkgs/9b09bde6e3fc9493b6a8b2a5702ac87c66505c64#nodejs_18", + "resolved": "github:NixOS/nixpkgs/3f316d2a50699a78afe5e77ca486ad553169061e#nodejs_20", "source": "devbox-search", - "version": "18.19.1", + "version": "20.12.2", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/r5iax8ijz3fb2318mvk1lqhmgd3ywra0-nodejs-18.19.1", + "path": "/nix/store/bzzs4kvjyvjjhs3rj08vqpvvzmfggvbv-nodejs-20.12.2", "default": true }, { "name": "libv8", - "path": "/nix/store/5da4myd75fd2lng1c5y7vgsjx5d0fzps-nodejs-18.19.1-libv8" + "path": "/nix/store/c56874bxzncqwy58kif6wfnzy017v1sl-nodejs-20.12.2-libv8" } ], - "store_path": "/nix/store/r5iax8ijz3fb2318mvk1lqhmgd3ywra0-nodejs-18.19.1" + "store_path": "/nix/store/bzzs4kvjyvjjhs3rj08vqpvvzmfggvbv-nodejs-20.12.2" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/c77aqmmdz2xrnwx8r00830vrikpvxsgs-nodejs-18.19.1", + "path": "/nix/store/y50zafzgnnkrj4hvmk23icv2ggvys8r9-nodejs-20.12.2", "default": true }, { "name": "libv8", - "path": "/nix/store/kbsp3mrmr31fs0qvscik29ls4ph97wvy-nodejs-18.19.1-libv8" + "path": "/nix/store/vc7y8h3c8pwbh4zbvjcyfqrd3fhdjhw6-nodejs-20.12.2-libv8" } ], - "store_path": "/nix/store/c77aqmmdz2xrnwx8r00830vrikpvxsgs-nodejs-18.19.1" + "store_path": "/nix/store/y50zafzgnnkrj4hvmk23icv2ggvys8r9-nodejs-20.12.2" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/d797z1ds0cp8djhd4r6g7lw660npalv4-nodejs-18.19.1", + "path": "/nix/store/l53svh1nfrcb83qbqvrrkangrcl1rr25-nodejs-20.12.2", "default": true }, { "name": "libv8", - "path": "/nix/store/1m4bp8f0frjjrfgrply18hssy1ddpxjp-nodejs-18.19.1-libv8" + "path": "/nix/store/q71hh22bfqjygd34gq16dv4dwfc33378-nodejs-20.12.2-libv8" } ], - "store_path": "/nix/store/d797z1ds0cp8djhd4r6g7lw660npalv4-nodejs-18.19.1" + "store_path": "/nix/store/l53svh1nfrcb83qbqvrrkangrcl1rr25-nodejs-20.12.2" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/s01jxkf9lr2ajlqxk86065rs77mm96am-nodejs-18.19.1", + "path": "/nix/store/6g9n96qf1yx139xklnmy3v4xhjvjgsji-nodejs-20.12.2", "default": true }, { "name": "libv8", - "path": "/nix/store/82932mmpy9jyry0f1yn8s31l0vnd9xa2-nodejs-18.19.1-libv8" - } - ], - "store_path": "/nix/store/s01jxkf9lr2ajlqxk86065rs77mm96am-nodejs-18.19.1" - } - } - }, - "operator-sdk@1.34.1": { - "last_modified": "2024-03-22T07:26:23-04:00", - "resolved": "github:NixOS/nixpkgs/a3ed7406349a9335cb4c2a71369b697cecd9d351#operator-sdk", - "source": "devbox-search", - "version": "1.34.1", - "systems": { - "aarch64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/39bndv1fk8rrdi1a0xlfaw5qhgylnzkk-operator-sdk-1.34.1", - "default": true - } - ], - "store_path": "/nix/store/39bndv1fk8rrdi1a0xlfaw5qhgylnzkk-operator-sdk-1.34.1" - }, - "aarch64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/n9ln3a2whzwp0x2ik3rxh10wn7ra85r5-operator-sdk-1.34.1", - "default": true - } - ], - "store_path": "/nix/store/n9ln3a2whzwp0x2ik3rxh10wn7ra85r5-operator-sdk-1.34.1" - }, - "x86_64-darwin": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/y0vi2nvhsgakbvlmbhdpsqdnl83l8q65-operator-sdk-1.34.1", - "default": true - } - ], - "store_path": "/nix/store/y0vi2nvhsgakbvlmbhdpsqdnl83l8q65-operator-sdk-1.34.1" - }, - "x86_64-linux": { - "outputs": [ - { - "name": "out", - "path": "/nix/store/h607xcilqpwsc0mna9bc6yilj4v3q4y3-operator-sdk-1.34.1", - "default": true + "path": "/nix/store/s7b0dqga0311mvq48mirnlm0p3dr4gm3-nodejs-20.12.2-libv8" } ], - "store_path": "/nix/store/h607xcilqpwsc0mna9bc6yilj4v3q4y3-operator-sdk-1.34.1" + "store_path": "/nix/store/6g9n96qf1yx139xklnmy3v4xhjvjgsji-nodejs-20.12.2" } } }, diff --git a/docs/kie.svg b/docs/kie.svg index dba5a247bd4..4259d92dc97 100644 --- a/docs/kie.svg +++ b/docs/kie.svg @@ -1 +1,38 @@ -kie_horizontal_rgb_fullcolor_default \ No newline at end of file + + + + + + + kie_horizontal_rgb_fullcolor_default + + + + + + + + + + + + diff --git a/examples/base64png-editor-chrome-extension/README.md b/examples/base64png-editor-chrome-extension/README.md index 343f808c9b5..c9662c6b32e 100644 --- a/examples/base64png-editor-chrome-extension/README.md +++ b/examples/base64png-editor-chrome-extension/README.md @@ -1,3 +1,20 @@ + + ## Base64 PNG Editor - Chrome Extension You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8a-%e2%80%8ahow-to-create-a-chrome-extension-for-a-custom-editor.html) a step-by-step tutorial of how this Chrome Extension was built. @@ -23,3 +40,33 @@ pnpm serve-envelope It will run a server exposing your `dist/` folder on the `localhost:9000`. It's necessary to access your `localhost:9000`, and enable access to it. This step is required, so the extension can access the envelope located on `dist/envelope/index.html`. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/base64png-editor-chrome-extension/static/envelope/index.html b/examples/base64png-editor-chrome-extension/static/envelope/index.html index ae14109cb28..9afb4080e4f 100644 --- a/examples/base64png-editor-chrome-extension/static/envelope/index.html +++ b/examples/base64png-editor-chrome-extension/static/envelope/index.html @@ -1,3 +1,21 @@ + diff --git a/examples/base64png-editor-chrome-extension/static/resources/kie_icon_rgb_fullcolor_default.svg b/examples/base64png-editor-chrome-extension/static/resources/kie_icon_rgb_fullcolor_default.svg index 4cef559984a..e0c8c45f283 100644 --- a/examples/base64png-editor-chrome-extension/static/resources/kie_icon_rgb_fullcolor_default.svg +++ b/examples/base64png-editor-chrome-extension/static/resources/kie_icon_rgb_fullcolor_default.svg @@ -1 +1,35 @@ -kie_icon_rgb_fullcolor_default \ No newline at end of file + + + + + + + kie_icon_rgb_fullcolor_default + + + + + + + + + diff --git a/examples/base64png-editor-vscode-extension/README.md b/examples/base64png-editor-vscode-extension/README.md index 31c9a41d849..ef8dd0ce8ab 100644 --- a/examples/base64png-editor-vscode-extension/README.md +++ b/examples/base64png-editor-vscode-extension/README.md @@ -1,3 +1,20 @@ + + ## Base64 PNG Editor - Visual Studio Code Extension You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8a-%e2%80%8ahow-to-create-a-vs-code-extension-for-the-custom-editor.html) a step-by-step tutorial of how this VS Code Extension was built. @@ -28,3 +45,33 @@ code . - Installing VSIX The `pnpm build:prod` generate a vsix file on the `/dist` folder. Open your VS Code, and install it. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/base64png-editor/README.md b/examples/base64png-editor/README.md index f8be5b71f90..62b44924c7c 100644 --- a/examples/base64png-editor/README.md +++ b/examples/base64png-editor/README.md @@ -1,3 +1,20 @@ + + # Base64 PNG Editor You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8a-%e2%80%8ahow-to-create-a-custom-editor-in-a-react-application.html) a step-by-step tutorial of how this custom Editor was built. @@ -5,3 +22,33 @@ You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8 This package is a Custom Editor made with React, which enables you to edit a `.base64png` file. A `.base64png` file is a PNG image converted to base64 without its headers (e.g. data:image/png;base64). + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/commit-message-validation-service/README.md b/examples/commit-message-validation-service/README.md index fc2aba4d341..8f074021ab4 100644 --- a/examples/commit-message-validation-service/README.md +++ b/examples/commit-message-validation-service/README.md @@ -1,3 +1,20 @@ + + ## Commit message validation service Provides a simple service to check for patterns in a commit message. @@ -29,3 +46,33 @@ Provides a simple service to check for patterns in a commit message. "reasons": []string | undefined } ``` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/dashbuilder/kitchensink/Kitchensink.dash.yml b/examples/dashbuilder/kitchensink/Kitchensink.dash.yml index 12981cdd169..23f64a8a26a 100644 --- a/examples/dashbuilder/kitchensink/Kitchensink.dash.yml +++ b/examples/dashbuilder/kitchensink/Kitchensink.dash.yml @@ -1,3 +1,22 @@ +# +# 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. +# + pages: - name: index properties: diff --git a/examples/drools-process-usertasks-quarkus-example/README.md b/examples/drools-process-usertasks-quarkus-example/README.md new file mode 100644 index 00000000000..f7e844ad2ba --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/README.md @@ -0,0 +1,262 @@ + + +# Process user task orchestration + +## Description + +A quickstart project shows very typical user task orchestration. It comes with two tasks assigned +to human actors via groups assignments - `managers`. So essentially anyone who is a member of that +group can act on the tasks. Though this example applies four eye principle which essentially means +that user who approved first task cannot approve second one. So there must be always at least two +distinct manager involved. + +This example shows + +- working with user tasks +- four eye principle with user tasks + +

+ +- Diagram Properties (top) +

+ +- Diagram Properties (bottom) +

+ +- First Line Approval (top) +

+ +- First Line Approval (bottom) +

+ +- First Line Approval (Assignments) +

+ +- Second Line Approval +

+ +- Second Line Approval (Assignments) +

+ +## Build and run + +### Prerequisites + +You will need: + +- Java 17+ installed +- Environment variable JAVA_HOME set accordingly +- Maven 3.9.6+ installed + +When using native image compilation, you will also need: + +- GraalVM 19.3+ installed +- Environment variable GRAALVM_HOME set accordingly +- GraalVM native image needs as well native-image extension: https://www.graalvm.org/reference-manual/native-image/ +- Note that GraalVM native image compilation typically requires other packages (glibc-devel, zlib-devel and gcc) to be installed too, please refer to GraalVM installation documentation for more details. + +### Compile and Run in Local Dev Mode + +```sh +mvn clean compile quarkus:dev +``` + +NOTE: With dev mode of Quarkus you can take advantage of hot reload for business assets like processes, rules, decision tables and java code. No need to redeploy or restart your running application. + +### Package and Run in JVM mode + +```sh +mvn clean package +java -jar target/quarkus-app/quarkus-run.jar +``` + +or on windows + +```sh +mvn clean package +java -jar target\quarkus-app\quarkus-run.jar +``` + +### Package and Run using Local Native Image + +Note that this requires GRAALVM_HOME to point to a valid GraalVM installation + +```sh +mvn clean package -Pnative +``` + +To run the generated native executable, generated in `target/`, execute + +```sh +./target/process-usertasks-quarkus-runner +``` + +### OpenAPI (Swagger) documentation + +[Specification at swagger.io](https://swagger.io/docs/specification/about/) + +You can take a look at the [OpenAPI definition](http://localhost:8080/openapi?format=json) - automatically generated and included in this service - to determine all available operations exposed by this service. For easy readability you can visualize the OpenAPI definition file using a UI tool like for example available [Swagger UI](https://editor.swagger.io). + +In addition, various clients to interact with this service can be easily generated using this OpenAPI definition. + +When running in either Quarkus Development or Native mode, we also leverage the [Quarkus OpenAPI extension](https://quarkus.io/guides/openapi-swaggerui#use-swagger-ui-for-development) that exposes [Swagger UI](http://localhost:8080/swagger-ui/) that you can use to look at available REST endpoints and send test requests. + +### Submit a request to start new approval + +To make use of this application it is as simple as putting a sending request to `http://localhost:8080/approvals` with following content + +```json +{ + "traveller": { + "firstName": "John", + "lastName": "Doe", + "email": "jon.doe@example.com", + "nationality": "American", + "address": { + "street": "main street", + "city": "Boston", + "zipCode": "10005", + "country": "US" + } + } +} +``` + +Complete curl command can be found below: + +```sh +curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"traveller" : { "firstName" : "John", "lastName" : "Doe", "email" : "jon.doe@example.com", "nationality" : "American","address" : { "street" : "main street", "city" : "Boston", "zipCode" : "10005", "country" : "US" }}}' http://localhost:8080/approvals +``` + +### Show active approvals + +```sh +curl -H 'Content-Type:application/json' -H 'Accept:application/json' http://localhost:8080/approvals +``` + +### Show tasks + +```sh +curl -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8080/approvals/{uuid}/tasks?user=admin&group=managers' +``` + +where `{uuid}` is the id of the given approval instance + +### Complete first line approval task + +```sh +curl -X POST -d '{"approved" : true}' -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8080/approvals/{uuid}/firstLineApproval/{tuuid}?user=admin&group=managers' +``` + +where `{uuid}` is the id of the given approval instance and `{tuuid}` is the id of the task instance + +### Show tasks + +```sh +curl -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8080/approvals/{uuid}/tasks?user=admin&group=managers' +``` + +where `{uuid}` is the id of the given approval instance + +This should return empty response as the admin user was the first approver and by that can't be assigned to another one. + +Repeating the request with another user will return task + +```sh +curl -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8080/approvals/{uuid}/tasks?user=john&group=managers' +``` + +### Complete second line approval task + +```sh +curl -X POST -d '{"approved" : true}' -H 'Content-Type:application/json' -H 'Accept:application/json' 'http://localhost:8080/approvals/{uuid}/secondLineApproval/{tuuid}?user=john&group=managers' +``` + +where `{uuid}` is the id of the given approval instance and `{tuuid}` is the id of the task instance + +This completes the approval and returns approvals model where both approvals of first and second line can be found, +plus the approver who made the first one. + +```json +{ + "approver": "admin", + "firstLineApproval": true, + "id": "2eeafa82-d631-4554-8d8e-46614cbe3bdf", + "secondLineApproval": true, + "traveller": { + "address": { + "city": "Boston", + "country": "US", + "street": "main street", + "zipCode": "10005" + }, + "email": "jon.doe@example.com", + "firstName": "John", + "lastName": "Doe", + "nationality": "American" + } +} +``` + +You should see a similar message after performing the second line approval after the curl command + +```json +{ + "id": "f498de73-e02d-4829-905e-2f768479a4f1", + "approver": "admin", + "firstLineApproval": true, + "secondLineApproval": true, + "traveller": { + "firstName": "John", + "lastName": "Doe", + "email": "jon.doe@example.com", + "nationality": "American", + "address": { "street": "main street", "city": "Boston", "zipCode": "10005", "country": "US" } + } +} +``` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties.png b/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties.png new file mode 100644 index 00000000000..585c58f583b Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties3.png b/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties3.png new file mode 100644 index 00000000000..df43a371afa Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/diagramProperties3.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask.png b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask.png new file mode 100644 index 00000000000..b705a63b367 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask2.png b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask2.png new file mode 100644 index 00000000000..65c7dc08469 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTask2.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTaskAssignments.png b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTaskAssignments.png new file mode 100644 index 00000000000..0bbadf0ac59 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/firstLineApprovalUserTaskAssignments.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/process.png b/examples/drools-process-usertasks-quarkus-example/docs/images/process.png new file mode 100644 index 00000000000..78eadedc8c0 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/process.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTask.png b/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTask.png new file mode 100644 index 00000000000..bd3ce5bcce5 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTask.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTaskAssignments.png b/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTaskAssignments.png new file mode 100644 index 00000000000..9ac27a7af04 Binary files /dev/null and b/examples/drools-process-usertasks-quarkus-example/docs/images/secondLineApprovalUserTaskAssignments.png differ diff --git a/examples/drools-process-usertasks-quarkus-example/env/index.js b/examples/drools-process-usertasks-quarkus-example/env/index.js new file mode 100644 index 00000000000..3dd70a342fc --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/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 { + droolsProcessUsertasksQuarkusExample: { + version: require("../package.json").version, + }, + }; + }, +}); diff --git a/examples/drools-process-usertasks-quarkus-example/install.js b/examples/drools-process-usertasks-quarkus-example/install.js new file mode 100644 index 00000000000..9102bb3b499 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/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.droolsProcessUsertasksQuarkusExample.version} +`); diff --git a/examples/drools-process-usertasks-quarkus-example/package.json b/examples/drools-process-usertasks-quarkus-example/package.json new file mode 100644 index 00000000000..74b65a00968 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/package.json @@ -0,0 +1,45 @@ +{ + "private": true, + "name": "@kie-tools-examples/drools-process-usertasks-quarkus-example", + "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-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:dev:darwin:linux": "mvn clean compile -DskipTests", + "build:dev:win32": "pnpm powershell \"mvn clean compile -DskipTests \"", + "build:prod": "pnpm lint && run-script-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:prod:darwin:linux": "mvn clean compile -DskipTests=$(build-env tests.run --not) -Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures)", + "build:prod:win32": "pnpm powershell \"mvn clean compile `-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 compile quarkus:dev -DskipTests", + "quarkus:dev:win32": "mvn clean compile quarkus:dev -DskipTests", + "start": "pnpm quarkus:dev" + }, + "dependencies": { + "@kie-tools/jbpm-quarkus-devui": "workspace:*", + "@kie-tools/maven-base": "workspace:*" + }, + "devDependencies": { + "@kie-tools/maven-config-setup-helper": "workspace:*", + "@kie-tools/root-env": "workspace:*", + "run-script-os": "^1.1.6" + }, + "kieTools": { + "requiredPreinstalledCliCommands": [ + "java", + "mvn" + ] + } +} \ No newline at end of file diff --git a/examples/drools-process-usertasks-quarkus-example/pom.xml b/examples/drools-process-usertasks-quarkus-example/pom.xml new file mode 100644 index 00000000000..3ab00e2fc76 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/pom.xml @@ -0,0 +1,109 @@ + + + + 4.0.0 + + org.kie + kie-tools-maven-base + ${revision} + ./node_modules/@kie-tools/maven-base/pom.xml + + drools-process-usertasks-quarkus-example + Kie-Tools Example :: Process with Usertasks Quarkus + Kogito user tasks orchestration - Quarkus + + + + + org.jbpm + jbpm-quarkus-devui-bom + ${project.version} + pom + import + + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + org.jbpm + jbpm-with-drools-quarkus + + + org.jbpm + jbpm-quarkus-devui + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-smallrye-health + + + org.kie + kie-addons-quarkus-persistence-rocksdb + + + org.kie + kie-addons-quarkus-process-svg + + + + ${project.artifactId} + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + + diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.jvm b/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.jvm new file mode 100644 index 00000000000..ec46ab84c71 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.jvm @@ -0,0 +1,26 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the docker image run: +# +# mvn package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/process-usertasks-quarkus-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/process-usertasks-quarkus-jvm +# +### +FROM fabric8/java-alpine-openjdk11-jre +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV AB_ENABLED=jmx_exporter + +COPY target/quarkus-app/lib/ /deployments/lib/ +COPY target/quarkus-app/*.jar /deployments/ +COPY target/quarkus-app/app/ /deployments/app/ +COPY target/quarkus-app/quarkus/ /deployments/quarkus/ + +ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.native b/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.native new file mode 100644 index 00000000000..b508cbb1d38 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/docker/Dockerfile.native @@ -0,0 +1,22 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the docker image run: +# +# mvn package -Pnative -Dnative-image.docker-build=true +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/kogito-infinispan-persistence-quarkus . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/kogito-infinispan-persistence-quarkus +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal +WORKDIR /work/ +COPY target/*-runner /work/application +RUN chmod 775 /work +EXPOSE 8080 +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Address.java b/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Address.java new file mode 100644 index 00000000000..662dfa9b9f7 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Address.java @@ -0,0 +1,76 @@ +/* + * 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.acme.travels; + +public class Address { + + private String street; + private String city; + private String zipCode; + private String country; + + public Address() { + + } + + public Address(String street, String city, String zipCode, String country) { + super(); + this.street = street; + this.city = city; + this.zipCode = zipCode; + this.country = country; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getZipCode() { + return zipCode; + } + + public void setZipCode(String zipCode) { + this.zipCode = zipCode; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + @Override + public String toString() { + return "Address [street=" + street + ", city=" + city + ", zipCode=" + zipCode + ", country=" + country + "]"; + } +} diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Traveller.java b/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Traveller.java new file mode 100644 index 00000000000..c24685803d2 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/java/org/acme/travels/Traveller.java @@ -0,0 +1,88 @@ +/* + * 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.acme.travels; + +public class Traveller { + + private String firstName; + private String lastName; + private String email; + private String nationality; + private Address address; + + public Traveller() { + + } + + public Traveller(String firstName, String lastName, String email, String nationality, Address address) { + super(); + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + this.nationality = nationality; + this.address = address; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getNationality() { + return nationality; + } + + public void setNationality(String nationality) { + this.nationality = nationality; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } + + @Override + public String toString() { + return "Traveller [firstName=" + firstName + ", lastName=" + lastName + ", email=" + email + ", nationality=" + + nationality + ", address=" + address + "]"; + } + +} diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/processSVG/approvals.svg b/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/processSVG/approvals.svg new file mode 100644 index 00000000000..9284cb85b2d --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/processSVG/approvals.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + End + + + + + + + + + + + + + + + + + + + + + + + StartProcess + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Second Line + + + Approval + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + First Line + + + Approval + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/resources/index.html b/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/resources/index.html new file mode 100644 index 00000000000..8755eeba651 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/resources/META-INF/resources/index.html @@ -0,0 +1,172 @@ + + + + + + kogito-infinispan-persistence-quarkus - 1.0-SNAPSHOT + + + + + +
+
+

Congratulations, you have created a new Quarkus application.

+ +

Why do you see this?

+ +

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

+ +

What can I do from here?

+ +

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

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

How do I get rid of this page?

+

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

+
+
+
+

Application

+
    +
  • GroupId: org.acme
  • +
  • ArtifactId: kogito-infinispan-persistence-quarkus
  • +
  • Version: 1.0-SNAPSHOT
  • +
  • Quarkus Version: 0.19.1
  • +
+
+ +
+
+ + diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/resources/application.properties b/examples/drools-process-usertasks-quarkus-example/src/main/resources/application.properties new file mode 100644 index 00000000000..64a5d9c9500 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/resources/application.properties @@ -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. +# + +# Packaging +# quarkus.package.type=fast-jar + +quarkus.smallrye-openapi.path=/docs/openapi.json +quarkus.swagger-ui.always-include=true + +jbpm.devui.users.admin.groups=managers +jbpm.devui.users.manager.groups=managers +jbpm.devui.users.john.groups=managers \ No newline at end of file diff --git a/examples/drools-process-usertasks-quarkus-example/src/main/resources/org/acme/travels/approval.bpmn b/examples/drools-process-usertasks-quarkus-example/src/main/resources/org/acme/travels/approval.bpmn new file mode 100644 index 00000000000..5ec0a0d0ae9 --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/main/resources/org/acme/travels/approval.bpmn @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _9EAFE6C1-69B4-4908-B764-EF3C4A55BEE3 + _C13522F1-230A-4C26-B5A9-533A5D9FEE9D + + + + + + + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_TaskNameInputX + _8B62D3CA-5D03-4B2B-832B-126469288BB4_travellerInputX + _8B62D3CA-5D03-4B2B-832B-126469288BB4_SkippableInputX + _8B62D3CA-5D03-4B2B-832B-126469288BB4_GroupIdInputX + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_ActorIdOutputX + _8B62D3CA-5D03-4B2B-832B-126469288BB4_approvedOutputX + + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_TaskNameInputX + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_TaskNameInputX + + + + traveller + _8B62D3CA-5D03-4B2B-832B-126469288BB4_travellerInputX + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_SkippableInputX + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_SkippableInputX + + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_GroupIdInputX + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_GroupIdInputX + + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_ActorIdOutputX + approver + + + _8B62D3CA-5D03-4B2B-832B-126469288BB4_approvedOutputX + firstLineApproval + + + + manager + + + + + + + + + + _C13522F1-230A-4C26-B5A9-533A5D9FEE9D + _078F46FB-B7A1-4DBB-BE9A-75C7CB0CCD03 + + + + + + + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_TaskNameInputX + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_ExcludedOwnerIdInputX + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_travellerInputX + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_SkippableInputX + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_GroupIdInputX + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_approvedOutputX + + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_TaskNameInputX + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_TaskNameInputX + + + + approver + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_ExcludedOwnerIdInputX + + + traveller + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_travellerInputX + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_SkippableInputX + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_SkippableInputX + + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_GroupIdInputX + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_GroupIdInputX + + + + _0DBFABE8-92B0-46E6-B52E-A9593AFA4371_approvedOutputX + secondLineApproval + + + + + + + + + _9EAFE6C1-69B4-4908-B764-EF3C4A55BEE3 + + + + + + + + _078F46FB-B7A1-4DBB-BE9A-75C7CB0CCD03 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _F0jB8En5EeqlfsIhq1UCRQ + _F0jB8En5EeqlfsIhq1UCRQ + + diff --git a/examples/drools-process-usertasks-quarkus-example/src/test/java/org/acme/travels/quarkus/ApprovalsProcessTest.java b/examples/drools-process-usertasks-quarkus-example/src/test/java/org/acme/travels/quarkus/ApprovalsProcessTest.java new file mode 100644 index 00000000000..a5a41285c3d --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/test/java/org/acme/travels/quarkus/ApprovalsProcessTest.java @@ -0,0 +1,144 @@ +/* + * 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.acme.travels.quarkus; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.acme.travels.Address; +import org.acme.travels.Traveller; +import org.jbpm.process.instance.impl.humantask.HumanTaskTransition; +import org.jbpm.process.instance.impl.humantask.phases.Claim; +import org.jbpm.process.instance.impl.workitem.Complete; +import org.junit.jupiter.api.Test; +import org.kie.kogito.Model; +import org.kie.kogito.auth.IdentityProviders; +import org.kie.kogito.auth.SecurityPolicy; +import org.kie.kogito.process.Process; +import org.kie.kogito.process.ProcessInstance; +import org.kie.kogito.process.WorkItem; + +import io.quarkus.test.junit.QuarkusTest; + +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@QuarkusTest +public class ApprovalsProcessTest { + + @Named("approvals") + @Inject + Process approvalsProcess; + + @Test + public void testApprovalProcess() { + + assertNotNull(approvalsProcess); + + Model m = approvalsProcess.createModel(); + Map parameters = new HashMap<>(); + parameters.put("traveller", new Traveller("John", "Doe", "john.doe@example.com", "American", new Address("main street", "Boston", "10005", "US"))); + m.fromMap(parameters); + + ProcessInstance processInstance = approvalsProcess.createInstance(m); + processInstance.start(); + assertEquals(org.kie.api.runtime.process.ProcessInstance.STATE_ACTIVE, processInstance.status()); + + SecurityPolicy policy = SecurityPolicy.of(IdentityProviders.of("admin", Collections.singletonList("managers"))); + + processInstance.workItems(policy); + + List workItems = processInstance.workItems(policy); + assertEquals(1, workItems.size()); + Map results = new HashMap<>(); + results.put("approved", true); + processInstance.completeWorkItem(workItems.get(0).getId(), results, policy); + + workItems = processInstance.workItems(policy); + assertEquals(0, workItems.size()); + + policy = SecurityPolicy.of(IdentityProviders.of("john", Collections.singletonList("managers"))); + + processInstance.workItems(policy); + + workItems = processInstance.workItems(policy); + assertEquals(1, workItems.size()); + + results.put("approved", false); + processInstance.completeWorkItem(workItems.get(0).getId(), results, policy); + assertEquals(org.kie.api.runtime.process.ProcessInstance.STATE_COMPLETED, processInstance.status()); + + Model result = (Model) processInstance.variables(); + assertEquals(4, result.toMap().size()); + assertEquals(result.toMap().get("approver"), "admin"); + assertEquals(result.toMap().get("firstLineApproval"), true); + assertEquals(result.toMap().get("secondLineApproval"), false); + } + + @Test + public void testApprovalProcessViaPhases() { + + assertNotNull(approvalsProcess); + + Model m = approvalsProcess.createModel(); + Map parameters = new HashMap<>(); + parameters.put("traveller", new Traveller("John", "Doe", "john.doe@example.com", "American", new Address("main street", "Boston", "10005", "US"))); + m.fromMap(parameters); + + ProcessInstance processInstance = approvalsProcess.createInstance(m); + processInstance.start(); + assertEquals(org.kie.api.runtime.process.ProcessInstance.STATE_ACTIVE, processInstance.status()); + + SecurityPolicy policy = SecurityPolicy.of(IdentityProviders.of("admin", Collections.singletonList("managers"))); + + processInstance.workItems(policy); + + List workItems = processInstance.workItems(policy); + assertEquals(1, workItems.size()); + + processInstance.transitionWorkItem(workItems.get(0).getId(), new HumanTaskTransition(Claim.ID, null, policy)); + processInstance.transitionWorkItem(workItems.get(0).getId(), new HumanTaskTransition(Complete.ID, Collections.singletonMap("approved", true), policy)); + + workItems = processInstance.workItems(policy); + assertEquals(0, workItems.size()); + + policy = SecurityPolicy.of(IdentityProviders.of("john", Collections.singletonList("managers"))); + + processInstance.workItems(policy); + + workItems = processInstance.workItems(policy); + assertEquals(1, workItems.size()); + + processInstance.transitionWorkItem(workItems.get(0).getId(), new HumanTaskTransition(Claim.ID, null, policy)); + processInstance.transitionWorkItem(workItems.get(0).getId(), new HumanTaskTransition(Complete.ID, Collections.singletonMap("approved", false), policy)); + + assertEquals(org.kie.api.runtime.process.ProcessInstance.STATE_COMPLETED, processInstance.status()); + + Model result = (Model) processInstance.variables(); + assertEquals(4, result.toMap().size()); + assertEquals(result.toMap().get("approver"), "admin"); + assertEquals(result.toMap().get("firstLineApproval"), true); + assertEquals(result.toMap().get("secondLineApproval"), false); + } +} diff --git a/examples/drools-process-usertasks-quarkus-example/src/test/resources/application.properties b/examples/drools-process-usertasks-quarkus-example/src/test/resources/application.properties new file mode 100644 index 00000000000..e764de0f97d --- /dev/null +++ b/examples/drools-process-usertasks-quarkus-example/src/test/resources/application.properties @@ -0,0 +1,23 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Quarkus +quarkus.http.test-port=0 +kogito.persistence.rocksdb.clean=true +kogito.persistence.rocksdb.data.dir=pepe \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/README.md b/examples/jbpm-compact-architecture-example/README.md new file mode 100644 index 00000000000..c754d55ceb6 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/README.md @@ -0,0 +1,555 @@ +# jBPM Compact Architecture Quarkus Example + +## Description + +This example showcases a basic implementation of the **Hiring** process that drives a _Candidate_ through different +interviews until he gets hired. + +This quickstart project shows a simple example user task orchestration including the use of DMN decisions to +generate the candidate offer and tºimers to skip User Tasks. + +This example also demonstrates how to configure the whole _Kogito_ environment using the new _Compact Architecture_ that +enable simplifying the communication among _Kogito_ services removing the need of events (Kafka/HTTP) between them. This can +be achieved using the following _Quarkus_ addons: + +- `kogito-addons-quarkus-data-index-persistence-postgresql`: enables the _Kogito Runtime_ persisting directly into the + _Data-Index_ database. +- `kogito-addons-quarkus-jobs`: enables collocating the _Jobs Service_ inside the _Kogito Runtime_. + +## The Java models + +The **Hiring** process uses two POJOs to handle the process data, both of them can be found in the _org.kie.kogito.hr_ package. + +The `CandidateData` POJO is the input of the process. It represents the person that wants to get the job. + +```java +public class CandidateData { + + private String name; // Name of the candidate + private String lastName; // Last name of the candidate + private String email; // Email of the candidate + private Integer experience; // Years of experience + private List skills; // List of technical skills + + // Constructors, setters, getters... +} +``` + +The `Offer` POJO is the output of the process and represents the job offer that will be sent to the candidate. +It will be automatically calculated during the process execution depending on the candidate years of experience & skills. + +```java +public class Offer { + + private String category; // Job category based on the candidate experience + private Integer salary; // Salary based on the candidate experience and skills + + // Constructors, setters, getters... +} +``` + +## The _New Hiring Offer_ DMN + +This example makes use of the _New Hiring Offer_ DMN to generate a base offer for the `Candidate`. The DMN looks like this: + +In this simple DMN we have an `Offer` _Decision_, that will generate the candidate offer, which +has a requirement of a `CandidateData` _Input Data_. + +
+
+ DMN Diagram +
New Hiring Offer DMN diagram
+
+
+ +The DMN defines the following data types (`tCandidateData` & `tOffer` ) matching the POJOs defined in the project +(`CandidateData.java` & `Offer.java`): + +
+
+ DMN Type Definitions +
New Hiring Offer DMN types
+
+
+ +As expected, `CandidateData` _Input Data_ & `Offer` _Decision_ have a `tCandidateData` data + +The `Offer` decision uses the following _Boxed Expression_ to generate the `tOffer`: + +
+
+ DMN Decision +
New Hiring Offer DMN decision
+
+
+ +## The Hiring Process + +### Process variables + +The process handles the following _Variables_: + +| Variable | Type | Tags | Description | +| ----------------- | --------------------------------- | ------------ | ------------------------------------------------- | +| **candidateData** | `org.kie.kogito.hr.CandidateData` | **input** | The candidate data | +| **offer** | `org.kie.kogito.hr.Offer` | **output** | The generated candidate offer | +| **hr_approval** | `Boolean` | **internal** | Determines that HR department approves the hiring | +| **it_approval** | `Boolean` | **internal** | Determines that IT department approves the hiring | + +### The BPMN Process + +
+
+ Hiring Process Diagram +
Hiring Process Diagram
+
+
+ +The process starts receiving the `CandidateData` as an input and storing it into the `candidateData` variable, and if the +candidate meets two minimal requirements, the process will continue and reach the **Generate base offer**, otherwise the +candidate application will be denied and the process will complete without sending the `offer` to the candidate. + +The **Generate base offer** is a _Business Rule Task_ that will use the _New Hiring Offer_ decision defined in the +`NewHiringOffer.dmn` to generate the an `Offer` based on the candidate experience and skills. The task takes the `candidateData` +as an input and will produce an instance of `org.kie.kogito.hr.Offer` that will be stored in the `offer` variable. + +
+
+ Offer assignments +
Generate base Offer data assignments
+
+
+ +After the `offer` has been generated, the process will jump into the **HR Interview** _User Task_, where the candidate we'll +be interviewed by the _HR_ department. The task takes the `candidateData` and `offer` as inputs and as an output will produce +the `hr_approve` boolean and an updated `offer`. + +
+
+ HR Interview assignments +
HR Interviewr task data assignments
+
+
+ +The **HR Interview** _User Task_ also has a _Boundary Timer Event_ that will prevent the task to delay and will cancel the +task after certain time (for example purpose just 3 minutes). This _Boundary Timer Event_ will schedule a Job in the Jobs Service +that when trigger will notify the _Kogito Runtime_ to cancel the task and deny the application. + +If **HR Interview** successfully completed, the process will jump into the **IT Interview** _User Task_. Again the candidate +we'll have a second interview with the _IT_ department. Again, this task will take the `candidateData` and `offer` as inputs +but as an output will produce the `it_approve` boolean. + +
+
+ IT Interview assignments +
IT Interviewr task data assignments
+
+
+ +Once both tasks are completed, if the candidate got the approvals from _HR_ & _IT_ (both `hr_interview` & `hr_interview` being true) +the process will jump into the **Send Offer to Candidate** _Script Task_ that will notify the candidate about the offer +and the process will end. + +> **NOTE:** for simplicity, all the _User Tasks_ in this example are assigned to the _jdoe_ user present in the keycloak configuration + +## Running the example + +### Prerequisites + +- Java 17+ installed +- Environment variable JAVA_HOME set accordingly +- Maven 3.9.3+ installed +- Docker and Docker Compose to run the required example infrastructure. + +And when using native image compilation, you will also need: + +- GraalVM 20.3+ installed +- Environment variable GRAALVM_HOME set accordingly +- GraalVM native image needs as well native-image extension: https://www.graalvm.org/reference-manual/native-image/ +- Note that GraalVM native image compilation typically requires other packages (glibc-devel, zlib-devel and gcc) to be installed too, please refer to GraalVM installation documentation for more details. + +### Infrastructure Services + +This quickstart provides a docker compose template that starts all the required services. This setup ensures that all services are connected with a default configuration. + +- PostgreSQL: 5432 +- Management Console: 8280 +- Task Console: 8380 +- Keycloak: 8480 +- PgAdmin: 8055 +- Kogito Example Service: 8080 + +To help bootstraping the Infrastructure Services, the example provides the `startServices.sh` script inside the _docker-compose_ +folder. + +> **_NOTE_**: the docker compose template requires using _extra_hosts_ to allow the services use the host network, this may +> carry some issues if you are using a **podman** version older than **4.7**. + +### Building & Running the example + +To build the example, on a Terminal, run the following command: + +```shell +mvn clean package -Pcontainer +``` + +This will build the example quarkus application and create a Docker image that will be started in the `docker-compose` template. + +To execute the full example (including consoles), open a Terminal and run the following command inside the `docker-compose` folder: + +```shell +sh startServices.sh +``` + +> **_IMPORTANT:_** if you are running this example on MacOs and you are not using **Docker Desktop**, please append +> the following entry in your `/etc/hosts` file to enable a good communication between al components. +> +> ``` +> 127.0.0.1 kubernetes.docker.internal +> ``` + +Additionally, if you want to start only the example and the minimal Infrastructure Services (PostgreSQL, Data-Index and Jobs Service), +you can run the same `startServices.sh` script but passing the `example` argument + +```shell +sh startServices.sh example +``` + +> **_NOTE:_** starting the Infrastructure Services, please consider running a `mvn clean package -Pcontainer` +> command on the project root before running the `startServices.sh` script for the first time or any time you modify the project. + +### Running the example in Development mode + +To run the example in Development mode, just run the following command in a Terminal: + +```shell +mvn clean package quarkus:dev -Pdevelopment +``` + +The Development Mode will embed all the needed Infrastructure Services (PostgreSQL, Data-Index & Jobs Service) and won't +require any extra step. + +The `development` profile includes the **Runtime Tools Quarkus Extension** that exposes a new section in the **Quarkus Dev-UI** +unifying the **Management Console** & **Task Console** functionalities. **Quarkus Dev-UI** is available at http://localhost:8080/q/dev + +> **_NOTE:_** For more information about how to work with Kogito Runtime Tools Quarkus Extension, please refer to the [Kogito Documentation](https://docs.kogito.kie.org/latest/html_single/#con-runtime-tools-dev-ui_kogito-developing-process-services) page. + +### Starting an instance of the Hiring Process + +Once the service is up and running you can make use of the **Hiring** application by a sending request to `http://localhost:8080/hiring`. + +Sending the following valid `CandidateData` will start a process instance that will land into the _HR Interview_ task: + +```json +{ + "candidateData": { + "name": "Jon", + "lastName": "Snow", + "email": "jon@snow.org", + "experience": 5, + "skills": ["Java", "Kogito", "Fencing"] + } +} +``` + +In a Terminal you can execute this curl command to start a **Hiring** process: + +```bash +curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST http://localhost:8080/hiring -d '{"candidateData": { "name": "Jon", "lastName": "Snow", "email": "jon@snow.org", "experience": 5, "skills": ["Java", "Kogito", "Fencing"]}}' +``` + +If everything went well you may get a response like: + +```json +{ + "id": "628e679f-4deb-4abc-9f28-668914c64ef9", + "offer": { + "category": "Senior Software Engineer", + "salary": 40450 + } +} +``` + +In the server log You may find a trace like: + +``` +New Hiring has been created for candidate: Jon Snow +################################### +Generated offer for candidate: Jon Snow +Job Category: Senior Software Engineer +Base salary: 40450 +################################### +``` + +Use the following `CandidateData` that don't match the minimal candidate requirements, to start a process that will automatically end: + +```json +{ + "candidateData": { + "name": "Jon", + "lastName": "Snow", + "email": "jon@snow.org", + "experience": 0, + "skills": [] + } +} +``` + +In a Terminal you can execute this curl command to start a **Hiring** process: + +```bash +curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST http://localhost:8080/hiring -d '{"candidateData": { "name": "Jon", "lastName": "Snow", "email": "jon@snow.org", "experience": 0, "skills": []}}' +``` + +If everything went well you may get a response like: + +```json +{ + "id": "3659601a-bb59-458d-859e-7892621ad5b7", + "offer": null +} +``` + +In the server log You may find a trace like: + +``` +New Hiring has been created for candidate: Jon Snow +################################### +Candidate Jon Snow don't meet the requirements for the position but we'll keep it on records for the future! +################################### +``` + +### Using Keycloak as Authentication Server + +In this Quickstart we'll be using [Keycloak](https://www.keycloak.org/) as _Authentication Server_. It will be started as a part of the project _Infrastructure Services_, you can check the configuration on the project [docker-compose.yml](docker-compose/docker-compose.yml) in [docker-compose](docker-compose) folder. + +It will install the _Kogito Realm_ that comes with a predefined set of users: + +| Login | Password | Roles | +| ----- | -------- | ------------------- | +| admin | admin | _admin_, _managers_ | +| alice | alice | _user_ | +| jdoe | jdoe | _managers_ | + +Once Keycloak is started, you should be able to access your _Keycloak Server_ at [localhost:8480/auth](http://localhost:8480/auth) with _admin_ user. + +> **_NOTE:_** This example uses keycloak authentication to enable security only in the consoles not in runtime. + +### Using the Kogito Runtime Consoles to interact with the Hiring Process + +The following _step-by-step_ guides will show how to take advantage of both _Kogito Management Console_ and _Kogito Task Console_ +to operate with the instances of _Hiring_ process. + +To be able to follow the guides, please make sure that the example has been built using the `container` and all the _Infractructure Services_ +are started as explained in the [Building & Running the example](#building--running-the-example) section. + +> **_NOTE_**: For more information about how to operate with the _Kogito Runtime Consoles_, please refer to the +> [Management Console](https://docs.kogito.kie.org/latest/html_single/#con-management-console_kogito-developing-process-services) & [Task Console](https://docs.kogito.kie.org/latest/html_single/#con-task-console_kogito-developing-process-services) documentation. + +#### Show active Hiring process instance at Kogito Management Console + +_Kogito Management Console_ is the tool that enables the user to view and administrate process instances in our _Kogito application_. + +In this guide we'll see how to use the _Kogito Management Console_ to view the state of the Hiring process instances. + +1. With the example built and all the _Infrastructure Services_ running, let's start an instance of the _Hiring_ process. To do so, in a Terminal just run: + + ```bash + curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST http://localhost:8080/hiring -d '{"candidateData": { "name": "Jon", "lastName": "Snow", "email": "jon@snow.org", "experience": 5, "skills": ["Java", "Kogito", "Fencing"]}}' + ``` + + If everything went well, you should get a response like: + + ```json + { + "id": "064a6372-b5bb-4eff-a059-d7b24d4ac64a", + "offer": { "category": "Senior Software Engineer", "salary": 40450 } + } + ``` + + Which indicates that a new process instance with id **064a6372-b5bb-4eff-a059-d7b24d4ac64a** has been started. + +2. Now let's check the process instance state with the _Kogito Management Console_. To do so, in your browser navigate + to http://localhost:8280 and log in using any of the users specified in the [Using Keycloak as Authentication Server](#using-keycloak-as-authentication-server). + + Once you are logged in, you should be redirected to the **Process Instances** page where you should be able to see + the started process instance in active state. + +
+
+ Process List +
Process List in Kogito Management Console
+
+
+ +3. Click on the instance **id** to navigate into the _Process Details_ page. In there you'll be able to see different panels displaying relevant information about the instance state, such as the _Diagram_, _Timeline_, _Details_, _Variables_, _Jobs_... + +
+
+ Process Details +
Process Instance Details page
+
+
+ + Now check the **Diagram** panel, in there you'll se the instance execution path. Notice that it's stopped _HR Interview_ _User Task_ waiting for some input from the user. + The task has _Timer_ that will skip the task if it's not completed in a given time (3 minutes in this example). You should be able to see the + associated _Job_ in the **Jobs** panel. Now, let's wait 3 minutes to see the timer in action. + +4. After 3 minutes, the scheduled _Job_ should have been executed, making the process instance skip the _HR Interview_ task. + In the **Process Details** page, click the _Refresh_ button to see the process instance state. + +
+
+ Process Details after timer +
Process Instance completed after the timer execution.
+
+
+ + Again, check the _Diagram_ panel to see the process instance execution path and the _HR Interview_ task + should have been skipped and the process instance continued its execution by following the _Application denied_ path + reaching the _Completed_ state. + + Notice in the _Jobs_ panel that the associated _Job_ has the **Executed** status. + +#### Complete Hiring process instances using Kogito Task Console + +When a _Kogito_ process reaches a _User Task_, the process execution stops waiting for the user input +that will enable the _User Task_ to finish and allowing the process execution to continue. + +_Kogito Task Console_ is the tool that enables the user interacting with the process _User Tasks_ and provide the necesary data +for the process to continue (usually wiht forms). + +In this guide, we'll see how to complete the process _User Tasks_ using the _Kogito Task Console_ to interact with the process _User Tasks_ +using the engine autogenerated forms. + +> **_NOTE_**: For simplicity, all the _User Tasks_ are assigned to the user _jdoe_. Please make sure you use the _jdoe_/_jdoe_ credentials +> when logging in the _Task Console_ + +1. With the example built and all the _Infrastructure Services_ running, let's start an instance of the _Hiring_ process. To do so, in a Terminal just run: + + ```bash + curl -H "Content-Type: application/json" -H "Accept: application/json" -X POST http://localhost:8080/hiring -d '{"candidateData": { "name": "Jon", "lastName": "Snow", "email": "jon@snow.org", "experience": 5, "skills": ["Java", "Kogito", "Fencing"]}}' + ``` + + If everything went well, you should get a response like: + + ```json + { + "id": "3cf0d58f-a824-4046-ba6c-c2e79edc1df7", + "offer": { "category": "Senior Software Engineer", "salary": 40450 } + } + ``` + + Which indicates that a new process instance with id **3cf0d58f-a824-4046-ba6c-c2e79edc1df7** has been started. + +2. Let's check the process instance state. Again browse to http://localhost:8280 to access the _Kogito Management Console_, + and in the **Process List** click the **Id** column to open the **Process Details** page. + +
+
+ Process List +
Process List in Kogito Management Console
+
+
+ +
+
+ Process Details +
Process instance Details page.
+
+
+ + As expected, the process instance is stopped in the _HR Interview_ task waiting for some input from the user. Let's try to + complete the task. + +3. Now open the _Kogito Task Console_ by browsing to http://localhost:8380 and login using the **jdoe/jdoe** credentials. + After logging in, you'll be redirected to the **Task Inbox** page, which contains the list of _Active_ tasks assigned to the + logged user. In this case you should be able to see only the new _HR Interview_ task. + +
+
+ Task Inbox +
Task Inbox in Kogito Task Console
+
+
+ + Click on the **HR Interview** task to open the form and complete it! + +4. The **Task Form** is the main component to interact with User Tasks, it allows the user to provide the data required by + the task and transition it to the next phase, allowing the Process to continue. The **Task Form** is autogenerated based + on the _User Task_ data assignments. + +
+
+ HR Interview Form +
HR Interview Task Form
+
+
+ + _HR Interview_ Form allows you to edit the actual **Offer** that will be sent to the _Candidate_ and also approve or deny + the job application with the **Approve** checkbox. + + Now, check the **Approve** checkbox click the **Complete** button in order to submit the form and complete the task. If the + task could be successfully completed, a notification should appear in the screen and the form will stay in Read-Only mode. + +
+
+ HR Interview Form Notification +
HR Interview Success notification!
+
+
+ + With the _HR Interview_ task successfully completed the process has moved forward and reached the _IT Interview_ task. + + Optionally, you can check the process instance state in the **Kogito Management Console** and verify the current + execution path. + +
+
+ Process Details +
Process Instance details stopped in IT Interview
+
+
+ +5. Now is time to complete the **IT Interview** task and complete this Hiring process instance. In **Task Console**, go + back to **Task Inbox** and as expected, there you'll see that **HR Interview** is no longer available and a new + **IT Interview** has appeared. + +
+
+ Task Inbox +
IT Interview in Task Inbox
+
+
+ + As done in Step #3, click in the **IT Interview** task to open the task form. _IT Interview_ task only needs the + candidate **Approval** to be submitted. Please, check the **Approval** field and click the **Complete** button to + submit the form. + +
+
+ IT Interview Form +
IT Interview Task Form
+
+
+ +6. After the form is submitted the _IT Task_ should be completed and the process should continue, notifying the _Candidate_ + that he has succesfully finished the Hiring process. Please go back to **Task Inbox** to verify there are no other active tasks + waiting for you. + +
+
+ Empty Task Inbox +
Empty **Task Inbox** after completing the *IT Interview* Task
+
+
+ + You can also open use _Kogito Management Console_ to check the state of the process instance and verify that the + instance has been successfully completed. + +
+
+ Hiring Process succesfully completed +
Hiring Process sucessfully completed
+
+
diff --git a/examples/jbpm-compact-architecture-example/docker-compose/.gitignore b/examples/jbpm-compact-architecture-example/docker-compose/.gitignore new file mode 100644 index 00000000000..b6632dbda58 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/.gitignore @@ -0,0 +1,3 @@ +.env +svg/ +persistence/ \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/docker-compose/README.md b/examples/jbpm-compact-architecture-example/docker-compose/README.md new file mode 100644 index 00000000000..26834f79196 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/README.md @@ -0,0 +1,61 @@ +# Kogito and Infrastructure services + +To allow a quick setup of all services required to run this demo, we provide a docker compose template that starts the following services: + +- Postgresql +- PgAdmin +- Kogito Example Service (Only available if the example has been compiled using the `container` mvn profile eg: `mvn cleanp package -Dcontainer`) +- Kogito Management Console +- Kogito Task Console +- Keycloak + +The docker compose template provides three profiles to enable starting only the set of services you want to use. The profiles are: + +- **infra**: Starts only the minimal infrastructure to run the example (Postgresql, pgadmin, Kogito Data Index) +- **example**: Starts the services in _infra_ profile and the Kogito Example Service. Requires the example to be compiled using the `container` mvn profile eg: `mvn cleanp package -Dcontainer`. +- **full** (default): includes all the above and also starts the **Management Console**, **Task Console** and a **Keycloak** to handle the consoles authentication. Requires the example to be compiled using the `container` mvn profile eg: `mvn cleanp package -Dcontainer`. + +> NOTE: In order to use it, please ensure you have Docker Compose installed on your machine, otherwise follow the instructions available +> in [here](https://docs.docker.com/compose/install/). + +## Starting the services + +Use the `startServices.sh` passing the docker profile you want to use as an argument. If no profile is provided the script will default to **full**. + +Eg: + +```shell +sh startServices.sh example +``` + +Once the services are started (depending on the profile), the following ports will be assigned on your local machine: + +- Postgresql: 5432 +- PgAdmin: 8055 +- Kogito Example Service: 8080 +- Kogito Management Console: 8280 +- Kogito Task Console: 8380 +- Keycloak: 8480 + +## Stopping and removing volume data + +To stop all services, simply run: + +```shell +docker compose stop +``` + +or + +```shell +docker compose down +``` + +to stop the services and remove the containers +docker-compose -f docker-compose-postgresql.yml stop + +For more details please check the Docker Compose documentation. + +```shell +docker compose --help +``` diff --git a/examples/jbpm-compact-architecture-example/docker-compose/docker-compose.yml b/examples/jbpm-compact-architecture-example/docker-compose/docker-compose.yml new file mode 100644 index 00000000000..95032f77340 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/docker-compose.yml @@ -0,0 +1,129 @@ +version: "3" + +services: + postgres: + container_name: postgres + image: postgres:16.1-alpine3.19 + profiles: ["infra", "example", "full"] + ports: + - "5432:5432" + volumes: + - ./sql:/docker-entrypoint-initdb.d:Z + healthcheck: + test: ["CMD", "pg_isready", "-q", "-d", "kogito", "-U", "kogito-user"] + timeout: 45s + interval: 10s + retries: 50 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + + pgadmin: + container_name: pgadmin + image: dpage/pgadmin4:8.2 + profiles: ["infra", "example", "full"] + ports: + - 8055:80 + depends_on: + - postgres + volumes: + - ./pgadmin/servers.json:/pgadmin4/servers.json + - ./pgadmin/pgpass:/pgadmin4/pgpass + entrypoint: > + /bin/sh -c " + cp -f /pgadmin4/pgpass /var/lib/pgadmin/; + chmod 600 /var/lib/pgadmin/pgpass; + /entrypoint.sh + " + environment: + PGADMIN_DEFAULT_EMAIL: user@kogito.org + PGADMIN_DEFAULT_PASSWORD: pass + PGADMIN_CONFIG_SERVER_MODE: "False" + PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False" + GUNICORN_ACCESS_LOGFILE: "/dev/null" + + jbpm-compact-architecture-example-service: + container_name: jbpm-compact-architecture-example-service + image: dev.local/${USER}/jppm-compact-architecture-example-service:${PROJECT_VERSION} + profiles: ["example", "full"] + ports: + - "8080:8080" + environment: + QUARKUS_HTTP_CORS_ORIGINS: "/.*/" + QUARKUS_DATASOURCE_JDBC_URL: "jdbc:postgresql://postgres:5432/kogito" + QUARKUS_DATASOURCE_REACTIVE_URL: "postgresql://postgres:5432/kogito" + QUARKUS_DATASOURCE_USERNAME: kogito-user + QUARKUS_DATASOURCE_PASSWORD: kogito-pass + QUARKUS_DATASOURCE_DB_KIND: postgresql + KOGITO_JOBS_SERVICE_URL: http://${DOCKER_GATEWAY_HOST}:8080 + KOGITO_SERVICE_URL: http://${DOCKER_GATEWAY_HOST}:8080 + KOGITO_DATAINDEX_HTTP_URL: http://${DOCKER_GATEWAY_HOST}:8080 + extra_hosts: + - "${DOCKER_GATEWAY_HOST}:host-gateway" + + keycloak: + container_name: keycloak + image: quay.io/keycloak/keycloak:legacy + profiles: ["full"] + ports: + - "8480:8080" + depends_on: + postgres: + condition: service_healthy + volumes: + - ./keycloak/kogito-realm.json:/tmp/kogito-realm.json + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/auth/realms/kogito"] + interval: 2s + timeout: 1s + retries: 50 + environment: + DB_VENDOR: POSTGRES + DB_ADDR: postgres + DB_DATABASE: keycloak + DB_USER: kogito-user + DB_SCHEMA: public + DB_PASSWORD: kogito-pass + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: admin + KEYCLOAK_IMPORT: /tmp/kogito-realm.json + + management-console: + container_name: management-console + image: ${KOGITO_MANAGEMENT_CONSOLE_IMAGE} + profiles: ["full"] + ports: + - 8280:8080 + depends_on: + jbpm-compact-architecture-example-service: + condition: service_started + keycloak: + condition: service_healthy + volumes: + - ./svg/:/home/kogito/data/svg/ + environment: + RUNTIME_TOOLS_MANAGEMENT_CONSOLE_KOGITO_ENV_MODE: "PROD" + RUNTIME_TOOLS_MANAGEMENT_CONSOLE_DATA_INDEX_ENDPOINT: http://${DOCKER_GATEWAY_HOST:-host.docker.internal}:8080/graphql + KOGITO_CONSOLES_KEYCLOAK_HEALTH_CHECK_URL: http://localhost:8480/auth/realms/kogito/.well-known/openid-configuration + KOGITO_CONSOLES_KEYCLOAK_URL: http://localhost:8480/auth + KOGITO_CONSOLES_KEYCLOAK_REALM: kogito + KOGITO_CONSOLES_KEYCLOAK_CLIENT_ID: kogito-console-quarkus + + task-console: + container_name: task-console + image: ${KOGITO_TASK_CONSOLE_IMAGE} + profiles: ["full"] + ports: + - 8380:8080 + depends_on: + jbpm-compact-architecture-example-service: + condition: service_started + keycloak: + condition: service_healthy + environment: + RUNTIME_TOOLS_TASK_CONSOLE_KOGITO_ENV_MODE: "PROD" + RUNTIME_TOOLS_TASK_CONSOLE_DATA_INDEX_ENDPOINT: http://${DOCKER_GATEWAY_HOST:-host.docker.internal}:8080/graphql + KOGITO_CONSOLES_KEYCLOAK_HEALTH_CHECK_URL: http://localhost:8480/auth/realms/kogito/.well-known/openid-configuration + KOGITO_CONSOLES_KEYCLOAK_URL: http://localhost:8480/auth + KOGITO_CONSOLES_KEYCLOAK_REALM: kogito + KOGITO_CONSOLES_KEYCLOAK_CLIENT_ID: kogito-console-quarkus diff --git a/examples/jbpm-compact-architecture-example/docker-compose/keycloak/kogito-realm.json b/examples/jbpm-compact-architecture-example/docker-compose/keycloak/kogito-realm.json new file mode 100644 index 00000000000..711887e17a2 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/keycloak/kogito-realm.json @@ -0,0 +1,2010 @@ +{ + "realm": "kogito", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "name": "managers", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "admin", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "user", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "HR", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "IT", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + }, + { + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "11d78bf6-6d10-4484-baba-a1388379d68b", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "impersonation", + "manage-identity-providers", + "view-identity-providers", + "view-realm", + "query-users", + "manage-clients", + "manage-events", + "manage-realm", + "view-authorization", + "manage-authorization", + "view-users", + "create-client", + "query-clients", + "query-groups", + "manage-users", + "view-clients", + "view-events", + "query-realms" + ] + } + }, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-groups", "query-users"] + } + }, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + }, + { + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "376bd940-e50a-4495-80fc-9c6c07312748", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "kogito-service": [ + { + "name": "uma_protection", + "composite": false, + "clientRole": true, + "containerId": "0ac5df91-e044-4051-bd03-106a3a5fb9cc", + "attributes": {} + } + ], + "broker": [ + { + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "53d4fe53-a039-471e-886a-28eddc950e95", + "attributes": {} + } + ], + "account": [ + { + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes": {} + }, + { + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes": {} + }, + { + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "e55e1234-38fa-432d-8d90-39f5e024688d", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": ["uma_authorization", "offline_access"], + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clients": [ + { + "clientId": "account", + "name": "${client_account}", + "baseUrl": "/auth/realms/kogito/account", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "0136c3ef-0dfd-4b13-a6d0-2c8b6358edec", + "defaultRoles": ["view-profile", "manage-account"], + "redirectUris": ["/auth/realms/kogito/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "a951803a-79c7-46a6-8197-e32835286971", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "e1f7edd7-e15c-43b4-8736-ff8204d16836", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "kogito-frontend", + "rootUrl": "http://localhost:8082", + "adminUrl": "http://localhost:8082", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "secret", + "redirectUris": ["http://localhost:8082/*"], + "webOrigins": ["http://localhost:8082"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"], + "access": { + "view": true, + "configure": true, + "manage": true + } + }, + { + "clientId": "kogito-app", + "rootUrl": "http://localhost:8080", + "adminUrl": "http://localhost:8080", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "secret", + "redirectUris": ["http://localhost:8080/*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"], + "access": { + "view": true, + "configure": true, + "manage": true + } + }, + { + "clientId": "kogito-service", + "rootUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "secret", + "redirectUris": ["*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "authorizationServicesEnabled": true, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"], + "authorizationSettings": { + "allowRemoteResourceManagement": true, + "policyEnforcementMode": "ENFORCING", + "resources": [ + { + "name": "User Resource", + "ownerManagedAccess": false, + "attributes": {}, + "_id": "df1b74a9-3f10-499d-a581-368de48e512b", + "uris": ["/api/users/*"] + }, + { + "name": "Administration Resource", + "ownerManagedAccess": false, + "attributes": {}, + "_id": "7124e2f1-e6dc-44b4-87ab-24b010090b97", + "uris": ["/api/admin/*"] + } + ], + "policies": [ + { + "name": "Any User Policy", + "description": "Any user granted with the user role can access something", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"user\",\"required\":false}]" + } + }, + { + "name": "Only Administrators", + "description": "Only administrators can access", + "type": "role", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "roles": "[{\"id\":\"admin\",\"required\":false}]" + } + }, + { + "name": "User Resource Permission", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"User Resource\"]", + "applyPolicies": "[\"Any User Policy\"]" + } + }, + { + "name": "Administration Resource Permission", + "type": "resource", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "config": { + "resources": "[\"Administration Resource\"]", + "applyPolicies": "[\"Only Administrators\"]" + } + } + ], + "scopes": [], + "decisionStrategy": "UNANIMOUS" + } + }, + { + "clientId": "kogito-console-react", + "rootUrl": "http://localhost:9000", + "adminUrl": "http://localhost:9000/", + "baseUrl": "http://localhost:9000/", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": ["http://localhost:9000/*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "kogito-console-quarkus", + "rootUrl": "http://localhost:8380", + "adminUrl": "http://localhost:8380/", + "baseUrl": "http://localhost:8380/", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "**********", + "redirectUris": ["http://localhost:8380/*", "http://localhost:8280/*"], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "kogito-jobs-service", + "rootUrl": "http://localhost:8080", + "adminUrl": "http://localhost:8080", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "secret", + "redirectUris": ["http://localhost:8080/*"], + "webOrigins": ["http://localhost:8080"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"], + "access": { + "view": true, + "configure": true, + "manage": true + } + }, + { + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "c41b709a-a012-4c69-89d7-4f926dba0619", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "baseUrl": "/auth/admin/kogito/console/index.html", + "surrogateAuthRequired": false, + "enabled": true, + "clientAuthenticatorType": "client-secret", + "secret": "e571b211-2550-475d-b87f-116ff54091ee", + "redirectUris": ["/auth/admin/kogito/console/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "role_list", "profile", "roles", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + } + ], + "clientScopes": [ + { + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + } + ] + }, + { + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": ["role_list", "profile", "email", "roles", "web-origins"], + "defaultOptionalClientScopes": ["offline_access", "address", "phone", "microprofile-jwt"], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "oidc-address-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-usermodel-property-mapper" + ] + } + }, + { + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-full-name-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAn5T13suF8mlS+pJXp0U1bto41nW55wpcs+Rps8ZVCRyJKWqzwSCYnI7lm0rB2wBpAAO4OPoj1zlmVoFmBPsDU9Xf7rjsJb5LIzIQDCZY44aSDZt6RR+gakPiQvlzHyW/RozYpngDJF7TsTD7rdRF1xQ4RprfBF8fwK/xsU7pxbeom5xDHZhz3fiw8s+7UdbmnazDHfAjU58aUrLGgVRfUsuoHjtsptYlOIXEifaeMetXZE+HhqLYRHQPDap5fbBJl773Trosn7N9nmzN4x1xxGj9So21WC5UboQs9sAIVgizc4omjZ5Y4RN9HLH7G4YwJctNntzmnJhDui9zAO+zSQIDAQABAoIBADi+F7rTtVoft0Cfnok8o6Y58/HVxHdxiMryUd95iy0FN4RBi48FTx6D9QKFz25Ws/8sU2n3D51srIXf1u24b1N0/f39RQKaqk7mcyxOylaEuBQcj5pah4ihgKd92UBfBKdKV5LBo6RgD3e2yhbiHr8+UlBQqzH7vOef6Bm6zIbfmi3N88swAJhP0YizRZFklsbmLsK6nkwyro00CHJvPVKSBbM+ad+/zIBsLw56MvNngB5TuFguUgoljd6M1T2z4utmZGlTUqrfE1onAVLJZoGnRohyIr7dJEg6YxWR70PxsgmkDKyeRvet9P1trO0n+OSprusfrC3cHJStabap1V0CgYEA1A/CtsqTnjdYYsB19eumZgdpzUgNc/YEAzZ/OWb8yTLoB2ncci+63A1rXHUXAqJFY7vtjn5mxv7SuASNbUrzq+6KfZvC1x9XEtnczqT/ypunNfxmIZuj8Nuu6vtURguZ8kPPwdkI8toTizRFeRE5ZDBvoQryiEVYugfHaHT5vzsCgYEAwKWODwquI0Lv9BuwdNVrBXQpkKh3ZfYOA7i9xvhxlM7xUu8OMCwwCPn3r7vrW5APjTqX4h330mJ44SLEs+7gbCUs4BbJBLA6g0ChlHa9PTkxp6tk2nDF/B34fxiZSRkE85L+d+at0Dc3hnlzLCJCzJawGpoPniPU9e4w0p4dN0sCgYAsGnMGjS8SUrRhJWHjGXVr9tK8TOXvXhULjgP7rj2Yoqu7Dvs4DFEyft/7RKbad2EzEtyfLA64CDtO5jN7rYDsGxpWcVSeZPg5BXJ0z8AbJTArfCjJiJMZ/rZsTIUEZFlKF2xYBolj6JLz+pUQTtK+0YwF1D8ItFN1rTR9twZSDQKBgQC6sPXNX+VH6LuPTjIf1x8CxwLs3EXxOpV0R9kp9GRl+HJnk6GlT30xhcThufQo5KAdllXQXIhoiuNoEoCbevhj9Vbax1oBQCNERSMRNEzKAx46xd9TzYwgeo7x5E3QR/3DaoVOfu+cY5ZcrF/PulgP2kxJS1mtQD5GIpGP2oinpwKBgGqiqTFPqRcelx76vBvTU+Jp1zM62T4AotbMrSQR/oUvqHe5Ytj/SbZx+wbbHAiyGgV700Mosyviik83YEAbR3kdOPjgYvAJJW2Y3jEMdQ7MwriXz8XLh5BGmYfVjkSOJXed9ua9WlYLKOJeXXv191BbDvrx5NXuJyVVU4vJx3YZ" + ], + "certificate": [ + "MIICnTCCAYUCBgFp4EYIrjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdwcm90ZWFuMB4XDTE5MDQwMjIyNTYxOVoXDTI5MDQwMjIyNTc1OVowEjEQMA4GA1UEAwwHcHJvdGVhbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ+U9d7LhfJpUvqSV6dFNW7aONZ1uecKXLPkabPGVQkciSlqs8EgmJyO5ZtKwdsAaQADuDj6I9c5ZlaBZgT7A1PV3+647CW+SyMyEAwmWOOGkg2bekUfoGpD4kL5cx8lv0aM2KZ4AyRe07Ew+63URdcUOEaa3wRfH8Cv8bFO6cW3qJucQx2Yc934sPLPu1HW5p2swx3wI1OfGlKyxoFUX1LLqB47bKbWJTiFxIn2njHrV2RPh4ai2ER0Dw2qeX2wSZe+9066LJ+zfZ5szeMdccRo/UqNtVguVG6ELPbACFYIs3OKJo2eWOETfRyx+xuGMCXLTZ7c5pyYQ7ovcwDvs0kCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVtmRKDb4OK5iSA46tagMBkp6L7WuPpCWuHGWwobEP+BecYsShW7zP3s12oA8SNSwbhvu0CRqgzxhuypgf3hKQFVU153Erv4hzkj+8S0s5LR/ZE7tDNY2lzJ3yQKXy3Md7EkuzzvOZ50MTrcSKAanWq/ZW1OTnrtGymj5zGJnTg7mMnJzEIGePxkvPu/QdchiPBLqxfZYm1jsFGY25djOC3N/KmVcRVmPRGuu6D8tBFHlKoPfZYPdbMvsvs24aupHKRcZ+ofTCpK+2Qo8c0pSSqeEYHGmuGqC6lC6ozxtxSABPO9Q1R1tZBU7Kg5HvXUwwmoVS3EGub46YbHqbmWMLg==" + ], + "priority": ["100"] + } + }, + { + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["96afd00e-85cf-4d35-b18e-061d3813d8b2"], + "secret": ["qBFGKdUGf6xDgKphnRfoFzIzaFHJW4bYnZ9MinPFzN38X5_ctq-2u1q5RdZzeJukXvk2biHB8_s3DxWmmLZFsA"], + "priority": ["100"], + "algorithm": ["HS256"] + } + }, + { + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["b04473d3-8395-4016-b455-19a9e951106b"], + "secret": ["x68mMOVdz3qKWzltzReV0g"], + "priority": ["100"] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "OPTIONAL", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "OPTIONAL", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "OPTIONAL", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "_browser_header.xXSSProtection": "1; mode=block", + "_browser_header.xFrameOptions": "SAMEORIGIN", + "_browser_header.strictTransportSecurity": "max-age=31536000; includeSubDomains", + "permanentLockout": "false", + "quickLoginCheckMilliSeconds": "1000", + "_browser_header.xRobotsTag": "none", + "maxFailureWaitSeconds": "900", + "minimumQuickLoginWaitSeconds": "60", + "failureFactor": "30", + "actionTokenGeneratedByUserLifespan": "300", + "maxDeltaTimeSeconds": "43200", + "_browser_header.xContentTypeOptions": "nosniff", + "offlineSessionMaxLifespan": "5184000", + "actionTokenGeneratedByAdminLifespan": "43200", + "_browser_header.contentSecurityPolicyReportOnly": "", + "bruteForceProtected": "false", + "_browser_header.contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "waitIncrementSeconds": "60", + "offlineSessionMaxLifespanEnabled": "false" + }, + "users": [ + { + "username": "admin", + "enabled": true, + "totp": false, + "emailVerified": false, + "credentials": [ + { + "type": "password", + "hashedSaltedValue": "NICTtwsvSxJ5hL8hLAuleDUv9jwZcuXgxviMXvR++cciyPtiIEStEaJUyfA9DOir59awjPrHOumsclPVjNBplA==", + "salt": "T/2P5o5oxFJUEk68BRURRg==", + "hashIterations": 27500, + "counter": 0, + "algorithm": "pbkdf2-sha256", + "digits": 0, + "period": 0, + "createdDate": 1554245879354, + "config": {} + } + ], + "disableableCredentialTypes": ["password"], + "requiredActions": [], + "realmRoles": ["admin", "managers", "user", "IT", "HR"], + "notBefore": 0, + "groups": [] + }, + { + "username": "alice", + "enabled": true, + "totp": false, + "emailVerified": false, + "credentials": [ + { + "type": "password", + "hashedSaltedValue": "A3okqV2T/ybXTVEgKfosoSjP8Yc9IZbFP/SY4cEd6hag7TABQrQ6nUSuwagGt96l8cw1DTijO75PqX6uiTXMzw==", + "salt": "sl4mXx6T9FypPH/s9TngfQ==", + "hashIterations": 27500, + "counter": 0, + "algorithm": "pbkdf2-sha256", + "digits": 0, + "period": 0, + "createdDate": 1554245879116, + "config": {} + } + ], + "disableableCredentialTypes": ["password"], + "requiredActions": [], + "realmRoles": ["user", "HR"], + "notBefore": 0, + "groups": [] + }, + { + "username": "jdoe", + "enabled": true, + "totp": false, + "emailVerified": false, + "credentials": [ + { + "type": "password", + "hashedSaltedValue": "JV3DUNLjqOadjbBOtC4rvacQI553CGaDGAzBS8MR5ReCr7SwF3E6CsW3T7/XO8ITZAsch8+A/6loeuCoVLLJrg==", + "salt": "uCbOH7HZtyDtMd0E9DG/nw==", + "hashIterations": 27500, + "counter": 0, + "algorithm": "pbkdf2-sha256", + "digits": 0, + "period": 0, + "createdDate": 1554245879227, + "config": {} + } + ], + "disableableCredentialTypes": ["password"], + "requiredActions": [], + "realmRoles": ["managers", "user", "IT"], + "notBefore": 0, + "groups": [] + } + ], + "keycloakVersion": "6.0.0", + "userManagedAccessAllowed": false +} diff --git a/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/pgpass b/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/pgpass new file mode 100644 index 00000000000..11a6f7c6019 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/pgpass @@ -0,0 +1,3 @@ +postgres:5432:kogito:kogito-user:kogito-pass +postgres:5432:keycloak:kogito-user:kogito-pass +postgres:5432:postgres:kogito-user:kogito-pass \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/servers.json b/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/servers.json new file mode 100644 index 00000000000..caafe268c0f --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/pgadmin/servers.json @@ -0,0 +1,14 @@ +{ + "Servers": { + "1": { + "Name": "kogito", + "Group": "Servers", + "Host": "postgres", + "Port": 5432, + "MaintenanceDB": "kogito", + "Username": "kogito-user", + "SSLMode": "disable", + "PassFile": "/var/lib/pgadmin/pgpass" + } + } +} diff --git a/examples/jbpm-compact-architecture-example/docker-compose/sql/init.sql b/examples/jbpm-compact-architecture-example/docker-compose/sql/init.sql new file mode 100644 index 00000000000..92ea9b4e5c6 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/sql/init.sql @@ -0,0 +1,33 @@ +CREATE ROLE "kogito-user" WITH + LOGIN + SUPERUSER + INHERIT + CREATEDB + CREATEROLE + NOREPLICATION + PASSWORD 'kogito-pass'; + +CREATE DATABASE kogito + WITH + OWNER = "kogito-user" + ENCODING = 'UTF8' + LC_COLLATE = 'en_US.utf8' + LC_CTYPE = 'en_US.utf8' + TABLESPACE = pg_default + CONNECTION LIMIT = -1; + +CREATE DATABASE keycloak + WITH + OWNER = "kogito-user" + ENCODING = 'UTF8' + LC_COLLATE = 'en_US.utf8' + LC_CTYPE = 'en_US.utf8' + TABLESPACE = pg_default + CONNECTION LIMIT = -1; + +GRANT ALL PRIVILEGES ON DATABASE postgres TO "kogito-user"; +GRANT ALL PRIVILEGES ON DATABASE kogito TO "kogito-user"; +GRANT ALL PRIVILEGES ON DATABASE kogito TO postgres; + +GRANT ALL PRIVILEGES ON DATABASE keycloak TO "kogito-user"; +GRANT ALL PRIVILEGES ON DATABASE keycloak TO postgres; \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/docker-compose/startServices.sh b/examples/jbpm-compact-architecture-example/docker-compose/startServices.sh new file mode 100755 index 00000000000..e8a42f5b2ee --- /dev/null +++ b/examples/jbpm-compact-architecture-example/docker-compose/startServices.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +PROFILE="full" + +echo "Script requires your Kogito Example to be compiled" + +PROJECT_VERSION=$(cd ../ && mvn help:evaluate -Dexpression=project.version -q -DforceStdout) +KOGITO_MANAGEMENT_CONSOLE_IMAGE=$(cd ../ && mvn help:evaluate -Dexpression=kogito.management-console.image -q -DforceStdout) +KOGITO_TASK_CONSOLE_IMAGE=$(cd ../ && mvn help:evaluate -Dexpression=kogito.task-console.image -q -DforceStdout) + + +if [ -n "$1" ]; then + if [[ ("$1" == "full") || ("$1" == "infra") || ("$1" == "example")]]; + then + PROFILE="$1" + else + echo "Unknown docker profile '$1'. The supported profiles are:" + echo "* 'infra': Use this profile to start only the minimum infrastructure to run the example (postgresql, data-index & jobs-service)." + echo "* 'example': Use this profile to start the example infrastructure and the kogito-example service. Requires the example to be compiled using the 'container' profile (-Pcontainer)" + echo "* 'full' (default): Starts full example setup, including infrastructure (database, data-index & jobs-service), the kogito-example-service container and the runtime consoles (management-console, task-console & keycloak). Requires the example to be compiled using the 'container' profile (-Pcontainer)" + exit 1; + fi +fi + +echo "PROJECT_VERSION=${PROJECT_VERSION}" > ".env" +echo "KOGITO_MANAGEMENT_CONSOLE_IMAGE=${KOGITO_MANAGEMENT_CONSOLE_IMAGE}" >> ".env" +echo "KOGITO_TASK_CONSOLE_IMAGE=${KOGITO_TASK_CONSOLE_IMAGE}" >> ".env" +echo "COMPOSE_PROFILES='${PROFILE}'" >> ".env" + +if [ "$(uname)" == "Darwin" ]; then + echo "DOCKER_GATEWAY_HOST=kubernetes.docker.internal" >> ".env" +elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + echo "DOCKER_GATEWAY_HOST=172.17.0.1" >> ".env" +fi + +if [ ! -d "./svg" ] +then + echo "SVG folder does not exist. Have you compiled the project? mvn clean install -DskipTests" + exit 1 +fi + +docker compose up \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/docs/images/g1_1_mc_list.png b/examples/jbpm-compact-architecture-example/docs/images/g1_1_mc_list.png new file mode 100644 index 00000000000..4e5aa6274df Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g1_1_mc_list.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g1_2_mc_details.png b/examples/jbpm-compact-architecture-example/docs/images/g1_2_mc_details.png new file mode 100644 index 00000000000..f2b228f20d0 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g1_2_mc_details.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g1_3_mc_details_executed_job.png b/examples/jbpm-compact-architecture-example/docs/images/g1_3_mc_details_executed_job.png new file mode 100644 index 00000000000..1ab358819c3 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g1_3_mc_details_executed_job.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_10_mc_details_completed.png b/examples/jbpm-compact-architecture-example/docs/images/g2_10_mc_details_completed.png new file mode 100644 index 00000000000..610849f19ee Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_10_mc_details_completed.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_1_mc_list.png b/examples/jbpm-compact-architecture-example/docs/images/g2_1_mc_list.png new file mode 100644 index 00000000000..6526e8e7f23 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_1_mc_list.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_2_mc_details.png b/examples/jbpm-compact-architecture-example/docs/images/g2_2_mc_details.png new file mode 100644 index 00000000000..ec18201f08a Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_2_mc_details.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_3_tc_inbox.png b/examples/jbpm-compact-architecture-example/docs/images/g2_3_tc_inbox.png new file mode 100644 index 00000000000..0d612f76c80 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_3_tc_inbox.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_4_tc_hr_form.png b/examples/jbpm-compact-architecture-example/docs/images/g2_4_tc_hr_form.png new file mode 100644 index 00000000000..d89b41302da Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_4_tc_hr_form.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_5_tc_hr_form_notification.png b/examples/jbpm-compact-architecture-example/docs/images/g2_5_tc_hr_form_notification.png new file mode 100644 index 00000000000..8dd81fc4a92 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_5_tc_hr_form_notification.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_6_mc_details.png b/examples/jbpm-compact-architecture-example/docs/images/g2_6_mc_details.png new file mode 100644 index 00000000000..4970b8f8374 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_6_mc_details.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_7_tc_inbox.png b/examples/jbpm-compact-architecture-example/docs/images/g2_7_tc_inbox.png new file mode 100644 index 00000000000..03f63ff7b45 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_7_tc_inbox.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_8_tc_it_form.png b/examples/jbpm-compact-architecture-example/docs/images/g2_8_tc_it_form.png new file mode 100644 index 00000000000..5e6872a09c8 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_8_tc_it_form.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/g2_9_tc_inbox_empty.png b/examples/jbpm-compact-architecture-example/docs/images/g2_9_tc_inbox_empty.png new file mode 100644 index 00000000000..696433e93d3 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/g2_9_tc_inbox_empty.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/generate_offer_assignments.png b/examples/jbpm-compact-architecture-example/docs/images/generate_offer_assignments.png new file mode 100644 index 00000000000..2e3f93529f3 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/generate_offer_assignments.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/hiring_diagram.png b/examples/jbpm-compact-architecture-example/docs/images/hiring_diagram.png new file mode 100644 index 00000000000..cb57a2dfb9d Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/hiring_diagram.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/hr_interview_assignments.png b/examples/jbpm-compact-architecture-example/docs/images/hr_interview_assignments.png new file mode 100644 index 00000000000..63de050eb5e Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/hr_interview_assignments.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/it_interview_assignments.png b/examples/jbpm-compact-architecture-example/docs/images/it_interview_assignments.png new file mode 100644 index 00000000000..077430569d6 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/it_interview_assignments.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/mc_details_1.png b/examples/jbpm-compact-architecture-example/docs/images/mc_details_1.png new file mode 100644 index 00000000000..2c1c3b26abb Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/mc_details_1.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/mc_list.png b/examples/jbpm-compact-architecture-example/docs/images/mc_list.png new file mode 100644 index 00000000000..4fc32b05cc3 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/mc_list.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn.png b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn.png new file mode 100644 index 00000000000..451313a264d Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_decision.png b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_decision.png new file mode 100644 index 00000000000..f58d869d36f Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_decision.png differ diff --git a/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_types.png b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_types.png new file mode 100644 index 00000000000..d028d331da9 Binary files /dev/null and b/examples/jbpm-compact-architecture-example/docs/images/new_hiring_offer_dmn_types.png differ diff --git a/examples/jbpm-compact-architecture-example/env/index.js b/examples/jbpm-compact-architecture-example/env/index.js new file mode 100644 index 00000000000..4b9f86d2fa6 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/env/index.js @@ -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. + */ + +const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env"); + +const { + env: { kogitoManagementConsole: kogitoManagementConsoleEnv }, +} = require("@kie-tools/kogito-management-console/env"); + +const { + env: { kogitoTaskConsole: kogitoTaskConsoleEnv }, +} = require("@kie-tools/kogito-task-console/env"); + +module.exports = composeEnv([require("@kie-tools/root-env/env")], { + vars: varsWithName({ + JBPM_COMPACT_ARCHITECTURE_EXAMPLE__managementConsoleImage: { + default: `${kogitoManagementConsoleEnv.registry}/${kogitoManagementConsoleEnv.account}/${kogitoManagementConsoleEnv.name}:${kogitoManagementConsoleEnv.buildTag}`, + description: "The image for the Kogito Management Console Image.", + }, + JBPM_COMPACT_ARCHITECTURE_EXAMPLE__taskConsoleImage: { + default: `${kogitoTaskConsoleEnv.registry}/${kogitoTaskConsoleEnv.account}/${kogitoTaskConsoleEnv.name}:${kogitoTaskConsoleEnv.buildTag}`, + description: "The image for the Kogito Task Console Image.", + }, + }), + get env() { + return { + jbpmCompactArchitectureExample: { + kogitoManagementConsoleImage: getOrDefault(this.vars.JBPM_COMPACT_ARCHITECTURE_EXAMPLE__managementConsoleImage), + kogitoTaskConsoleImage: getOrDefault(this.vars.JBPM_COMPACT_ARCHITECTURE_EXAMPLE__taskConsoleImage), + version: require("../package.json").version, + }, + }; + }, +}); diff --git a/examples/jbpm-compact-architecture-example/install.js b/examples/jbpm-compact-architecture-example/install.js new file mode 100644 index 00000000000..47e591480b5 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/install.js @@ -0,0 +1,35 @@ +/* + * 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, setPomProperty } = require("@kie-tools/maven-config-setup-helper"); + +setup(` + -Drevision=${buildEnv.env.jbpmCompactArchitectureExample.version} +`); + +setPomProperty({ + key: "kogito.management-console.image", + value: buildEnv.env.jbpmCompactArchitectureExample.kogitoManagementConsoleImage, +}); + +setPomProperty({ + key: "kogito.task-console.image", + value: buildEnv.env.jbpmCompactArchitectureExample.kogitoTaskConsoleImage, +}); diff --git a/examples/jbpm-compact-architecture-example/package.json b/examples/jbpm-compact-architecture-example/package.json new file mode 100644 index 00000000000..2ce94a9f7b6 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/package.json @@ -0,0 +1,55 @@ +{ + "private": true, + "name": "@kie-tools-examples/jbpm-compact-architecture-example", + "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": "run-script-os", + "build:darwin:linux": "mvn clean install -P container", + "build:dev": "run-script-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:dev:darwin:linux": "mvn clean compile -DskipTests", + "build:dev:win32": "pnpm powershell \"mvn clean compile `-DskipTests \"", + "build:prod": "pnpm lint && run-script-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:prod:darwin:linux": "mvn clean compile -DskipTests=$(build-env tests.run --not) -Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures) -Pcontainer", + "build:prod:win32": "pnpm powershell \"mvn clean compile `-DskipTests `-Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures) `-Pcontainer \"", + "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 -Pdevelopment", + "quarkus:dev:win32": "mvn clean package quarkus:dev `-DskipTests `-Pdevelopment", + "run": "run-script-os", + "run:darwin:linux": "cd ./docker-compose && sh startServices.sh", + "start": "run-script-os", + "start:darwin:linux": "pnpm run build && pnpm run run", + "start:win32": "echo Running full example ot supported in windows", + "stop": "run-script-os", + "stop:darwin:linux": "cd ./docker-compose && docker compose down" + }, + "dependencies": { + "@kie-tools/jbpm-quarkus-devui": "workspace:*", + "@kie-tools/maven-base": "workspace:*" + }, + "devDependencies": { + "@kie-tools/kogito-management-console": "workspace:*", + "@kie-tools/kogito-task-console": "workspace:*", + "@kie-tools/maven-config-setup-helper": "workspace:*", + "@kie-tools/root-env": "workspace:*", + "run-script-os": "^1.1.6" + }, + "kieTools": { + "requiredPreinstalledCliCommands": [ + "java", + "mvn" + ] + } +} \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/pom.xml b/examples/jbpm-compact-architecture-example/pom.xml new file mode 100644 index 00000000000..56b4cef9276 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/pom.xml @@ -0,0 +1,202 @@ + + + + 4.0.0 + + org.kie + kie-tools-maven-base + ${revision} + ./node_modules/@kie-tools/maven-base/pom.xml + + + jbpm-compact-architecture-quarkus-example + Kie-Tools Example :: jBPM Compact Architecture Quarkus Example + + + docker.io/apache/incubator-kie-kogito-management-console:main + docker.io/apache/incubator-kie-kogito-task-console:main + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-smallrye-health + + + + org.jbpm + jbpm-with-drools-quarkus + + + + org.kie + kie-addons-quarkus-process-management + + + org.kie + kogito-addons-quarkus-jobs-management + + + org.kie + kie-addons-quarkus-process-svg + + + org.kie + kie-addons-quarkus-source-files + + + + + io.quarkus + quarkus-jdbc-postgresql + + + io.quarkus + quarkus-agroal + + + org.kie + kie-addons-quarkus-persistence-jdbc + + + + + org.kie + kogito-addons-quarkus-data-index-postgresql + + + + + org.kie + kogito-addons-quarkus-data-index-persistence-postgresql + + + + + org.kie + kogito-addons-quarkus-jobs + + + org.kie.kogito + jobs-service-postgresql-common + + + + + org.kie + kogito-addons-quarkus-data-audit-jpa + + + org.kie + kogito-addons-quarkus-data-audit + + + + + + container + + container + + + + io.quarkus + quarkus-container-image-jib + + + + + development + + dev + + + + + org.jbpm + jbpm-quarkus-devui-bom + ${project.version} + pom + import + + + + + + org.jbpm + jbpm-quarkus-devui + + + + + + + ${project.artifactId} + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + maven-antrun-plugin + + + package + + run + + + + + + + + + + + + + + diff --git a/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/CandidateData.java b/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/CandidateData.java new file mode 100644 index 00000000000..eae14184da0 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/CandidateData.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.kie.kogito.hr; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +public class CandidateData { + + private String name; + + private String lastName; + + private String email; + + private Integer experience; + + private List skills; + + public CandidateData() { + } + + public CandidateData(String name, String lastName, String email, Integer experience, List skills) { + this.name = name; + this.lastName = lastName; + this.email = email; + this.experience = experience; + this.skills = skills; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public Integer getExperience() { + return experience; + } + + public void setExperience(Integer experience) { + this.experience = experience; + } + + public List getSkills() { + return skills; + } + + public void setSkills(List skills) { + this.skills = skills; + } + + @JsonIgnore + public String getFullName() { + return name + " " + lastName; + } +} diff --git a/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/Offer.java b/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/Offer.java new file mode 100644 index 00000000000..a377b480aa0 --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/java/org/kie/kogito/hr/Offer.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.kogito.hr; + +public class Offer { + + private String category; + + private Integer salary; + + public Offer() { + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public Integer getSalary() { + return salary; + } + + public void setSalary(Integer salary) { + this.salary = salary; + } +} diff --git a/examples/jbpm-compact-architecture-example/src/main/resources/META-INF/processSVG/hiring.svg b/examples/jbpm-compact-architecture-example/src/main/resources/META-INF/processSVG/hiring.svg new file mode 100644 index 00000000000..fba0500706c --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/resources/META-INF/processSVG/hiring.svg @@ -0,0 +1 @@ +HR InterviewIT InterviewNew Hiring Send notification HR Interview avoided Application denied Generate base offer Log OfferSend Offer to Candidate \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/src/main/resources/NewHiringOffer.dmn b/examples/jbpm-compact-architecture-example/src/main/resources/NewHiringOffer.dmn new file mode 100644 index 00000000000..67b0eded68d --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/resources/NewHiringOffer.dmn @@ -0,0 +1,163 @@ + + + + + + string + + + string + + + string + + + number + + + string + + + + + number + + + string + + "Software Engineer", "Senior Software Engineer", "Software Architect" + + + + + + + + + + + + + + + + + + count(CandidateData.skills) * 150 + + + + + + + + CandidateData.experience + + + + + "Software Engineer", "Senior Software Engineer", "Software Architect" + + + + + + + [0..5) + + + "Software Engineer" + + + 30000 + SalaryBonus + + + + + + + + [5..10) + + + "Senior Software Engineer" + + + 40000 + SalaryBonus + + + + + + + + >=10 + + + "Software Architect" + + + 50000 + SalaryBonus + + + + + + + + + + Offer + + + + + + + + + + 50 + 120 + 926 + + + 926 + + + 50 + 175 + 104 + 437 + 140 + + + 926 + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/src/main/resources/application.properties b/examples/jbpm-compact-architecture-example/src/main/resources/application.properties new file mode 100644 index 00000000000..b2a3bf6b7fe --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/resources/application.properties @@ -0,0 +1,46 @@ +# Packaging +#quarkus.package.type=fast-jar + +#https://quarkus.io/guides/openapi-swaggerui +quarkus.http.cors=true +quarkus.smallrye-openapi.path=/docs/openapi.json +quarkus.swagger-ui.always-include=true +quarkus.kogito.data-index.graphql.ui.always-include=true +quarkus.http.test-port=0 + +# Kogito-service +kogito.service.url=http://localhost:8080 + +#Job-service +kogito.jobs-service.url=http://localhost:8080 +kogito.dataindex.http.url=http://localhost:8080 + +# run create tables scripts +quarkus.flyway.migrate-at-start=true +quarkus.flyway.baseline-on-migrate=true +quarkus.flyway.baseline-version=0.0 +quarkus.flyway.locations=classpath:/db/migration,classpath:/db/jobs-service,classpath:/db/data-audit/postgresql +quarkus.flyway.table=FLYWAY_RUNTIME_SERVICE + +kogito.persistence.type=jdbc +quarkus.datasource.db-kind=postgresql +%prod.quarkus.datasource.username=kogito-user +%prod.quarkus.datasource.password=kogito-pass +%prod.quarkus.datasource.jdbc.url=${QUARKUS_DATASOURCE_JDBC_URL:jdbc:postgresql://localhost:5432/kogito} +%prod.quarkus.datasource.reactive.url=${QUARKUS_DATASOURCE_REACTIVE_URL:postgresql://localhost:5432/kogito} + +quarkus.native.native-image-xmx=8g + +# profile to pack this example into a container, to use it execute activate the maven container profile, -Pcontainer +%container.quarkus.container-image.build=true +%container.quarkus.container-image.push=false +%container.quarkus.container-image.group=${USER} +%container.quarkus.container-image.registry=dev.local +%container.quarkus.container-image.tag=${project.version} +%container.quarkus.container-image.name=jppm-compact-architecture-example-service + +%dev.quarkus.kogito.devservices.enabled=false +%dev.jbpm.devui.users.jdoe.groups=admin,HR,IT + +# Disabling OIDC +quarkus.oidc.enabled=false \ No newline at end of file diff --git a/examples/jbpm-compact-architecture-example/src/main/resources/hiring.bpmn b/examples/jbpm-compact-architecture-example/src/main/resources/hiring.bpmn new file mode 100644 index 00000000000..3c043c24cfc --- /dev/null +++ b/examples/jbpm-compact-architecture-example/src/main/resources/hiring.bpmn @@ -0,0 +1,691 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _B11455DE-F77A-4251-A85B-4C66636E3CD9 + _7DDA574A-C220-4FEF-9784-22EF8052EDEC + System.out.println("###################################"); +System.out.println("To: " + candidateData.getEmail()); +System.out.println("Subject: Congratulations you made it!"); +System.out.println("Dear " + candidateData.getFullName() + ", we are happy to tell you that you've successfuly went trhough the hiring process. You'll find the fina Offer details in attached."); +System.out.println("Job Category: " + offer.getCategory()); +System.out.println("Base salary: " + offer.getSalary()); +System.out.println("###################################"); + + + + + + + + _9C33F5EA-89C7-4ED1-B3C2-CF18DE439AF5 + _ACEE7578-B7D2-4EDF-B104-9ECF3DD8A383 + System.out.println("###################################"); +System.out.println("Generated offer for candidate: " + candidateData.getFullName()); +System.out.println("Job Category: " + offer.getCategory()); +System.out.println("Base salary: " + offer.getSalary()); +System.out.println("###################################"); + + + _7DDA574A-C220-4FEF-9784-22EF8052EDEC + + + + + + + + _59F9A0E6-7F9C-43A9-8920-5B40A91169E6 + _9C33F5EA-89C7-4ED1-B3C2-CF18DE439AF5 + + + + + + + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_fileNameInputX + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_namespaceInputX + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_decisionInputX + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_modelInputX + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_CandidateDataInputX + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_OfferOutputX + + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_fileNameInputX + + + + + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_namespaceInputX + + + + + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_decisionInputX + + + + + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_modelInputX + + + + + + + candidateData + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_CandidateDataInputX + + + _F4D56F6C-4CFE-4D5C-BF5E-67261F68EF1A_OfferOutputX + offer + + + + _527D3164-4989-4D2C-B80B-9BA9D4C8FB89 + + + + + + + + _94172225-E124-4F14-98DA-C3D62C11254A + _527D3164-4989-4D2C-B80B-9BA9D4C8FB89 + System.out.println("###################################"); +System.out.println("Candidate " + candidateData.getFullName() + " don't meet the requirements for the position but we'll keep it on records for the future!"); +System.out.println("###################################"); + + + + _5334FFDC-1FCB-47E6-8085-36DC9A3D17B9 + _B7FC63DD-C08F-4CB3-A51A-79C1B8B18E6E + _C6E61C53-FD35-4347-B69E-30AA93AE4404 + _94172225-E124-4F14-98DA-C3D62C11254A + + + _5162ABF0-DD2E-4BDC-9A46-DDCFCB010287 + _59F9A0E6-7F9C-43A9-8920-5B40A91169E6 + _C6E61C53-FD35-4347-B69E-30AA93AE4404 + + + _C62F7EFB-A009-450A-81C7-57D36F0DF766 + _B11455DE-F77A-4251-A85B-4C66636E3CD9 + _B7FC63DD-C08F-4CB3-A51A-79C1B8B18E6E + + + + + + + + _7B41F971-C74D-4036-8A5E-EFF81C37986A + _5334FFDC-1FCB-47E6-8085-36DC9A3D17B9 + System.out.println("###################################"); +System.out.println("HR Interview have been avoided after reasonable time"); +System.out.println("###################################"); + + + + + + + + + _8863B46B-9B0F-40B9-AAB1-A7503CF9AA0A + _5162ABF0-DD2E-4BDC-9A46-DDCFCB010287 + System.out.println("New Hiring has been created for candidate: " + candidateData.getFullName()); + +kcontext.setVariable("hr_approval", false); +kcontext.setVariable("it_approval", false); + + + + + + + + _A76C6603-0406-423C-940B-3403948DCA1F + _C62F7EFB-A009-450A-81C7-57D36F0DF766 + + + + + + + + + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_TaskNameInputX + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_candidateInputX + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_offerInputX + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_approveInputX + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_SkippableInputX + + + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_approveOutputX + + + + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_TaskNameInputX + + + + + + + candidateData + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_candidateInputX + + + offer + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_offerInputX + + + it_approval + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_approveInputX + + + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_SkippableInputX + + + + + + + _8962C15F-55EC-46F7-B926-5D5A1FD8D35E_approveOutputX + it_approval + + + + jdoe + + + + + + + + + + _ACEE7578-B7D2-4EDF-B104-9ECF3DD8A383 + _A76C6603-0406-423C-940B-3403948DCA1F + + + + + + + + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_TaskNameInputX + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_candidateInputX + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_offerInputX + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_approveInputX + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_SkippableInputX + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_approveOutputX + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_offerOutputX + + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_TaskNameInputX + + + + + + + candidateData + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_candidateInputX + + + offer + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_offerInputX + + + hr_approval + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_approveInputX + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_SkippableInputX + + + + + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_approveOutputX + hr_approval + + + _B8C4F63C-81AD-4291-9C1B-84967277EEF6_offerOutputX + offer + + + + jdoe + + + + + _8863B46B-9B0F-40B9-AAB1-A7503CF9AA0A + + + _7B41F971-C74D-4036-8A5E-EFF81C37986A + + PT180S + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _0IqVEG0AEDySCYWhrcdpgA + _0IqVEG0AEDySCYWhrcdpgA + + \ No newline at end of file diff --git a/examples/ping-pong-view-angular/README.md b/examples/ping-pong-view-angular/README.md index c3ad3cedbb0..eaed70e262d 100644 --- a/examples/ping-pong-view-angular/README.md +++ b/examples/ping-pong-view-angular/README.md @@ -1,3 +1,20 @@ + + # Ping-Pong View :: Angular implementation The Ping-Pong View is an interface that components can implement to be used inside a Ping-Pong View Envelope. @@ -9,3 +26,33 @@ It's build into 3 projects: - An Angular application that can run in an iFrame; - A Web Component from the angular components that can be rendered into any page; - A lib used to help render the Web Component in a container. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/ping-pong-view-angular/src/app/ping-pong/ping-pong.component.html b/examples/ping-pong-view-angular/src/app/ping-pong/ping-pong.component.html index 24a8f57569b..4d59d3f26dc 100644 --- a/examples/ping-pong-view-angular/src/app/ping-pong/ping-pong.component.html +++ b/examples/ping-pong-view-angular/src/app/ping-pong/ping-pong.component.html @@ -1,3 +1,21 @@ +

This is an implementation of Ping-Pong View in Angular

diff --git a/examples/ping-pong-view-angular/src/environments/environment.ts b/examples/ping-pong-view-angular/src/environments/environment.ts index 66998ae9a7c..17452f48282 100644 --- a/examples/ping-pong-view-angular/src/environments/environment.ts +++ b/examples/ping-pong-view-angular/src/environments/environment.ts @@ -1,3 +1,22 @@ +/* + * 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. + */ + // This file can be replaced during build by using the `fileReplacements` array. // `ng build` replaces `environment.ts` with `environment.prod.ts`. // The list of file replacements can be found in `angular.json`. diff --git a/examples/ping-pong-view-angular/src/index.html b/examples/ping-pong-view-angular/src/index.html index a36926b59b5..1723453d81e 100644 --- a/examples/ping-pong-view-angular/src/index.html +++ b/examples/ping-pong-view-angular/src/index.html @@ -1,3 +1,21 @@ + diff --git a/examples/ping-pong-view-angular/src/polyfills.ts b/examples/ping-pong-view-angular/src/polyfills.ts index 40a0e9f068d..37e0a2e85a4 100644 --- a/examples/ping-pong-view-angular/src/polyfills.ts +++ b/examples/ping-pong-view-angular/src/polyfills.ts @@ -1,3 +1,22 @@ +/* + * 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. + */ + /** * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. diff --git a/examples/ping-pong-view-angular/src/styles.css b/examples/ping-pong-view-angular/src/styles.css index 90d4ee0072c..3f2e00b1ece 100644 --- a/examples/ping-pong-view-angular/src/styles.css +++ b/examples/ping-pong-view-angular/src/styles.css @@ -1 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + /* You can add global styles to this file, and also import other style files */ diff --git a/examples/ping-pong-view-react/README.md b/examples/ping-pong-view-react/README.md index e16caf8ca98..8dee412811b 100644 --- a/examples/ping-pong-view-react/README.md +++ b/examples/ping-pong-view-react/README.md @@ -1,3 +1,20 @@ + + # Ping-Pong View :: React implementation You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples-how-to-create-a-more-complex-custom-view.html) a step-by-step tutorial of how this custom View was built. @@ -5,3 +22,33 @@ You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples-how-to- The Ping-Pong View is an interface that components can implement to be used inside a Ping-Pong View Envelope. Ths package provides a Ping-Pong View implementation as a React component. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/ping-pong-view/README.md b/examples/ping-pong-view/README.md index d8b9851502e..beaa755ff48 100644 --- a/examples/ping-pong-view/README.md +++ b/examples/ping-pong-view/README.md @@ -1,3 +1,20 @@ + + # Ping-Pong View You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples-how-to-create-a-more-complex-custom-view.html) a step-by-step tutorial of how this custom View was built. @@ -63,3 +80,33 @@ It's divided into the following submodules: } ``` - **pingPongViewFactory** should be an instance of the factory class created in step _1_; + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/sonataflow-greeting-quarkus-example/README.md b/examples/sonataflow-greeting-quarkus-example/README.md new file mode 100644 index 00000000000..f6ad33cc050 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/README.md @@ -0,0 +1,199 @@ + + +# KIE-Tools SonataFlow - Greeting Example + +## Description + +This example contains two simple greeting workflow services. +The services are described using both JSON and YAML formats as defined in the +[CNCF Serverless Workflow specification](https://github.com/cncf/wg-serverless/tree/main/workflow/spec). + +The workflow expects as JSON input containing the name of the person to greet, and the language in +which to greet them in +(see details in the [Submit a request](#Submit-a-request) section). + +The workflow starts with a SWITCH state, which is like a gateway. The switch state +decides which language to greet the person in based on the workflow input "language" parameter. +Depending on the language the workflow then injects the language-based greeting via RELAY states. +Relay states are just "pass" states which do not execute any functions and only have the ability +to inject data into the workflow. +The inject states then transition to the OPERATION state which call a "sysout" function passing it +input parameter containing the greeting and the name of the person to greet: "$.greeting $.name". +The function then prints out the greeting to the console. + +## Installing and Running + +### Prerequisites + +You will need: + +- Java 11+ installed +- Environment variable JAVA_HOME set accordingly +- Maven 3.8.6+ installed + +When using native image compilation, you will also need: + +- [GraalVm](https://www.graalvm.org/downloads/) 19.3.1+ installed +- Environment variable GRAALVM_HOME set accordingly +- Note that GraalVM native image compilation typically requires other packages (glibc-devel, zlib-devel and gcc) to be installed too. You also need 'native-image' installed in GraalVM (using 'gu install native-image'). Please refer to [GraalVM installation documentation](https://www.graalvm.org/docs/reference-manual/aot-compilation/#prerequisites) for more details. + +### Compile and Run in Local Dev Mode + +```sh +mvn clean package quarkus:dev +``` + +### Compile and Run in JVM mode + +```sh +mvn clean package +java -jar target/quarkus-app/quarkus-run.jar +``` + +or on windows + +```sh +mvn clean package +java -jar target\quarkus-app\quarkus-run.jar +``` + +### Compile and Run using Local Native Image + +Note that this requires GRAALVM_HOME to point to a valid GraalVM installation + +```sh +mvn clean package -Pnative +``` + +To run the generated native executable, generated in `target/`, execute + +```sh +./target/sw-quarkus-greeting-{version}-runner +``` + +### Submit a request + +The service based on the JSON workflow definition can be access by sending a request to http://localhost:8080/jsongreet' +with following content + +```json +{ + "name": "John", + "language": "English" +} +``` + +Complete curl command can be found below: + +```sh +curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://localhost:8080/jsongreet +``` + +Log after curl executed: + +```json +{ + "id": "541a5363-1667-4f6d-a8b4-1299eba81eac", + "workflowdata": { "name": "John", "language": "English", "greeting": "Hello from JSON Workflow, " } +} +``` + +In Quarkus you should see the log message printed: + +```text +Hello from JSON Workflow, John +``` + +If you would like to greet the person in Spanish, we need to pass the following data on workflow start: + +```json +{ + "name": "John", + "language": "Spanish" +} +``` + +Complete curl command can be found below: + +```sh +curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "Spanish"}' http://localhost:8080/jsongreet +``` + +In Quarkus you should now see the log message printed: + +```text +Saludos desde JSON Workflow, John +``` + +Similarly, the service based on the YAML workflow definition can be access by sending a request to http://localhost:8080/yamlgreet' +using the same content: + +```json +{ + "name": "John", + "language": "English" +} +``` + +Complete curl command can be found below: + +```sh +curl -X POST -H 'Content-Type:application/json' -H 'Accept:application/json' -d '{"name": "John", "language": "English"}' http://localhost:8080/yamlgreet +``` + +In Quarkus you should see the log message: + +```text +Hello from YAML Workflow, John +``` + +You can also change the language parameter value to "Spanish" to get the greeting in Spanish. + +## Deploying with Kogito Operator + +In the [`operator`](operator) directory you'll find the custom resources needed to deploy this example on OpenShift with the [Kogito Operator](https://docs.jboss.org/kogito/release/latest/html_single/#chap_kogito-deploying-on-openshift). + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/sonataflow-greeting-quarkus-example/env/index.js b/examples/sonataflow-greeting-quarkus-example/env/index.js new file mode 100644 index 00000000000..7767297ccd4 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/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 { + sonataflowGreetingQuarkus: { + version: require("../package.json").version, + }, + }; + }, +}); diff --git a/examples/sonataflow-greeting-quarkus-example/install.js b/examples/sonataflow-greeting-quarkus-example/install.js new file mode 100644 index 00000000000..7230ea08fa9 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/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.sonataflowGreetingQuarkus.version} +`); diff --git a/examples/sonataflow-greeting-quarkus-example/package.json b/examples/sonataflow-greeting-quarkus-example/package.json new file mode 100644 index 00000000000..91d6b0e68aa --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/package.json @@ -0,0 +1,45 @@ +{ + "private": true, + "name": "@kie-tools-examples/sonataflow-greeting-quarkus-example", + "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-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:dev:darwin:linux": "mvn clean package -DskipTests", + "build:dev:win32": "pnpm powershell \"mvn clean package -DskipTests \"", + "build:prod": "pnpm lint && run-script-if --bool \"$(build-env examples.build)\" --then run-script-os", + "build:prod:darwin:linux": "mvn clean package -DskipTests=$(build-env tests.run --not) -Dmaven.test.failure.ignore=$(build-env tests.ignoreFailures)", + "build:prod:win32": "pnpm powershell \"mvn clean package `-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", + "start": "pnpm quarkus:dev" + }, + "dependencies": { + "@kie-tools/maven-base": "workspace:*", + "@kie-tools/sonataflow-quarkus-devui": "workspace:*" + }, + "devDependencies": { + "@kie-tools/maven-config-setup-helper": "workspace:*", + "@kie-tools/root-env": "workspace:*", + "run-script-os": "^1.1.6" + }, + "kieTools": { + "requiredPreinstalledCliCommands": [ + "java", + "mvn" + ] + } +} \ No newline at end of file diff --git a/examples/sonataflow-greeting-quarkus-example/pom.xml b/examples/sonataflow-greeting-quarkus-example/pom.xml new file mode 100644 index 00000000000..8b500cfc5a3 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/pom.xml @@ -0,0 +1,165 @@ + + + + 4.0.0 + + + org.kie + kie-tools-maven-base + ${revision} + ./node_modules/@kie-tools/maven-base/pom.xml + + + org.apache.kie.sonataflow.examples + sonataflow-greeting-quarkus-example + ${revision} + + KIE Tools Example :: SonataFlow Greeting :: Quarkus + SonataFlow Example - Quarkus + + + + + org.apache.kie.sonataflow + sonataflow-quarkus-devui-bom + ${project.version} + pom + import + + + + + + + org.apache.kie.sonataflow + sonataflow-quarkus + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-resteasy-jackson + + + io.quarkus + quarkus-jsonp + + + org.apache.kie.sonataflow + sonataflow-quarkus-devui + + + org.kie + kie-addons-quarkus-source-files + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-smallrye-health + + + + ${project.artifactId} + + + maven-compiler-plugin + ${compiler-plugin.version} + + ${maven.compiler.release} + + + + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + maven-failsafe-plugin + ${version.failsafe.plugin} + + + org.jboss.logmanager.LogManager + ${maven.home} + + + + + + integration-test + verify + + + + + + + + + container + + + container + + + + container + + + + io.quarkus + quarkus-container-image-jib + + + + + native + + + native + + + + native + + + + diff --git a/examples/sonataflow-greeting-quarkus-example/src/main/resources/application.properties b/examples/sonataflow-greeting-quarkus-example/src/main/resources/application.properties new file mode 100644 index 00000000000..bb5e12d81ec --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/src/main/resources/application.properties @@ -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. +# + +# Packaging +# quarkus.package.type=fast-jar +quarkus.native.native-image-xmx=8g + +# profile to pack this example into a container, to use it execute activate the maven container profile, -Dcontainer +%container.quarkus.container-image.build=true +%container.quarkus.container-image.push=false +%container.quarkus.container-image.group=${USER} +%container.quarkus.container-image.registry=dev.local +%container.quarkus.container-image.tag=1.0-SNAPSHOT +%container.quarkus.jib.jvm-entrypoint=/home/kogito/kogito-app-launch.sh +%container.quarkus.jib.base-jvm-image=quay.io/kiegroup/kogito-runtime-jvm:latest +%container.quarkus.jib.working-directory=/home/kogito/bin diff --git a/examples/sonataflow-greeting-quarkus-example/src/main/resources/jsongreet.sw.json b/examples/sonataflow-greeting-quarkus-example/src/main/resources/jsongreet.sw.json new file mode 100644 index 00000000000..5998a47f30b --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/src/main/resources/jsongreet.sw.json @@ -0,0 +1,65 @@ +{ + "id": "jsongreet", + "version": "1.0", + "name": "Greeting workflow", + "description": "JSON based greeting workflow", + "start": "ChooseOnLanguage", + "functions": [ + { + "name": "greetFunction", + "type": "custom", + "operation": "sysout" + } + ], + "states": [ + { + "name": "ChooseOnLanguage", + "type": "switch", + "dataConditions": [ + { + "condition": "${ .language == \"English\" }", + "transition": "GreetInEnglish" + }, + { + "condition": "${ .language == \"Spanish\" }", + "transition": "GreetInSpanish" + } + ], + "defaultCondition": { + "transition": "GreetInEnglish" + } + }, + { + "name": "GreetInEnglish", + "type": "inject", + "data": { + "greeting": "Hello from JSON Workflow, " + }, + "transition": "GreetPerson" + }, + { + "name": "GreetInSpanish", + "type": "inject", + "data": { + "greeting": "Saludos desde JSON Workflow, " + }, + "transition": "GreetPerson" + }, + { + "name": "GreetPerson", + "type": "operation", + "actions": [ + { + "name": "greetAction", + "functionRef": { + "refName": "greetFunction", + "arguments": { + "message": ".greeting+.name" + } + } + } + ], + "end": true + } + ] +} diff --git a/examples/sonataflow-greeting-quarkus-example/src/main/resources/yamlgreet.sw.yml b/examples/sonataflow-greeting-quarkus-example/src/main/resources/yamlgreet.sw.yml new file mode 100644 index 00000000000..108b4dc6a31 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/src/main/resources/yamlgreet.sw.yml @@ -0,0 +1,59 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +id: yamlgreet +version: "1.0" +name: Greeting workflow +description: YAML based greeting workflow +expressionLang: jsonpath +start: ChooseOnLanguage +functions: + - name: greetFunction + type: custom + operation: sysout +states: + - name: ChooseOnLanguage + type: switch + dataConditions: + - condition: "${$.[?(@.language == 'English')]}" + transition: GreetInEnglish + - condition: "${$.[?(@.language == 'Spanish')]}" + transition: GreetInSpanish + defaultCondition: + transition: GreetInEnglish + - name: GreetInEnglish + type: inject + data: + greeting: "Hello from YAML Workflow, " + transition: GreetPerson + - name: GreetInSpanish + type: inject + data: + greeting: "Saludos desde YAML Workflow, " + transition: GreetPerson + - name: GreetPerson + type: operation + actions: + - name: greetAction + functionRef: + refName: greetFunction + arguments: + message: "$.greeting $.name" + end: + terminate: true diff --git a/examples/sonataflow-greeting-quarkus-example/src/test/java/org/kie/kogito/examples/GreetRestIT.java b/examples/sonataflow-greeting-quarkus-example/src/test/java/org/kie/kogito/examples/GreetRestIT.java new file mode 100644 index 00000000000..8c1bd391914 --- /dev/null +++ b/examples/sonataflow-greeting-quarkus-example/src/test/java/org/kie/kogito/examples/GreetRestIT.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.examples; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusIntegrationTest; +import io.restassured.http.ContentType; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.containsString; + +@QuarkusIntegrationTest +class GreetRestIT { + + @Test + void testEnglish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"John\", \"language\":\"English\"}").when() + .post("/jsongreet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Hello")); + } + + @Test + void testSpanish() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"Javierito\", \"language\":\"Spanish\"}").when() + .post("/jsongreet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Saludos")); + } + + @Test + void testDefaultLanguage() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"John\"}").when() + .post("/jsongreet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Hello")); + } + + @Test + void testUnsupportedLanguage() { + given() + .contentType(ContentType.JSON) + .accept(ContentType.JSON) + .body("{\"name\" : \"Jan\", \"language\":\"Czech\"}").when() + .post("/jsongreet") + .then() + .statusCode(201) + .body("workflowdata.greeting", containsString("Hello")); + } +} diff --git a/examples/todo-list-view-vscode-extension/README.md b/examples/todo-list-view-vscode-extension/README.md index 5cd4430ead8..61b35e68e5f 100644 --- a/examples/todo-list-view-vscode-extension/README.md +++ b/examples/todo-list-view-vscode-extension/README.md @@ -1,3 +1,20 @@ + + # 'To do' list View :: VS Code Extension You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8a-%e2%80%8ahow-to-create-a-vs-code-extension-for-a-custom-view.html) a step-by-step tutorial of how this VS Code Extension was built. @@ -20,3 +37,33 @@ This extensions has the following commands: ## Building Run `pnpm build:prod`. A `.vsix` file will be on the `dist` folder. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/todo-list-view/README.md b/examples/todo-list-view/README.md index dc2eb263f17..1784914df28 100644 --- a/examples/todo-list-view/README.md +++ b/examples/todo-list-view/README.md @@ -1,3 +1,20 @@ + + # 'To do' list View You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples-how-to-create-a-custom-view.html) a step-by-step tutorial of how this custom View was built. @@ -14,3 +31,33 @@ It's divided in the following submodules: - Provides the necessary class for a Channel to create a 'To do' list Envelope. 1. `vscode` - Provides a convenience class to create a Webview inside a VS Code Extension. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/uniforms-patternfly/README.md b/examples/uniforms-patternfly/README.md new file mode 100644 index 00000000000..e134e8a5b71 --- /dev/null +++ b/examples/uniforms-patternfly/README.md @@ -0,0 +1,48 @@ + + +## @kie-tools-examples/uniforms-patternfly + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/uniforms-patternfly/src/schemas/json-schema.ts b/examples/uniforms-patternfly/src/schemas/json-schema.ts index d74a2049f15..d2e99e55434 100644 --- a/examples/uniforms-patternfly/src/schemas/json-schema.ts +++ b/examples/uniforms-patternfly/src/schemas/json-schema.ts @@ -27,6 +27,7 @@ function createValidator(schema: any) { return (model: any) => { validator(model); if (validator.errors && validator.errors.length) { + console.error("VALIDATION ERROR: MODEL: ", model); throw { details: validator.errors }; } }; @@ -83,8 +84,8 @@ const schema = { }, numberOfBeds: { placeholder: "Select...", - enum: [1, 2, 3], - type: "number", + enum: [null, 0, 1, 2], + type: ["null", "number"], }, }, }, diff --git a/examples/webapp/README.md b/examples/webapp/README.md index 9a98d7d0202..5c9f54cf72a 100644 --- a/examples/webapp/README.md +++ b/examples/webapp/README.md @@ -1,3 +1,20 @@ + + ## Webapp Example You can read [here](https://blog.kie.org/2020/10/kogito-tooling-examples%e2%80%8a-%e2%80%8ahow-to-integrate-a-custom-editor-an-existing-editors-and-custom-views.html) a step-by-step tutorial of how create this WebApp. @@ -43,3 +60,33 @@ To start the webapp execute the following command on the root folder of the proj ```shell script pnpm -F @kie-tools-examples/webapp start ``` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/examples/webapp/static/examples/sample.bpmn b/examples/webapp/static/examples/sample.bpmn index 5e743d410e1..9c5894125f3 100644 --- a/examples/webapp/static/examples/sample.bpmn +++ b/examples/webapp/static/examples/sample.bpmn @@ -1,3 +1,22 @@ + + diff --git a/examples/webapp/static/examples/sample.dmn b/examples/webapp/static/examples/sample.dmn index cb20a547f50..ac55ca5bb9d 100644 --- a/examples/webapp/static/examples/sample.dmn +++ b/examples/webapp/static/examples/sample.dmn @@ -1,3 +1,22 @@ + + diff --git a/package.json b/package.json index bbf4be36980..3da362533a6 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "format": "prettier --write . '**/*.xml'", "format:check": "prettier --check . '**/*.xml'", "prepare": "husky install", + "update-kogito-version-to": "kie-tools--update-kogito-version-to", + "update-stream-name-to": "kie-tools--update-stream-name-to", "update-version-to": "kie-tools--update-version-to" }, "devDependencies": { @@ -18,10 +20,12 @@ "@kie-tools-scripts/check-junit-report-results": "workspace:*", "@kie-tools-scripts/run-script-if": "workspace:*", "@kie-tools-scripts/sparse-checkout": "workspace:*", + "@kie-tools-scripts/update-kogito-version": "workspace:*", + "@kie-tools-scripts/update-stream-name": "workspace:*", "@kie-tools-scripts/update-version": "workspace:*", "@nice-move/prettier-plugin-package-json": "^0.6.1", "@prettier/plugin-xml": "^2", - "@types/node": "^18.13.0", + "@types/node": "^20.14.2", "husky": "^6.0.0", "postinstall-postinstall": "^2.1.0", "prettier": "^2.8.8", @@ -32,10 +36,10 @@ "react-dropzone": "^11.4.2" }, "engines": { - "node": ">=18", - "pnpm": "8.7.0" + "node": ">=20", + "pnpm": "9.3.0" }, - "packageManager": "pnpm@8.7.0", + "packageManager": "pnpm@9.3.0", "pnpm": { "packageExtensions": { "monaco-editor-webpack-plugin": { diff --git a/packages/backend/README.md b/packages/backend/README.md index 4efd6045a52..f40e7e8f5fd 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -1 +1,48 @@ + + Apache KIE Tools Backend API + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/backend/package.json b/packages/backend/package.json index 37b3e515f02..b7803f3e118 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -26,7 +26,7 @@ "@kie-tools-core/i18n": "workspace:*", "@kie-tools-core/notifications": "workspace:*", "@kie-tools-core/workspace": "workspace:*", - "axios": "^1.6.4", + "axios": "^1.6.8", "fast-xml-parser": "^4.3.1", "portfinder": "^1.0.32", "semver": "^7.5.4", diff --git a/packages/boxed-expression-component/README.md b/packages/boxed-expression-component/README.md index bbcde626a6c..07ff0b92702 100644 --- a/packages/boxed-expression-component/README.md +++ b/packages/boxed-expression-component/README.md @@ -1,3 +1,20 @@ + + # Boxed Expression Editor This editor provides the possibility to edit the expression related to a Decision Node, or to a Business Knowledge Model's function. @@ -58,3 +75,33 @@ pnpm test:e2e # To update the PlayWright Snapshot files, used for the regression: pnpm test:e2e:run -u ``` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/boxed-expression-component/src/BoxedExpressionEditor.tsx b/packages/boxed-expression-component/src/BoxedExpressionEditor.tsx index 30e4a095b4a..29c524e421c 100644 --- a/packages/boxed-expression-component/src/BoxedExpressionEditor.tsx +++ b/packages/boxed-expression-component/src/BoxedExpressionEditor.tsx @@ -20,7 +20,7 @@ import "@patternfly/react-styles/css/components/Drawer/drawer.css"; import { I18nDictionariesProvider } from "@kie-tools-core/i18n/dist/react-components"; import * as React from "react"; -import { BeeGwtService, DmnDataType, BoxedExpression, PmmlDocument } from "./api"; +import { BeeGwtService, BoxedExpression, DmnDataType, PmmlDocument } from "./api"; import { boxedExpressionEditorDictionaries, BoxedExpressionEditorI18nContext, @@ -42,7 +42,7 @@ export interface BoxedExpressionEditorProps { /** Name of the Decision or BKM containing `expression` */ expressionHolderName: string; /** TypeRef of the Decision or BKM containing `expression` */ - expressionHolderTypeRef: string; + expressionHolderTypeRef: string | undefined; /** The boxed expression itself */ expression: BoxedExpression | undefined; /** Called every time something changes on the expression */ diff --git a/packages/boxed-expression-component/src/api/BeeGwtService.ts b/packages/boxed-expression-component/src/api/BeeGwtService.ts index 4e85810e2c1..8a6b6b18762 100644 --- a/packages/boxed-expression-component/src/api/BeeGwtService.ts +++ b/packages/boxed-expression-component/src/api/BeeGwtService.ts @@ -25,7 +25,7 @@ import { BoxedExpression } from "./BoxedExpression"; export interface BeeGwtService { getDefaultExpressionDefinition( logicType: BoxedExpression["__$$element"] | undefined, - typeRef: string, + typeRef: string | undefined, isRoot?: boolean ): { expression: BoxedExpression; widthsById: Map }; diff --git a/packages/boxed-expression-component/src/contextMenu/MenuWithHelp/MenuItemWithHelp.tsx b/packages/boxed-expression-component/src/contextMenu/MenuWithHelp/MenuItemWithHelp.tsx index e6be10abb6c..aa488fc2b60 100644 --- a/packages/boxed-expression-component/src/contextMenu/MenuWithHelp/MenuItemWithHelp.tsx +++ b/packages/boxed-expression-component/src/contextMenu/MenuWithHelp/MenuItemWithHelp.tsx @@ -52,7 +52,10 @@ export function MenuItemWithHelp({ actions={ (isHovered || visibleHelp === menuItemHelp) && ( setVisibleHelp(menuItemHelp)} + onClick={(e) => { + e.stopPropagation(); + setVisibleHelp(menuItemHelp); + }} icon={} actionId={menuItemKey + "-help"} aria-label={menuItemKey + "-help"} diff --git a/packages/boxed-expression-component/src/expressionVariable/DataTypeSelector.tsx b/packages/boxed-expression-component/src/expressionVariable/DataTypeSelector.tsx index 95679d7e630..4c33f5b02c1 100644 --- a/packages/boxed-expression-component/src/expressionVariable/DataTypeSelector.tsx +++ b/packages/boxed-expression-component/src/expressionVariable/DataTypeSelector.tsx @@ -19,18 +19,18 @@ import { Select, SelectGroup, SelectOption, SelectVariant } from "@patternfly/react-core/dist/js/components/Select"; import * as React from "react"; -import { useCallback, useState, useRef, useMemo } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; import { useBoxedExpressionEditorI18n } from "../i18n"; import * as _ from "lodash"; -import { DmnDataType } from "../api"; +import { DmnBuiltInDataType, DmnDataType } from "../api"; import { useBoxedExpressionEditor } from "../BoxedExpressionEditorContext"; import { Divider } from "@patternfly/react-core/dist/js/components/Divider"; export interface DataTypeSelectorProps { /** The pre-selected data type */ - value: string; + value: string | undefined; /** On DataType selection callback */ - onChange: (dataType: string) => void; + onChange: (dataType: string | undefined) => void; /** By default the menu will be appended inline, but it is possible to append on the parent or on other elements */ /** Callback for toggle select behavior */ onToggle?: (isOpen: boolean) => void; @@ -41,6 +41,7 @@ export interface DataTypeSelectorProps { /** This is the optimal height for the dropdown menu for the "Data Type" */ const DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT = 500; +const MININAL_SELECT_DATA_TYPE_MENU_HEIGHT = 70; /** This margin is the height of the status bar in the on-line editor because it can't be overlaped */ const POPUP_BOTTOM_MARGIN = 46; @@ -64,7 +65,7 @@ export const DataTypeSelector: React.FunctionComponent = /* this setTimeout keeps the context menu open after type selection changes. Without this Popover component thinks there has been a click outside the context menu, after DataTypeSelector has changed. This because the Select component has been removed from the html*/ setTimeout(() => setOpen(false), 0); - onChange(selection); + onChange(selection === DmnBuiltInDataType.Undefined ? undefined : selection); // Because Select leave the focus to the detached btn, give back the focus to the selectWrapperRef (selectContainerRef.current?.querySelector("button") as HTMLInputElement)?.focus(); @@ -108,7 +109,7 @@ export const DataTypeSelector: React.FunctionComponent = return buildSelectGroups().reduce((acc, group) => { const filteredGroup = React.cloneElement(group, { children: group.props?.children?.filter((item: React.ReactElement) => - item.props.value.toLowerCase().includes(textInput.toLowerCase()) + (item.props.value ?? DmnBuiltInDataType.Undefined).toLowerCase().includes(textInput.toLowerCase()) ), }); @@ -140,7 +141,10 @@ export const DataTypeSelector: React.FunctionComponent = DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT + yPos > availableHeight ) { const offset = DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT + yPos - availableHeight; - return DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT - offset - POPUP_BOTTOM_MARGIN; + const calculatedHeight = DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT - offset - POPUP_BOTTOM_MARGIN; + return calculatedHeight < MININAL_SELECT_DATA_TYPE_MENU_HEIGHT + ? MININAL_SELECT_DATA_TYPE_MENU_HEIGHT + : calculatedHeight; } } return DEFAULT_SELECT_DATA_TYPE_MENU_HEIGHT; @@ -157,7 +161,7 @@ export const DataTypeSelector: React.FunctionComponent = onSelect={onSelect} onFilter={onFilter} isOpen={isOpen} - selections={value} + selections={value ?? DmnBuiltInDataType.Undefined} isGrouped={true} hasInlineFilter={true} inlineFilterPlaceholderText={i18n.choose} diff --git a/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableCell.tsx b/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableCell.tsx index 735c0c32c02..aeaab2fe45c 100644 --- a/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableCell.tsx +++ b/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableCell.tsx @@ -49,7 +49,7 @@ export const ExpressionVariableCell: React.FunctionComponent< const { expression, variable, index } = data[rowIndex]; const onVariableUpdated = useCallback( - ({ name = DEFAULT_EXPRESSION_VARIABLE_NAME, typeRef = DmnBuiltInDataType.Undefined }) => { + ({ name = DEFAULT_EXPRESSION_VARIABLE_NAME, typeRef = undefined }) => { onExpressionWithVariableUpdated(index, { // `expression` and `variable` must always have the same `typeRef` and `name/label`, as those are dictated by `variable`. expression: expression @@ -99,7 +99,7 @@ export const ExpressionVariableCell: React.FunctionComponent< rowIndex, columnIndex, undefined, - useCallback(() => `${variable["@_name"]} (${variable["@_typeRef"] ?? DmnBuiltInDataType.Undefined}})`, [variable]) + useCallback(() => `${variable["@_name"]} (${variable["@_typeRef"]}})`, [variable]) ); const { beeGwtService } = useBoxedExpressionEditor(); diff --git a/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableMenu.tsx b/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableMenu.tsx index c257fdea2c5..03e50bfe219 100644 --- a/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableMenu.tsx +++ b/packages/boxed-expression-component/src/expressionVariable/ExpressionVariableMenu.tsx @@ -18,10 +18,9 @@ */ import * as React from "react"; -import { useCallback, useEffect, useState, useRef } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { PopoverMenu, PopoverMenuRef } from "../contextMenu/PopoverMenu"; import { useBoxedExpressionEditorI18n } from "../i18n"; -import { DmnBuiltInDataType, BoxedExpression } from "../api"; import { useBoxedExpressionEditor } from "../BoxedExpressionEditorContext"; import { DataTypeSelector } from "./DataTypeSelector"; import { CogIcon } from "@patternfly/react-icons/dist/js/icons/cog-icon"; @@ -30,7 +29,7 @@ import { NavigationKeysUtils } from "../keysUtils/keyUtils"; import { PopoverPosition } from "@patternfly/react-core/dist/js/components/Popover"; import "./ExpressionVariableMenu.css"; -export type OnExpressionVariableUpdated = (args: { name: string; typeRef: string }) => void; +export type OnExpressionVariableUpdated = (args: { name: string; typeRef: string | undefined }) => void; export interface ExpressionVariableMenuProps { /** Optional children element to be considered for triggering the edit expression menu */ @@ -62,7 +61,7 @@ export function ExpressionVariableMenu({ arrowPlacement, nameField, dataTypeField, - selectedDataType = DmnBuiltInDataType.Undefined, + selectedDataType = undefined, selectedExpressionName, onVariableUpdated, position, @@ -92,7 +91,7 @@ export function ExpressionVariableMenu({ setExpressionName(event.target.value); }, []); - const onDataTypeChange = useCallback((dataType: DmnBuiltInDataType) => { + const onDataTypeChange = useCallback((dataType: string | undefined) => { setDataType(dataType); }, []); diff --git a/packages/boxed-expression-component/src/expressions/ConditionalExpression/ConditionalExpressionCell.tsx b/packages/boxed-expression-component/src/expressions/ConditionalExpression/ConditionalExpressionCell.tsx index c50aa319963..58a89c8853c 100644 --- a/packages/boxed-expression-component/src/expressions/ConditionalExpression/ConditionalExpressionCell.tsx +++ b/packages/boxed-expression-component/src/expressions/ConditionalExpression/ConditionalExpressionCell.tsx @@ -1,3 +1,22 @@ +/* + * 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. + */ + import { BeeTableCellProps, BoxedConditional } from "../../api"; import { diff --git a/packages/boxed-expression-component/src/expressions/ContextExpression/ContextExpression.tsx b/packages/boxed-expression-component/src/expressions/ContextExpression/ContextExpression.tsx index 1bb8078b966..c3b1dee1373 100644 --- a/packages/boxed-expression-component/src/expressions/ContextExpression/ContextExpression.tsx +++ b/packages/boxed-expression-component/src/expressions/ContextExpression/ContextExpression.tsx @@ -35,11 +35,11 @@ import { import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useNestedExpressionContainerWithNestedExpressions } from "../../resizing/Hooks"; import { NestedExpressionContainerContext } from "../../resizing/NestedExpressionContainerContext"; -import { ResizerStopBehavior, ResizingWidth, useResizingWidths } from "../../resizing/ResizingWidthsContext"; +import { ResizerStopBehavior, ResizingWidth } from "../../resizing/ResizingWidthsContext"; import { CONTEXT_ENTRY_EXPRESSION_MIN_WIDTH, - CONTEXT_ENTRY_VARIABLE_MIN_WIDTH, CONTEXT_ENTRY_VARIABLE_COLUMN_WIDTH_INDEX, + CONTEXT_ENTRY_VARIABLE_MIN_WIDTH, CONTEXT_EXPRESSION_EXTRA_WIDTH, } from "../../resizing/WidthConstants"; import { useBeeTableCoordinates, useBeeTableSelectableCellRef } from "../../selection/BeeTableSelectionContext"; @@ -49,13 +49,11 @@ import { DEFAULT_EXPRESSION_VARIABLE_NAME } from "../../expressionVariable/Expre import { ContextEntryExpressionCell } from "./ContextEntryExpressionCell"; import { ExpressionVariableCell, ExpressionWithVariable } from "../../expressionVariable/ExpressionVariableCell"; import { ContextResultExpressionCell } from "./ContextResultExpressionCell"; -import { getExpressionMinWidth, getExpressionTotalMinWidth } from "../../resizing/WidthMaths"; +import { getExpressionTotalMinWidth } from "../../resizing/WidthMaths"; import { DMN15__tContextEntry } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { findAllIdsDeep } from "../../ids/ids"; import "./ContextExpression.css"; -const CONTEXT_ENTRY_DEFAULT_DATA_TYPE = DmnBuiltInDataType.Undefined; - export type ROWTYPE = ExpressionWithVariable & { index: number }; export function ContextExpression({ @@ -156,7 +154,7 @@ export function ContextExpression({ accessor: expressionHolderId as any, // FIXME: https://github.com/apache/incubator-kie-issues/issues/169 label: contextExpression["@_label"] ?? DEFAULT_EXPRESSION_VARIABLE_NAME, isRowIndexColumn: false, - dataType: contextExpression["@_typeRef"] ?? CONTEXT_ENTRY_DEFAULT_DATA_TYPE, + dataType: contextExpression["@_typeRef"] ?? DmnBuiltInDataType.Undefined, width: undefined, columns: [ { @@ -286,7 +284,7 @@ export function ContextExpression({ variable: { "@_id": generateUuid(), "@_name": variableName, - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, description: { __$$text: "" }, }, }; diff --git a/packages/boxed-expression-component/src/expressions/DecisionTableExpression/DecisionTableExpression.tsx b/packages/boxed-expression-component/src/expressions/DecisionTableExpression/DecisionTableExpression.tsx index c9af257bcc4..27332a19b8d 100644 --- a/packages/boxed-expression-component/src/expressions/DecisionTableExpression/DecisionTableExpression.tsx +++ b/packages/boxed-expression-component/src/expressions/DecisionTableExpression/DecisionTableExpression.tsx @@ -62,7 +62,6 @@ import { DMN15__tInputClause, DMN15__tLiteralExpression, DMN15__tOutputClause, - DMN15__tRuleAnnotation, DMN15__tRuleAnnotationClause, DMN15__tUnaryTests, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; @@ -728,7 +727,7 @@ export function DecisionTableExpression({ "@_id": generateUuid(), inputExpression: { "@_id": generateUuid(), - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, text: { __$$text: newName }, }, }); @@ -768,7 +767,7 @@ export function DecisionTableExpression({ outputColumnsToAdd.push({ "@_id": generateUuid(), "@_name": name, - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, }); } diff --git a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionContainer.tsx b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionContainer.tsx index e5108118450..bc1f446fd2e 100644 --- a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionContainer.tsx +++ b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionContainer.tsx @@ -20,7 +20,7 @@ import * as React from "react"; import { useCallback, useEffect, useRef } from "react"; import { useBoxedExpressionEditor, useBoxedExpressionEditorDispatch } from "../../BoxedExpressionEditorContext"; -import { BoxedExpression, DmnBuiltInDataType, generateUuid } from "../../api"; +import { BoxedExpression, generateUuid } from "../../api"; import { findAllIdsDeep } from "../../ids/ids"; import { DEFAULT_EXPRESSION_VARIABLE_NAME } from "../../expressionVariable/ExpressionVariableMenu"; import { useBeeTableSelectableCellRef } from "../../selection/BeeTableSelectionContext"; @@ -65,11 +65,7 @@ export const ExpressionContainer: React.FunctionComponent { const { expression: defaultExpression, widthsById: defaultWidthsById } = - beeGwtService!.getDefaultExpressionDefinition( - logicType, - parentElementTypeRef ?? expressionTypeRef ?? DmnBuiltInDataType.Undefined, - !isNested - ); + beeGwtService!.getDefaultExpressionDefinition(logicType, parentElementTypeRef ?? expressionTypeRef, !isNested); setExpression((prev: BoxedExpression) => { // Do not inline this variable for type safety. See https://github.com/microsoft/TypeScript/issues/241 diff --git a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionLogicTypeSelector.tsx b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionLogicTypeSelector.tsx index 361f27af11a..466a7ea4a8c 100644 --- a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionLogicTypeSelector.tsx +++ b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionLogicTypeSelector.tsx @@ -33,11 +33,10 @@ import { RebootingIcon } from "@patternfly/react-icons/dist/js/icons/rebooting-i import { ResourcesAlmostEmptyIcon } from "@patternfly/react-icons/dist/js/icons/resources-almost-empty-icon"; import { ResourcesFullIcon } from "@patternfly/react-icons/dist/js/icons/resources-full-icon"; import * as React from "react"; -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react"; import { BoxedExpression } from "../../api"; import { useCustomContextMenuHandler } from "../../contextMenu"; import { MenuItemWithHelp } from "../../contextMenu/MenuWithHelp"; -import { PopoverMenu } from "../../contextMenu/PopoverMenu"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useNestedExpressionContainer } from "../../resizing/NestedExpressionContainerContext"; import { useBoxedExpressionEditor, useBoxedExpressionEditorDispatch } from "../../BoxedExpressionEditorContext"; @@ -76,11 +75,14 @@ export interface ExpressionDefinitionLogicTypeSelectorProps { hideDmn14BoxedExpressions?: boolean; } +const DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT = 550; +const LOGIC_TYPE_SELECTOR_BOTTOM_MARGIN = 5; +const PASTE_MENU_ITEM_ID = "paste"; + export function ExpressionDefinitionLogicTypeSelector({ expression, onLogicTypeSelected, onLogicTypeReset, - getPlacementRef, isResetSupported, isNested, parentElementId, @@ -90,6 +92,27 @@ export function ExpressionDefinitionLogicTypeSelector({ () => (isNested ? new Set([undefined]) : new Set([undefined, "functionDefinition"])), [isNested] ); + const { i18n } = useBoxedExpressionEditorI18n(); + const { setCurrentlyOpenContextMenu, widthsById, scrollableParentRef } = useBoxedExpressionEditor(); + const [isOpen, setOpen] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + + const showSelectExpression = useCallback( + (e: React.MouseEvent) => { + setPosition({ + x: + e.pageX + + (scrollableParentRef.current?.offsetLeft ?? 0) - + (scrollableParentRef.current?.getBoundingClientRect().left ?? 0), + y: + e.pageY + + (scrollableParentRef.current?.offsetTop ?? 0) - + (scrollableParentRef.current?.getBoundingClientRect().top ?? 0), + }); + setOpen(true); + }, + [scrollableParentRef] + ); const selectableLogicTypes = useMemo>( () => [ @@ -101,17 +124,15 @@ export function ExpressionDefinitionLogicTypeSelector({ "invocation", ...(isNested ? (["functionDefinition"] as const) : []), ...(!hideDmn14BoxedExpressions ? (["conditional"] as const) : []), - "for", - "every", - "some", - "filter", + ...(!hideDmn14BoxedExpressions ? (["for"] as const) : []), + ...(!hideDmn14BoxedExpressions ? (["every"] as const) : []), + ...(!hideDmn14BoxedExpressions ? (["some"] as const) : []), + ...(!hideDmn14BoxedExpressions ? (["filter"] as const) : []), ], [hideDmn14BoxedExpressions, isNested] ); - const { i18n } = useBoxedExpressionEditorI18n(); - - const { setCurrentlyOpenContextMenu, editorRef, widthsById } = useBoxedExpressionEditor(); + const selectLogicTypeContainer = useRef(null); const renderExpression = useMemo(() => { const logicType = expression?.__$$element; @@ -152,20 +173,15 @@ export function ExpressionDefinitionLogicTypeSelector({ } }, [expression, isNested, parentElementId]); - const getPopoverArrowPlacement = useCallback(() => { - return getPlacementRef() as HTMLDivElement; - }, [getPlacementRef]); - - const getPopoverContainer = useCallback(() => { - return editorRef?.current ?? getPopoverArrowPlacement; - }, [getPopoverArrowPlacement, editorRef]); - const selectLogicType = useCallback( - (_: React.MouseEvent, itemId?: string | number) => { - onLogicTypeSelected(itemId as BoxedExpression["__$$element"] | undefined); + (mouseEvent: React.MouseEvent, itemId?: string | number) => { + if (itemId !== PASTE_MENU_ITEM_ID) { + onLogicTypeSelected(itemId as BoxedExpression["__$$element"] | undefined); + } setCurrentlyOpenContextMenu(undefined); - setPasteExpressionError(""); setVisibleHelp(""); + setOpen(false); + mouseEvent.stopPropagation(); }, [onLogicTypeSelected, setCurrentlyOpenContextMenu] ); @@ -195,6 +211,27 @@ export function ExpressionDefinitionLogicTypeSelector({ return isResetContextMenuOpen && expression && isResetSupported; }, [isResetContextMenuOpen, isResetSupported, expression]); + const selectExpressionMenuContainerRef = React.useRef(null); + + const shouldRenderSelectExpressionContextMenu = useMemo(() => { + return isOpen && !expression; + }, [isOpen, expression]); + + const hide = useCallback((e: MouseEvent) => { + if (e.target != selectExpressionMenuContainerRef.current) { + setOpen(false); + } + }, []); + + useEffect(() => { + if (isOpen) { + document.addEventListener("click", hide); + } + return () => { + document.removeEventListener("click", hide); + }; + }, [hide, isOpen]); + const logicTypeIcon = useCallback((logicType: BoxedExpression["__$$element"] | undefined) => { switch (logicType) { case undefined: @@ -347,7 +384,7 @@ export function ExpressionDefinitionLogicTypeSelector({ case "for": return ( "A boxed iterator offers a visual representation of an iterator statement. " + - 'For the for loop, the right part of the "for" displays the iterator variable name. The second row holds an expression representing the collection that will be iterated over. The expression in the "in" row MUST resolve to a collection.' + + 'For the "for" loop, the right part of the "for" displays the iterator variable name. The second row holds an expression representing the collection that will be iterated over. The expression in the "in" row MUST resolve to a collection.' + " The last row contains the expression that will process each element of the collection." ); @@ -355,13 +392,13 @@ export function ExpressionDefinitionLogicTypeSelector({ return ( "A boxed iterator offers a visual representation of an iterator statement. " + 'For the "every" loop, the right part of the "every" displays the iterator variable name. The second row holds an expression representing the collection that will be iterated over. The expression in the "in" row MUST resolve to a collection.' + - "The last line is an expression that will be evaluated on each item. The expression defined in the satisfies MUST resolve to a boolean." + 'The last line is an expression that will be evaluated on each item. The expression defined in the "satisfies" MUST resolve to a boolean.' ); case "some": return ( "A boxed iterator offers a visual representation of an iterator statement. " + 'For the "some" loop, the right part of the "some" displays the iterator variable name. The second row holds an expression representing the collection that will be iterated over. The expression in the "in" row MUST resolve to a collection. ' + - "The last line is an expression that will be evaluated on each item. The expression defined in the satisfies MUST resolve to a boolean." + 'The last line is an expression that will be evaluated on each item. The expression defined in the "satisfies" MUST resolve to a boolean.' ); case "filter": return ( @@ -454,135 +491,186 @@ export function ExpressionDefinitionLogicTypeSelector({ setVisibleHelp((previousHelp) => (previousHelp !== help ? help : "")); }, []); - return ( - <> -
{ + if (selectLogicTypeContainer.current) { + const boundingClientRect = selectLogicTypeContainer.current?.getBoundingClientRect(); + if (boundingClientRect) { + const yPos = boundingClientRect.top; + const availableHeight = document.documentElement.clientHeight; + if (DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT + yPos > availableHeight) { + const offset = + DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT + yPos - availableHeight - LOGIC_TYPE_SELECTOR_BOTTOM_MARGIN; + selectLogicTypeContainer.current.style.height = DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT - offset + "px"; + selectLogicTypeContainer.current.style.overflowY = "scroll"; + } else { + selectLogicTypeContainer.current.style.height = DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT + "px"; + selectLogicTypeContainer.current.style.overflowY = "visible"; } - > - {expression ? ( - <> - {showExpressionHeader && ( -
- { - if (NavigationKeysUtils.isEsc(e.key)) { - setDropdownOpen(false); - } - }} - toggle={ - {logicTypeIcon(expression.__$$element)}} - style={{ padding: 0 }} - onToggle={setDropdownOpen} - tabIndex={-1} - > - {getLogicTypeLabel(expression?.__$$element)} - {expression.__$$element === "functionDefinition" && ` (${expression["@_kind"]})`} - - } + } else { + selectLogicTypeContainer.current.style.height = DEFAULT_LOGIC_TYPE_SELECTOR_HEIGHT + "px"; + selectLogicTypeContainer.current.style.overflowY = "visible"; + } + } + }); + + const getRenderExpressionElement = useCallback(() => { + return ( + <> + {showExpressionHeader && expression && ( +
+ { + if (NavigationKeysUtils.isEsc(e.key)) { + setDropdownOpen(false); + } + }} + toggle={ + {logicTypeIcon(expression.__$$element)}} + style={{ padding: 0 }} + onToggle={setDropdownOpen} + tabIndex={-1} > - - <>{contextMenuItems} - - -
- )} - {renderExpression} - - ) : ( - i18n.selectExpression + {getLogicTypeLabel(expression?.__$$element)} + {expression.__$$element === "functionDefinition" && ` (${expression["@_kind"]})`} + + } + > + + <>{contextMenuItems} + +
+
)} + {renderExpression} + + ); + }, [contextMenuItems, expression, isDropdownOpen, logicTypeIcon, renderExpression, showExpressionHeader]); - {!expression && ( - { - setPasteExpressionError(""); - setVisibleHelp(""); - }} - arrowPlacement={getPopoverArrowPlacement} - appendTo={getPopoverContainer()} - className="logic-type-popover" - hasAutoWidth={true} - body={ - <> - - - - <> - {selectableLogicTypes.map((key) => { - const label = getLogicTypeLabel(key); - return ( - - ); - })} - - - - - - - - - -
- } - > - {i18n.terms.paste} - - - - - } - /> - )} + const getSelectExpressionElement = useCallback(() => { + return ( +
+ + + + {selectableLogicTypes.map((key) => { + const label = getLogicTypeLabel(key); + return ( + + ); + })} + + + +
+ } + > + {i18n.terms.paste} + + + +
- {shouldRenderResetContextMenu && ( + ); + }, [ + i18n.terms.paste, + logicTypeHelp, + logicTypeIcon, + menuIconContainerStyle, + pasteExpression, + pasteExpressionError, + position.x, + position.y, + selectLogicType, + selectableLogicTypes, + toggleVisibleHelp, + visibleHelp, + ]); + + const getResetExpressionElement = useCallback(() => { + return ( +
{ + if (NavigationKeysUtils.isEsc(e.key)) { + setDropdownOpen(false); + } + }} + > + + + {contextMenuItems} + +
+ ); + }, [contextMenuItems, expression?.__$$element, resetContextMenuXPos, resetContextMenuYPos]); + + return ( + <> + {!expression && (
{ - if (NavigationKeysUtils.isEsc(e.key)) { - setDropdownOpen(false); - } - }} + className={cssClass} + ref={selectExpressionMenuContainerRef} + onClick={showSelectExpression} + style={ + !expression && nestedExpressionContainer.resizingWidth + ? { width: `${nestedExpressionContainer.resizingWidth?.value}px` } + : {} + } + > + {i18n.selectExpression} + {!expression && shouldRenderSelectExpressionContextMenu && getSelectExpressionElement()} +
+ )} + + {expression && ( +
- - - {contextMenuItems} - + {expression ? getRenderExpressionElement() : i18n.selectExpression}
)} + {shouldRenderResetContextMenu && getResetExpressionElement()} ); } diff --git a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionRoot.tsx b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionRoot.tsx index 0ad8abb2907..3139416ed87 100644 --- a/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionRoot.tsx +++ b/packages/boxed-expression-component/src/expressions/ExpressionDefinitionRoot/ExpressionDefinitionRoot.tsx @@ -25,7 +25,7 @@ import "./ExpressionDefinitionRoot.css"; export interface ExpressionDefinitionRootProps { expressionHolderId: string; - expressionHolderTypeRef: string; + expressionHolderTypeRef: string | undefined; expression?: BoxedExpression; isResetSupported: boolean | undefined; expressionHolderName?: string; diff --git a/packages/boxed-expression-component/src/expressions/FilterExpression/FilterExpression.css b/packages/boxed-expression-component/src/expressions/FilterExpression/FilterExpression.css index 44d66b203e8..d436886b4da 100644 --- a/packages/boxed-expression-component/src/expressions/FilterExpression/FilterExpression.css +++ b/packages/boxed-expression-component/src/expressions/FilterExpression/FilterExpression.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .filter-expression-cell { display: flex; align-content: center; diff --git a/packages/boxed-expression-component/src/expressions/FunctionExpression/FeelFunctionExpression.tsx b/packages/boxed-expression-component/src/expressions/FunctionExpression/FeelFunctionExpression.tsx index 91be6514c94..2bc3aa89ffb 100644 --- a/packages/boxed-expression-component/src/expressions/FunctionExpression/FeelFunctionExpression.tsx +++ b/packages/boxed-expression-component/src/expressions/FunctionExpression/FeelFunctionExpression.tsx @@ -27,10 +27,10 @@ import { BeeTableOperation, BeeTableOperationConfig, BeeTableProps, - DmnBuiltInDataType, BoxedExpression, BoxedFunction, BoxedFunctionKind, + DmnBuiltInDataType, } from "../../api"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useNestedExpressionContainerWithNestedExpressions } from "../../resizing/Hooks"; diff --git a/packages/boxed-expression-component/src/expressions/FunctionExpression/FunctionExpression.tsx b/packages/boxed-expression-component/src/expressions/FunctionExpression/FunctionExpression.tsx index 5875422b6c4..48a36374342 100644 --- a/packages/boxed-expression-component/src/expressions/FunctionExpression/FunctionExpression.tsx +++ b/packages/boxed-expression-component/src/expressions/FunctionExpression/FunctionExpression.tsx @@ -20,15 +20,15 @@ import _ from "lodash"; import * as React from "react"; import { useCallback, useMemo } from "react"; -import { DmnBuiltInDataType, BoxedFunction, BoxedFunctionKind, generateUuid } from "../../api"; +import { BoxedFunction, BoxedFunctionKind, DmnBuiltInDataType, generateUuid } from "../../api"; import { PopoverMenu } from "../../contextMenu/PopoverMenu"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useBoxedExpressionEditor, useBoxedExpressionEditorDispatch } from "../../BoxedExpressionEditorContext"; -import { FeelFunctionExpression, BoxedFunctionFeel } from "./FeelFunctionExpression"; +import { BoxedFunctionFeel, FeelFunctionExpression } from "./FeelFunctionExpression"; import { FunctionKindSelector } from "./FunctionKindSelector"; -import { JavaFunctionExpression, BoxedFunctionJava } from "./JavaFunctionExpression"; +import { BoxedFunctionJava, JavaFunctionExpression } from "./JavaFunctionExpression"; import { ParametersPopover } from "./ParametersPopover"; -import { PmmlFunctionExpression, BoxedFunctionPmml } from "./PmmlFunctionExpression"; +import { BoxedFunctionPmml, PmmlFunctionExpression } from "./PmmlFunctionExpression"; import { DMN15__tFunctionDefinition, DMN15__tFunctionKind, @@ -86,11 +86,11 @@ export function useFunctionExpressionControllerCell(functionKind: DMN15__tFuncti "@_label": prev["@_label"], "@_id": generateUuid(), "@_kind": BoxedFunctionKind.Feel, - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, expression: { __$$element: "literalExpression", "@_id": generateUuid(), - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, }, formalParameter: [], }; @@ -108,7 +108,7 @@ export function useFunctionExpressionControllerCell(functionKind: DMN15__tFuncti "@_id": generateUuid(), }, "@_kind": BoxedFunctionKind.Java, - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, formalParameter: [], }; return retJava; @@ -123,7 +123,7 @@ export function useFunctionExpressionControllerCell(functionKind: DMN15__tFuncti "@_id": generateUuid(), }, "@_kind": BoxedFunctionKind.Pmml, - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, formalParameter: [], }; return retPmml; @@ -167,7 +167,9 @@ export function useFunctionExpressionParametersColumnHeader( {parameter["@_name"]} {": "} - ({parameter["@_typeRef"]}) + + ({parameter["@_typeRef"] ?? DmnBuiltInDataType.Undefined}) + {i < (formalParameters ?? []).length - 1 && {", "}} ))} diff --git a/packages/boxed-expression-component/src/expressions/FunctionExpression/JavaFunctionExpression.tsx b/packages/boxed-expression-component/src/expressions/FunctionExpression/JavaFunctionExpression.tsx index ddf0285725d..430ca10c3f1 100644 --- a/packages/boxed-expression-component/src/expressions/FunctionExpression/JavaFunctionExpression.tsx +++ b/packages/boxed-expression-component/src/expressions/FunctionExpression/JavaFunctionExpression.tsx @@ -29,10 +29,10 @@ import { BeeTableOperation, BeeTableOperationConfig, BeeTableProps, - DmnBuiltInDataType, + BoxedFunction, BoxedFunctionKind, + DmnBuiltInDataType, generateUuid, - BoxedFunction, } from "../../api"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { usePublishedBeeTableResizableColumns } from "../../resizing/BeeTableResizableColumnsContext"; diff --git a/packages/boxed-expression-component/src/expressions/FunctionExpression/ParametersPopover.tsx b/packages/boxed-expression-component/src/expressions/FunctionExpression/ParametersPopover.tsx index 12bd285985c..db5aed31578 100644 --- a/packages/boxed-expression-component/src/expressions/FunctionExpression/ParametersPopover.tsx +++ b/packages/boxed-expression-component/src/expressions/FunctionExpression/ParametersPopover.tsx @@ -24,7 +24,7 @@ import { CubesIcon } from "@patternfly/react-icons/dist/js/icons/cubes-icon"; import { OutlinedTrashAltIcon } from "@patternfly/react-icons/dist/js/icons/outlined-trash-alt-icon"; import * as React from "react"; import { ChangeEvent, useCallback } from "react"; -import { DmnBuiltInDataType, BoxedFunction, generateUuid, getNextAvailablePrefixedName } from "../../api"; +import { BoxedFunction, DmnBuiltInDataType, generateUuid, getNextAvailablePrefixedName } from "../../api"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useBoxedExpressionEditorDispatch } from "../../BoxedExpressionEditorContext"; import { DMN15__tInformationItem } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; @@ -51,7 +51,7 @@ export const ParametersPopover: React.FunctionComponent (prev.formalParameter ?? []).map((p) => p["@_name"]), "p" ), - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, }, ]; @@ -120,7 +120,7 @@ function ParameterEntry({ parameter, index }: { parameter: DMN15__tInformationIt ); const onDataTypeChange = useCallback( - (typeRef: string) => { + (typeRef: string | undefined) => { setExpression((prev: BoxedFunction) => { const newParameters = [...(prev.formalParameter ?? [])]; newParameters[index] = { @@ -166,11 +166,7 @@ function ParameterEntry({ parameter, index }: { parameter: DMN15__tInformationIt placeholder={"Parameter Name"} defaultValue={parameter["@_name"]} /> - + diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/java/org/dashbuilder/renderer/client/selector/SelectorLabelSetDisplayerView.html b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/java/org/dashbuilder/renderer/client/selector/SelectorLabelSetDisplayerView.html index a6811665f4a..e926c597d0a 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/java/org/dashbuilder/renderer/client/selector/SelectorLabelSetDisplayerView.html +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/java/org/dashbuilder/renderer/client/selector/SelectorLabelSetDisplayerView.html @@ -1,3 +1,21 @@ +
diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/ErraiApp.properties index 7765a670d6e..c9de3c1625f 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/beans.xml b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/beans.xml index 37897ed19e5..dff3dc02769 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/beans.xml +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/META-INF/beans.xml @@ -1,4 +1,22 @@ + diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/DefaultRenderer.gwt.xml b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/DefaultRenderer.gwt.xml index 9ad89f2ec2d..9a257a12115 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/DefaultRenderer.gwt.xml +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/DefaultRenderer.gwt.xml @@ -1,4 +1,22 @@ + diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_de.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_de.properties index 0b312052e18..bb33add62f9 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_de.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_de.properties @@ -1,3 +1,22 @@ +# +# 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. +# + error=FEHLER\: error_settings_unset=DisplayerSettings-Eigenschaft nicht eingestellt. error_handler_unset=DataSetHandler-Eigenschaft nicht eingestellt. diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_pt_BR.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_pt_BR.properties index 42b8c406be5..1000821099e 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_pt_BR.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_pt_BR.properties @@ -1,3 +1,22 @@ +# +# 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. +# + error=ERRO\: error_settings_unset=Propriedade do DisplayerSettings não foi definida. error_handler_unset=Propriedade do DataSetHandler não foi definida. diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_zh_CN.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_zh_CN.properties index db3f29f95e3..53b3e02afd8 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_zh_CN.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/CommonConstants_zh_CN.properties @@ -1,3 +1,22 @@ +# +# 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. +# + error=错误: error_settings_unset=没有设置 DisplayerSettings 属性。 error_handler_unset=没有设置 DataSetHandler 属性。 diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_de.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_de.properties index 5cb99deb1f7..eadea671922 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_de.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_de.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + metricDisplayer_columnsTitle=Metrik diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_pt_BR.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_pt_BR.properties index 0b51da21326..de6d6ea62b0 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_pt_BR.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_pt_BR.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + metricDisplayer_columnsTitle=Métrica diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_zh_CN.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_zh_CN.properties index d4cb49c62fb..758e80e88e3 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_zh_CN.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/MetricConstants_zh_CN.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + metricDisplayer_columnsTitle=度量 diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_de.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_de.properties index d7175467d2f..37c1c3055bd 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_de.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_de.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + selectorDisplayer_select=Auswählen diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_pt_BR.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_pt_BR.properties index b5101794bfa..72e410ef0e5 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_pt_BR.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_pt_BR.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + selectorDisplayer_select=Selecionar diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_zh_CN.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_zh_CN.properties index 4dd267d77e3..0d22e132ade 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_zh_CN.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/SelectorConstants_zh_CN.properties @@ -1 +1,20 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + selectorDisplayer_select=选择 diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_de.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_de.properties index 5a920c03cca..dd369e93823 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_de.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_de.properties @@ -1,2 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + tableDisplayer_columnsTitle=Spalten tableDisplayer_reset=zurücksetzen diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_pt_BR.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_pt_BR.properties index 48b807ae8e8..7ab57e3cdbe 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_pt_BR.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_pt_BR.properties @@ -1,2 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + tableDisplayer_columnsTitle=Colunas tableDisplayer_reset=reiniciar diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_zh_CN.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_zh_CN.properties index 6caf60c541c..cd026a8022f 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_zh_CN.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-default/src/main/resources/org/dashbuilder/renderer/client/resources/i18n/TableConstants_zh_CN.properties @@ -1,2 +1,21 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + tableDisplayer_columnsTitle=列 tableDisplayer_reset=重置 diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/ErraiApp.properties index 7765a670d6e..c9de3c1625f 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/beans.xml b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/beans.xml index 4e126443374..2e493afa6d5 100644 --- a/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/beans.xml +++ b/packages/dashbuilder/dashbuilder-client/dashbuilder-renderers/dashbuilder-renderer-echarts/src/main/resources/META-INF/beans.xml @@ -1,4 +1,22 @@ + diff --git a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/META-INF/ErraiApp.properties index f301e0195da..76a247b5242 100644 --- a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/org/dashbuilder/client/resources/css/mode-dark.css b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/org/dashbuilder/client/resources/css/mode-dark.css index 177f8febbcf..c20c4d9f7d7 100644 --- a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/org/dashbuilder/client/resources/css/mode-dark.css +++ b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/resources/org/dashbuilder/client/resources/css/mode-dark.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + [dashbuilder-mode="dark"] { background-color: #100c2a !important; color: #eef1fa; diff --git a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/WEB-INF/beans.xml b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/WEB-INF/beans.xml index 2e5219c3f5f..cb61c852076 100644 --- a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/WEB-INF/beans.xml +++ b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/WEB-INF/beans.xml @@ -1,4 +1,22 @@ + diff --git a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/index.css b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/index.css index b47b8609b28..195f5404abe 100644 --- a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/index.css +++ b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/index.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .loader_container { position: fixed; top: 0; diff --git a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/setup.js b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/setup.js index 46b2de05bfd..db9194f0b87 100644 --- a/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/setup.js +++ b/packages/dashbuilder/dashbuilder-runtime-parent/dashbuilder-runtime-client/src/main/webapp/setup.js @@ -1,3 +1,22 @@ +/* + * 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. + */ + dashbuilder = { /* // possible modes are EDITOR and CLIENT - if dashboards is set then CLIENT mode is assumed diff --git a/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/java/org/dashbuilder/displayer/MapColorScheme.java b/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/java/org/dashbuilder/displayer/MapColorScheme.java index a281d3c0490..efff613e667 100644 --- a/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/java/org/dashbuilder/displayer/MapColorScheme.java +++ b/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/java/org/dashbuilder/displayer/MapColorScheme.java @@ -1,3 +1,22 @@ +/* + * 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.dashbuilder.displayer; import java.util.stream.Stream; diff --git a/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/resources/META-INF/ErraiApp.properties index 7765a670d6e..c9de3c1625f 100644 --- a/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-shared/dashbuilder-displayer-api/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-shared/dashbuilder-navigation-api/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-shared/dashbuilder-navigation-api/src/main/resources/META-INF/ErraiApp.properties index 140bd5616d1..92b3b405bbf 100644 --- a/packages/dashbuilder/dashbuilder-shared/dashbuilder-navigation-api/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-shared/dashbuilder-navigation-api/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-shared/dashbuilder-services-api/src/main/resources/META-INF/ErraiApp.properties b/packages/dashbuilder/dashbuilder-shared/dashbuilder-services-api/src/main/resources/META-INF/ErraiApp.properties index 7765a670d6e..c9de3c1625f 100644 --- a/packages/dashbuilder/dashbuilder-shared/dashbuilder-services-api/src/main/resources/META-INF/ErraiApp.properties +++ b/packages/dashbuilder/dashbuilder-shared/dashbuilder-services-api/src/main/resources/META-INF/ErraiApp.properties @@ -1,3 +1,22 @@ +# +# 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. +# + # # ErraiApp.properties # diff --git a/packages/dashbuilder/dashbuilder-shared/pom.xml b/packages/dashbuilder/dashbuilder-shared/pom.xml index 7f01fbbdd57..3d4bb982fbd 100644 --- a/packages/dashbuilder/dashbuilder-shared/pom.xml +++ b/packages/dashbuilder/dashbuilder-shared/pom.xml @@ -1,4 +1,22 @@ + + + org.kie + kie-tools-maven-base + ${revision} + ./node_modules/@kie-tools/maven-base/pom.xml + + 4.0.0 org.dashbuilder dashbuilder-parent - ${revision} pom Dashbuilder parent @@ -73,7 +79,7 @@ 2.0.2 4.1.2 1.2 - 3.8.4.Final + 3.8.14.Final 4.1.59.Final 1.1.0 3.3.3 @@ -93,7 +99,6 @@ 3.26.0-GA - 3.1.0 3.1.2 3.3.0 2.22.2 @@ -563,7 +568,6 @@ org.apache.maven.plugins maven-source-plugin - ${version.source.plugin} attach-sources @@ -719,17 +723,17 @@ > ${illegaltransitivereportonly} - org\.jboss\.errai\.marshalling\.server\.impl\.ServerMarshallingFactory.* - io\.fabric8\.kubernetes.* io\.fabric8\.openshift.* @@ -752,17 +756,17 @@ - - commons-logging:commons-log* log4j:log4j javassist:javassist org.apache.cxf:cxf-bundle-jaxrs org.jboss.weld.se:weld-se @@ -804,8 +808,8 @@ ban-duplicated-classes - none @@ -815,14 +819,14 @@ - org.xmlpull.v1.XmlPullParserException org.xmlpull.v1.XmlPullParser - org.relaxng.datatype.* - org.apache.xmlbeans.xml.stream.XMLName org.apache.xmlbeans.xml.stream.XMLInputStream @@ -834,17 +838,17 @@ org.apache.xmlbeans.xml.stream.ReferenceResolver org.apache.xmlbeans.xml.stream.XMLEvent org.apache.xmlbeans.xml.stream.Location - org.w3c.dom.ElementTraversal org.w3c.dom.UserDataHandler - org.hornetq.utils.HornetQUtilBundle_$bundle org.hornetq.utils.HornetQUtilLogger_$logger - net.jcip.annotations.* @@ -858,22 +862,22 @@ org.eclipse.aether.* org.apache.maven.* org.codehaus.plexus.* - org.infinispan.util.AbstractDelegatingConcurrentMap org.infinispan.util.AbstractDelegatingMap org.infinispan.util.AbstractDelegatingSet org.infinispan.util.AbstractDelegatingCollection - org.apache.kafka.common.message.* org.apache.sshd.common.io.* org.apache.sshd.common.auth.* - org.glassfish @@ -882,7 +886,7 @@ javax.json.* - org.glassfish @@ -891,7 +895,7 @@ javax.el.* - com.sun.activation @@ -900,7 +904,7 @@ javax.activation.* - com.sun.mail @@ -910,7 +914,7 @@ - org.jboss.errai errai-ioc @@ -919,7 +923,7 @@ - org.jboss.errai errai-javax-enterprise @@ -928,7 +932,7 @@ - io.quarkus quarkus-ide-launcher @@ -936,9 +940,9 @@ * - com.google.gwt @@ -952,14 +956,14 @@ org.elasticsearch elasticsearch - org.joda.time.base.BaseDateTime - org.kie @@ -969,7 +973,7 @@ - org.dominokit @@ -979,7 +983,7 @@ - org.antlr @@ -996,7 +1000,7 @@ - org.kie.server @@ -1021,8 +1025,8 @@ - io.fabric8 openshift-client @@ -1300,7 +1304,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.3.0 + 1.6.0 true resolveCiFriendliesOnly @@ -1341,8 +1345,8 @@ ban-duplicated-classes - validate diff --git a/packages/dev-deployment-base-image/Containerfile b/packages/dev-deployment-base-image/Containerfile index bbdeed39045..20b8dc1eb97 100644 --- a/packages/dev-deployment-base-image/Containerfile +++ b/packages/dev-deployment-base-image/Containerfile @@ -31,13 +31,13 @@ USER root ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_EXTRACT_TO_DIR=$HOME_PATH/app ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_PORT=8080 -RUN microdnf install -y tar-2:1.34-6.el9_1.x86_64 gzip-1.12-1.el9.x86_64 \ - && microdnf clean all \ +RUN microdnf --disableplugin=subscription-manager install -y tar gzip \ + && microdnf --disableplugin=subscription-manager clean all \ && mkdir -p -m 777 $HOME_PATH/app \ && mkdir -p -m 777 /tmp/app \ && mkdir -p -m 777 /.m2 -COPY dist-dev/dev-deployment-upload-service /usr/local/bin +COPY dist-dev/dev-deployment-upload-service /usr/local/bin/ COPY .mvn $HOME_PATH/app/.mvn COPY mvnw $HOME_PATH/app diff --git a/packages/dev-deployment-base-image/README.md b/packages/dev-deployment-base-image/README.md index 3d213d44454..ea0016830d2 100644 --- a/packages/dev-deployment-base-image/README.md +++ b/packages/dev-deployment-base-image/README.md @@ -1,3 +1,20 @@ + + # Dev Deployment Base Image Docker image with Java and Maven, as well as the dev-deployment-upload-service binary installed and ready to be used. @@ -28,8 +45,38 @@ Docker image with Java and Maven, as well as the dev-deployment-upload-service b Run the image with: -- `docker run -p 8080:8080 -e DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=123 quay.io/kie-tools/dev-deployment-base-image:daily-dev 'dev-deployment-upload-service && ./mvnw quarkus:dev'` +- `docker run -p 8080:8080 -e DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=123 docker.io/apache/incubator-kie-sandbox-dev-deployment-base:daily-dev 'dev-deployment-upload-service && ./mvnw quarkus:dev'` Then upload a zip file containing the resources (full Java project) - `curl -X POST -H "Content-Type: multipart/form-data" -F "myFile=@" 'http://localhost:8080/upload?apiKey=123'` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-base-image/env/index.js b/packages/dev-deployment-base-image/env/index.js index 22bb4561cff..41b68d50034 100644 --- a/packages/dev-deployment-base-image/env/index.js +++ b/packages/dev-deployment-base-image/env/index.js @@ -19,7 +19,9 @@ const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env"); -module.exports = composeEnv([require("@kie-tools/root-env/env")], { +const rootEnv = require("@kie-tools/root-env/env"); + +module.exports = composeEnv([rootEnv], { vars: varsWithName({ DEV_DEPLOYMENT_BASE_IMAGE__builderImage: { default: "registry.access.redhat.com/ubi9/openjdk-17:1.18", @@ -34,19 +36,19 @@ module.exports = composeEnv([require("@kie-tools/root-env/env")], { description: "The container Home Path.", }, DEV_DEPLOYMENT_BASE_IMAGE__registry: { - default: "quay.io", + default: "docker.io", description: "The image registry.", }, DEV_DEPLOYMENT_BASE_IMAGE__account: { - default: "kie-tools", + default: "apache", description: "The image registry account.", }, DEV_DEPLOYMENT_BASE_IMAGE__name: { - default: "dev-deployment-base-image", + default: "incubator-kie-sandbox-dev-deployment-base", description: "The image name.", }, - DEV_DEPLOYMENT_BASE_IMAGE__buildTags: { - default: "daily-dev", + DEV_DEPLOYMENT_BASE_IMAGE__buildTag: { + default: rootEnv.env.root.streamName, description: "The image tag.", }, }), @@ -59,7 +61,7 @@ module.exports = composeEnv([require("@kie-tools/root-env/env")], { registry: getOrDefault(this.vars.DEV_DEPLOYMENT_BASE_IMAGE__registry), account: getOrDefault(this.vars.DEV_DEPLOYMENT_BASE_IMAGE__account), name: getOrDefault(this.vars.DEV_DEPLOYMENT_BASE_IMAGE__name), - tags: getOrDefault(this.vars.DEV_DEPLOYMENT_BASE_IMAGE__buildTags), + buildTag: getOrDefault(this.vars.DEV_DEPLOYMENT_BASE_IMAGE__buildTag), version: require("../package.json").version, }, }; diff --git a/packages/dev-deployment-base-image/install.js b/packages/dev-deployment-base-image/install.js index 34e95e6fe4f..5c348fae2e2 100644 --- a/packages/dev-deployment-base-image/install.js +++ b/packages/dev-deployment-base-image/install.js @@ -19,10 +19,7 @@ const buildEnv = require("./env"); const { setup } = require("@kie-tools/maven-config-setup-helper"); -const version = require("./package.json").version; setup(` - -Drevision=${version} - -Dquarkus.platform.version=${buildEnv.env.quarkusPlatform.version} - -Dversion.org.kie.kogito=${buildEnv.env.kogitoRuntime.version} + -Drevision=${buildEnv.env.devDeploymentBaseImage.version} `); diff --git a/packages/dev-deployment-base-image/package.json b/packages/dev-deployment-base-image/package.json index ca9c29f86aa..f71582cdb89 100644 --- a/packages/dev-deployment-base-image/package.json +++ b/packages/dev-deployment-base-image/package.json @@ -19,18 +19,21 @@ "copy-upload-service-assets": "run-script-os", "copy-upload-service-assets:linux:darwin": "mkdir -p ./dist-dev && cp -R ./node_modules/@kie-tools/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64 ./dist-dev/dev-deployment-upload-service", "copy-upload-service-assets:win32": "pnpm powershell \"Copy-Item -R ./node_modules/@kie-tools/dev-deployment-upload-service/dist/dev-deployment-upload-service-linux-amd64 ./dist-dev/dev-deployment-upload-service\"", - "create-test-image:build-only": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", - "create-test-image:kind": "kie-tools--image-builder kind -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --kind-cluster-name kie-sandbox-dev-cluster", - "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", - "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", - "image:docker:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --build-arg USER_ID_ARG=\"$(build-env devDeploymentBaseImage.userId)\" --build-arg HOME_PATH_ARG=\"$(build-env devDeploymentBaseImage.homePath)\"", - "image:podman:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --build-arg USER_ID_ARG=\"$(build-env devDeploymentBaseImage.userId)\" --build-arg HOME_PATH_ARG=\"$(build-env devDeploymentBaseImage.homePath)\" -e podman", + "create-test-image:build-only": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", + "create-test-image:kind": "kie-tools--image-builder kind -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --kind-cluster-name kie-sandbox-dev-cluster", + "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", + "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\"", + "image:docker:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --build-arg USER_ID_ARG=\"$(build-env devDeploymentBaseImage.userId)\" --build-arg HOME_PATH_ARG=\"$(build-env devDeploymentBaseImage.homePath)\"", + "image:podman:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentBaseImage.registry)\" -a \"$(build-env devDeploymentBaseImage.account)\" -n \"$(build-env devDeploymentBaseImage.name)\" -t \"$(build-env devDeploymentBaseImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentBaseImage.builderImage)\" --build-arg USER_ID_ARG=\"$(build-env devDeploymentBaseImage.userId)\" --build-arg HOME_PATH_ARG=\"$(build-env devDeploymentBaseImage.homePath)\" -e podman", "install": "node install.js && pnpm install:mvnw", "install:mvnw": "run-script-os", "install:mvnw:darwin:linux": "mvn -e org.apache.maven.plugins:maven-wrapper-plugin:3.3.0:wrapper", "install:mvnw:win32": "pnpm powershell \"mvn -e org.apache.maven.plugins:maven-wrapper-plugin:3.3.0:wrapper\"", "powershell": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command" }, + "dependencies": { + "@kie-tools/maven-base": "workspace:*" + }, "devDependencies": { "@kie-tools/dev-deployment-upload-service": "workspace:*", "@kie-tools/image-builder": "workspace:*", diff --git a/packages/dev-deployment-base-image/pom.xml b/packages/dev-deployment-base-image/pom.xml index 4b713116836..876881a7cdf 100644 --- a/packages/dev-deployment-base-image/pom.xml +++ b/packages/dev-deployment-base-image/pom.xml @@ -22,49 +22,15 @@ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > + + org.kie + kie-tools-maven-base + ${revision} + ./node_modules/@kie-tools/maven-base/pom.xml + + 4.0.0 org.kie.kogito dev-deployment-base-image - ${revision} - - - - - 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 - true - 17 - 17 - UTF-8 - UTF-8 - 3.2.5 - 4.13.2 - diff --git a/packages/dev-deployment-dmn-form-webapp-image/Containerfile b/packages/dev-deployment-dmn-form-webapp-image/Containerfile index cd04aea2986..37771fde116 100644 --- a/packages/dev-deployment-dmn-form-webapp-image/Containerfile +++ b/packages/dev-deployment-dmn-form-webapp-image/Containerfile @@ -5,19 +5,19 @@ # 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. +# under the License. -FROM --platform=linux/amd64 registry.access.redhat.com/ubi9/ubi-minimal:9.3 +FROM --platform=linux/amd64 registry.access.redhat.com/ubi9/ubi-minimal:9.4 -RUN microdnf --disableplugin=subscription-manager -y install httpd-2.4.57-5.el9.x86_64 \ +RUN microdnf --disableplugin=subscription-manager -y install httpd \ && microdnf --disableplugin=subscription-manager clean all \ && echo "Mutex posixsem" >> /etc/httpd/conf/httpd.conf \ && sed -i -e 's/Listen 80/Listen 8081/' /etc/httpd/conf/httpd.conf \ diff --git a/packages/dev-deployment-dmn-form-webapp-image/README.md b/packages/dev-deployment-dmn-form-webapp-image/README.md new file mode 100644 index 00000000000..aef5577ed7a --- /dev/null +++ b/packages/dev-deployment-dmn-form-webapp-image/README.md @@ -0,0 +1,48 @@ + + +## @kie-tools/dev-deployment-dmn-form-webapp-image + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-dmn-form-webapp-image/env/index.js b/packages/dev-deployment-dmn-form-webapp-image/env/index.js index 137f7b4cf2f..a10be8e74ae 100644 --- a/packages/dev-deployment-dmn-form-webapp-image/env/index.js +++ b/packages/dev-deployment-dmn-form-webapp-image/env/index.js @@ -19,22 +19,24 @@ const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env"); -module.exports = composeEnv([require("@kie-tools/root-env/env")], { +const rootEnv = require("@kie-tools/root-env/env"); + +module.exports = composeEnv([rootEnv], { vars: varsWithName({ DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry: { - default: "quay.io", + default: "docker.io", description: "The image registry.", }, DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account: { - default: "kie-tools", + default: "apache", description: "The image registry account.", }, DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name: { - default: "dev-deployment-dmn-form-webapp-image", + default: "incubator-kie-sandbox-dev-deployment-dmn-form-webapp", description: "The image name.", }, - DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags: { - default: "daily-dev", + DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag: { + default: rootEnv.env.root.streamName, description: "The image tag.", }, }), @@ -44,7 +46,7 @@ module.exports = composeEnv([require("@kie-tools/root-env/env")], { registry: getOrDefault(this.vars.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__registry), account: getOrDefault(this.vars.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__account), name: getOrDefault(this.vars.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__name), - tags: getOrDefault(this.vars.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTags), + buildTag: getOrDefault(this.vars.DEV_DEPLOYMENT_DMN_FORM_WEBAPP_IMAGE__buildTag), version: require("../package.json").version, }, }; diff --git a/packages/dev-deployment-dmn-form-webapp-image/package.json b/packages/dev-deployment-dmn-form-webapp-image/package.json index 5bc50f56546..a065e628376 100644 --- a/packages/dev-deployment-dmn-form-webapp-image/package.json +++ b/packages/dev-deployment-dmn-form-webapp-image/package.json @@ -18,12 +18,12 @@ "copy-assets": "run-script-os", "copy-assets:linux:darwin": "rimraf dist-dev && cp -R ./node_modules/@kie-tools/dev-deployment-dmn-form-webapp/dist/ dist-dev", "copy-assets:win32": "rimraf dist-dev && pnpm powershell \"Copy-Item -R ./node_modules/@kie-tools/dev-deployment-dmn-form-webapp/dist/ dist-dev\"", - "create-test-image:build-only": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\"", - "create-test-image:kind": "kie-tools--image-builder kind -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\" --kind-cluster-name kie-sandbox-dev-cluster", - "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\"", - "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\"", - "image:docker:build": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\"", - "image:podman:build": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.tags)\" -e podman", + "create-test-image:build-only": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\"", + "create-test-image:kind": "kie-tools--image-builder kind -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\" --kind-cluster-name kie-sandbox-dev-cluster", + "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\"", + "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\"", + "image:docker:build": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\"", + "image:podman:build": "kie-tools--image-builder build -r \"$(pnpm build-env devDeploymentDmnFormWebappImage.registry)\" -a \"$(pnpm build-env devDeploymentDmnFormWebappImage.account)\" -n \"$(pnpm build-env devDeploymentDmnFormWebappImage.name)\" -t \"$(pnpm build-env devDeploymentDmnFormWebappImage.buildTag)\" -e podman", "powershell": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command" }, "dependencies": { diff --git a/packages/dev-deployment-dmn-form-webapp/README.md b/packages/dev-deployment-dmn-form-webapp/README.md new file mode 100644 index 00000000000..34d9cbaaa95 --- /dev/null +++ b/packages/dev-deployment-dmn-form-webapp/README.md @@ -0,0 +1,48 @@ + + +## @kie-tools/dev-deployment-dmn-form-webapp + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-dmn-form-webapp/__mocks__/styleMock.js b/packages/dev-deployment-dmn-form-webapp/__mocks__/styleMock.js index f053ebf7976..4bd939113b0 100644 --- a/packages/dev-deployment-dmn-form-webapp/__mocks__/styleMock.js +++ b/packages/dev-deployment-dmn-form-webapp/__mocks__/styleMock.js @@ -1 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + module.exports = {}; diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/pom.xml b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/pom.xml index 20d0ea63639..e6bce3a0e1c 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/pom.xml +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/pom.xml @@ -7,15 +7,15 @@ ~ 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. + ~ under the License. --> 0.0.0 - 3.8.1 + 3.13.0 true - 11 - 11 + 17 + 17 UTF-8 UTF-8 - 3.0.0-M5 - 4.13.1 + 3.2.5 + 4.13.2 + true @@ -157,7 +158,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.3.0 + 1.6.0 true resolveCiFriendliesOnly diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Adjudication.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Adjudication.dmn index db93e5cacc3..fcef7059eb1 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Adjudication.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Adjudication.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/FlightRebooking.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/FlightRebooking.dmn index b661313f894..c9cae380621 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/FlightRebooking.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/FlightRebooking.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Functions.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Functions.dmn index 89ae0524143..096119d6f9d 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Functions.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Functions.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/InsurancePricing.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/InsurancePricing.dmn index c05115c5e26..dc75befff30 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/InsurancePricing.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/InsurancePricing.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/LoanPreQualification.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/LoanPreQualification.dmn index c1622f5dce3..5425f347d19 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/LoanPreQualification.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/LoanPreQualification.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/BusinessModeler_Logo_38x38.svg b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/BusinessModeler_Logo_38x38.svg index a6368c7f698..0514647292f 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/BusinessModeler_Logo_38x38.svg +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/BusinessModeler_Logo_38x38.svg @@ -1,25 +1,50 @@ - - - -Automation -Created with Sketch. -Automation -Created with Sketch. - + + + + + + Automation + + + Created with Sketch. + + + Automation + + + Created with Sketch. + - - - - + + + + + + - diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/app_logo_rgb_fullcolor_reverse.svg b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/app_logo_rgb_fullcolor_reverse.svg index 84ad27ea0d7..2cd885c3761 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/app_logo_rgb_fullcolor_reverse.svg +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/META-INF/resources/images/app_logo_rgb_fullcolor_reverse.svg @@ -1,23 +1,43 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/ManyInputs.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/ManyInputs.dmn index d7b10f66edc..9f26c10faaa 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/ManyInputs.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/ManyInputs.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Recursive.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Recursive.dmn index e4a4ee6043a..5aad4dac68c 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Recursive.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Recursive.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Routing.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Routing.dmn index 7832fc30e85..9ca15a9c4f1 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Routing.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Routing.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Strategy.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Strategy.dmn index 1f668799bc6..649efbed3e5 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Strategy.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Strategy.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Types.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Types.dmn index e602835f45d..141ab5d8763 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Types.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/Types.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/application.properties b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/application.properties index c427e9dcef7..54d6f6eedd9 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/application.properties +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/application.properties @@ -1,3 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + quarkus.swagger-ui.always-include=true quarkus.http.cors=true %dev.quarkus.http.cors.origins=/.*/ diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/CanDrive.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/CanDrive.dmn index ad3184d5bf1..f148133e5c1 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/CanDrive.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/CanDrive.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/Types3.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/Types3.dmn index 2657350d201..ed7bd9ffa44 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/Types3.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/canDrive/Types3.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive.dmn index 46345a9ff4e..0589da2c869 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive_2.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive_2.dmn index 8196d4b47e7..3cdbb7f4666 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive_2.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/complex/can_drive_2.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/findEmployees/FindEmployees.dmn b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/findEmployees/FindEmployees.dmn index ee0a5bd8520..2cc5895fac6 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/findEmployees/FindEmployees.dmn +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/quarkus-app/src/main/resources/findEmployees/FindEmployees.dmn @@ -1,4 +1,22 @@ + diff --git a/packages/dev-deployment-dmn-form-webapp/dev-webapp/webapp/static/images/app_logo_rgb_fullcolor_reverse.svg b/packages/dev-deployment-dmn-form-webapp/dev-webapp/webapp/static/images/app_logo_rgb_fullcolor_reverse.svg index 84ad27ea0d7..2cd885c3761 100644 --- a/packages/dev-deployment-dmn-form-webapp/dev-webapp/webapp/static/images/app_logo_rgb_fullcolor_reverse.svg +++ b/packages/dev-deployment-dmn-form-webapp/dev-webapp/webapp/static/images/app_logo_rgb_fullcolor_reverse.svg @@ -1,23 +1,43 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dev-deployment-dmn-form-webapp/package.json b/packages/dev-deployment-dmn-form-webapp/package.json index fb621bc7a15..2a89730608a 100644 --- a/packages/dev-deployment-dmn-form-webapp/package.json +++ b/packages/dev-deployment-dmn-form-webapp/package.json @@ -29,6 +29,7 @@ "@kie-tools/extended-services-api": "workspace:*", "@kie-tools/form-dmn": "workspace:*", "@kie-tools/i18n-common-dictionary": "workspace:*", + "@openapi-contrib/openapi-schema-to-json-schema": "^5.1.0", "@patternfly/react-core": "^4.276.6", "@patternfly/react-icons": "^4.93.6", "@readme/openapi-parser": "^2.5.0", @@ -51,6 +52,7 @@ "@testing-library/react": "^11.2.6", "@types/jest": "^26.0.23", "@types/jest-when": "^2.7.4", + "@types/json-schema": "^7.0.11", "@types/lodash": "^4.14.168", "@types/react": "^17.0.6", "@types/react-dom": "^17.0.5", diff --git a/packages/dev-deployment-dmn-form-webapp/src/DmnDevDeploymentFormWebAppDataApi.tsx b/packages/dev-deployment-dmn-form-webapp/src/DmnDevDeploymentFormWebAppDataApi.tsx index 9101a19a1cb..db8a4fa18b4 100644 --- a/packages/dev-deployment-dmn-form-webapp/src/DmnDevDeploymentFormWebAppDataApi.tsx +++ b/packages/dev-deployment-dmn-form-webapp/src/DmnDevDeploymentFormWebAppDataApi.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { ExtendedServicesDmnJsonSchema } from "@kie-tools/extended-services-api"; +import { ExtendedServicesFormSchema } from "@kie-tools/extended-services-api"; import OpenAPIParser from "@readme/openapi-parser"; import { routes } from "./Routes"; import { DmnFormAppProps } from "./DmnFormApp"; @@ -25,7 +25,7 @@ import * as path from "path"; export interface FormData { modelName: string; - schema: ExtendedServicesDmnJsonSchema; + schema: ExtendedServicesFormSchema; } export interface AppData { diff --git a/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx b/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx index 87b025a84db..93c86771dc6 100644 --- a/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx +++ b/packages/dev-deployment-dmn-form-webapp/src/DmnFormPage.tsx @@ -20,7 +20,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { I18nWrapped } from "@kie-tools-core/i18n/dist/react-components"; import { FormDmn, FormDmnOutputs } from "@kie-tools/form-dmn"; -import { DecisionResult, ExtendedServicesDmnJsonSchema } from "@kie-tools/extended-services-api"; +import { DecisionResult } from "@kie-tools/extended-services-api"; import { Alert, AlertActionCloseButton } from "@patternfly/react-core/dist/js/components/Alert"; import { EmptyState, EmptyStateBody, EmptyStateIcon } from "@patternfly/react-core/dist/js/components/EmptyState"; import { Page, PageSection } from "@patternfly/react-core/dist/js/components/Page"; @@ -32,9 +32,11 @@ import { DmnFormToolbar } from "./DmnFormToolbar"; import { ErrorBoundary } from "./ErrorBoundary"; import { useDmnFormI18n } from "./i18n"; import { useCancelableEffect } from "@kie-tools-core/react-hooks/dist/useCancelableEffect"; -import { resolveReferencesAndCheckForRecursion, getDefaultValues } from "@kie-tools/dmn-runner/dist/jsonSchema"; +import { dereferenceAndCheckForRecursion, getDefaultValues } from "@kie-tools/dmn-runner/dist/jsonSchema"; import { extractDifferences } from "@kie-tools/dmn-runner/dist/results"; import { DmnFormAppProps } from "./DmnFormApp"; +import { openapiSchemaToJsonSchema } from "@openapi-contrib/openapi-schema-to-json-schema"; +import type { JSONSchema4 } from "json-schema"; interface Props extends DmnFormAppProps { formData: FormData; @@ -54,7 +56,7 @@ export function DmnFormPage(props: Props) { const [formOutputs, setFormOutputs] = useState(); const [formOutputDiffs, setFormOutputDiffs] = useState(); const [formError, setFormError] = useState(false); - const [jsonSchema, setJsonSchema] = useState(undefined); + const [jsonSchema, setJsonSchema] = useState(undefined); const [openAlert, setOpenAlert] = useState(AlertTypes.NONE); const [pageError, setPageError] = useState(false); const errorBoundaryRef = useRef(null); @@ -62,18 +64,21 @@ export function DmnFormPage(props: Props) { useCancelableEffect( useCallback( ({ canceled }) => { - resolveReferencesAndCheckForRecursion(props.formData.schema, canceled).then((resolvedJsonSchema) => { - if (canceled.get()) { + dereferenceAndCheckForRecursion(props.formData.schema, canceled).then((dereferencedSchema) => { + if (canceled.get() || !dereferencedSchema) { return; } - setJsonSchema(resolvedJsonSchema); + const jsonSchema = openapiSchemaToJsonSchema(dereferencedSchema, { + definitionKeywords: ["definitions"], + }); + setJsonSchema(jsonSchema); setFormInputs((previousFormInputs) => { - if (!resolvedJsonSchema) { + if (!jsonSchema) { return {}; } return { - ...getDefaultValues(resolvedJsonSchema), + ...getDefaultValues(jsonSchema), ...previousFormInputs, }; }); diff --git a/packages/dev-deployment-dmn-form-webapp/static/favicon.svg b/packages/dev-deployment-dmn-form-webapp/static/favicon.svg index 4cef559984a..e0c8c45f283 100644 --- a/packages/dev-deployment-dmn-form-webapp/static/favicon.svg +++ b/packages/dev-deployment-dmn-form-webapp/static/favicon.svg @@ -1 +1,35 @@ -kie_icon_rgb_fullcolor_default \ No newline at end of file + + + + + + + kie_icon_rgb_fullcolor_default + + + + + + + + + diff --git a/packages/dev-deployment-dmn-form-webapp/static/images/app_logo_rgb_fullcolor_reverse.svg b/packages/dev-deployment-dmn-form-webapp/static/images/app_logo_rgb_fullcolor_reverse.svg index 84ad27ea0d7..2cd885c3761 100644 --- a/packages/dev-deployment-dmn-form-webapp/static/images/app_logo_rgb_fullcolor_reverse.svg +++ b/packages/dev-deployment-dmn-form-webapp/static/images/app_logo_rgb_fullcolor_reverse.svg @@ -1,23 +1,43 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/Containerfile b/packages/dev-deployment-kogito-quarkus-blank-app-image/Containerfile index 182f85b38cd..37cd0daa263 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app-image/Containerfile +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/Containerfile @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -# Defaults to quay.io/kie-tools/dev-deployment-base-image (check env/index.js) +# Defaults to docker.io/apache/incubator-kie-sandbox-dev-deployment-base (check env/index.js) ARG BUILDER_IMAGE_ARG FROM --platform=linux/amd64 ${BUILDER_IMAGE_ARG} @@ -27,8 +27,10 @@ ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_EXTRACT_TO_DIR=$HOME_PATH/app/src/main/resour ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_PORT=8080 COPY --chown=$USER_ID:$USER_ID dist-dev/quarkus-app $HOME_PATH/app/ +COPY --chown=$USER_ID:$USER_ID dist-dev/settings.xml /tmp/kogito/.m2/settings.xml -RUN ./mvnw clean package -B -ntp -Dmaven.test.skip -Dmaven.repo.local=/tmp/kogito/.m2/repository -Dquarkus.http.non-application-root-path=${ROOT_PATH}/q -Dquarkus.http.root-path=${ROOT_PATH} \ +# Pre-populate local Maven repository for faster startup +RUN ./mvnw clean package -B --settings /tmp/kogito/.m2/settings.xml -Dmaven.test.skip -Dmaven.repo.local=/tmp/kogito/.m2/repository -Dquarkus.http.non-application-root-path=${ROOT_PATH}/q -Dquarkus.http.root-path=${ROOT_PATH} \ && chgrp -R 0 $HOME_PATH/app && chmod -R g=u $HOME_PATH/app && chgrp -R 0 /tmp/kogito && chmod -R g=u /tmp/kogito && chgrp -R 0 /.m2 && chmod -R g=u /.m2 USER $USER_ID @@ -37,4 +39,4 @@ EXPOSE 8080 ENTRYPOINT ["/bin/bash", "-c"] -CMD ["dev-deployment-upload-service && cp -r $HOME_PATH/app/. /tmp/app && cd /tmp/app && ./mvnw quarkus:dev -Ddebug=false -Dmaven.repo.local=/tmp/kogito/.m2/repository -Dquarkus.http.non-application-root-path=${ROOT_PATH}/q -Dquarkus.http.root-path=${ROOT_PATH}"] +CMD ["dev-deployment-upload-service && cp -r $HOME_PATH/app/. /tmp/app && cd /tmp/app && ./mvnw quarkus:dev -o -s=/tmp/kogito/.m2/settings.xml -Dquarkus.analytics.disabled=true -Ddebug=false -Dmaven.repo.local=/tmp/kogito/.m2/repository -Dquarkus.http.non-application-root-path=${ROOT_PATH}/q -Dquarkus.http.root-path=${ROOT_PATH}"] diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/README.md b/packages/dev-deployment-kogito-quarkus-blank-app-image/README.md index ebd273e9141..b15806104ce 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app-image/README.md +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/README.md @@ -1,3 +1,20 @@ + + # Dev Deployment Kogito Quarkus Blank App Image This image is ready to be used for Dev deployments on KIE Sandbox. @@ -28,8 +45,38 @@ These files can decisions or processes, all of them will be used as resources fo Run the image with: -- `docker run -p 8080:8080 -e DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=123 quay.io/kie-tools/dev-deployment-kogito-quarkus-blank-app-image:daily-dev` +- `docker run -p 8080:8080 -e DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=123 docker.io/apache/incubator-kie-sandbox-dev-deployment-kogito-quarkus-blank-app:daily-dev` Then upload a zip file containing the resources (DMN, BPMN, etc) - `curl -X POST -H "Content-Type: multipart/form-data" -F "myFile=@" 'http://localhost:8080/upload?apiKey=123'` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/env/index.js b/packages/dev-deployment-kogito-quarkus-blank-app-image/env/index.js index 94da2b83f9f..5e3b3853f15 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app-image/env/index.js +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/env/index.js @@ -19,35 +19,42 @@ const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env"); -const buildEnv = require("@kie-tools/root-env/env"); -const devDeploymentBaseImageEnv = require("@kie-tools/dev-deployment-base-image/env"); +const rootEnv = require("@kie-tools/root-env/env"); -module.exports = composeEnv([buildEnv, devDeploymentBaseImageEnv], { +const { + env: { devDeploymentBaseImage: devDeploymentBaseImageEnv }, +} = require("@kie-tools/dev-deployment-base-image/env"); + +const { + env: { mavenM2RepoViaHttpImage: mavenM2RepoViaHttpImageEnv }, +} = require("@kie-tools/maven-m2-repo-via-http-image/env"); + +module.exports = composeEnv([rootEnv], { vars: varsWithName({ DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__builderImage: { - default: `${devDeploymentBaseImageEnv.env.devDeploymentBaseImage.registry}/${ - devDeploymentBaseImageEnv.env.devDeploymentBaseImage.account - }/${devDeploymentBaseImageEnv.env.devDeploymentBaseImage.name}:${ - devDeploymentBaseImageEnv.env.devDeploymentBaseImage.tags.split(" ")[0] - }`, + default: `${devDeploymentBaseImageEnv.registry}/${devDeploymentBaseImageEnv.account}/${devDeploymentBaseImageEnv.name}:${devDeploymentBaseImageEnv.buildTag}`, description: "The image used in the FROM import.", }, DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry: { - default: "quay.io", + default: "docker.io", description: "The image registry.", }, DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account: { - default: "kie-tools", + default: "apache", description: "The image registry account.", }, DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name: { - default: "dev-deployment-kogito-quarkus-blank-app-image", + default: "incubator-kie-sandbox-dev-deployment-kogito-quarkus-blank-app", description: "The image name.", }, - DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags: { - default: "daily-dev", + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag: { + default: rootEnv.env.root.streamName, description: "The image tag.", }, + DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__mavenM2RepoViaHttpImage: { + default: `${mavenM2RepoViaHttpImageEnv.registry}/${mavenM2RepoViaHttpImageEnv.account}/${mavenM2RepoViaHttpImageEnv.name}:${mavenM2RepoViaHttpImageEnv.tag}`, + description: "The image tag for the Maven M2 Repo via HTTP. Used during the build only.", + }, }), get env() { return { @@ -56,8 +63,13 @@ module.exports = composeEnv([buildEnv, devDeploymentBaseImageEnv], { registry: getOrDefault(this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__registry), account: getOrDefault(this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__account), name: getOrDefault(this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__name), - tags: getOrDefault(this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTags), + buildTag: getOrDefault(this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__buildTag), version: require("../package.json").version, + dev: { + mavenM2RepoViaHttpImage: getOrDefault( + this.vars.DEV_DEPLOYMENT_KOGITO_QUARKUS_BLANK_APP_IMAGE__mavenM2RepoViaHttpImage + ), + }, }, }; }, diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/install.js b/packages/dev-deployment-kogito-quarkus-blank-app-image/install.js new file mode 100644 index 00000000000..ef3dc831446 --- /dev/null +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/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.devDeploymentKogitoQuarkusBlankAppImage.version} +`); diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/package.json b/packages/dev-deployment-kogito-quarkus-blank-app-image/package.json index 0ca75292ca2..e86e04ea3e1 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app-image/package.json +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/package.json @@ -13,18 +13,25 @@ "url": "https://github.com/apache/incubator-kie-tools/issues" }, "scripts": { - "build:dev": "run-script-if --bool \"$(build-env containerImages.build)\" --then \"pnpm copy-assets\" \"pnpm image:docker:build\"", - "build:prod": "run-script-if --bool \"$(build-env containerImages.build)\" --then \"pnpm copy-assets\" \"pnpm image:docker:build\"", - "copy-assets": "rimraf dist-dev && pnpm copy-quarkus-app-assets", - "copy-quarkus-app-assets": "run-script-os", - "copy-quarkus-app-assets:linux:darwin": "mkdir -p ./dist-dev && cp -R ./node_modules/@kie-tools/dev-deployment-kogito-quarkus-blank-app/ ./dist-dev/quarkus-app", - "copy-quarkus-app-assets:win32": "pnpm powershell \"New-Item -ItemType Directory -Force -Path ./dist-dev/quarkus-app; Copy-Item -R ./node_modules/@kie-tools/dev-deployment-kogito-quarkus-blank-app/* ./dist-dev/quarkus-app -Exclude @('node_modules')\"", - "create-test-image:build-only": "kie-tools--image-builder build -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", - "create-test-image:kind": "kie-tools--image-builder kind -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/ --kind-cluster-name kie-sandbox-dev-cluster", - "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", - "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", - "image:docker:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", - "image:podman:build": "kie-tools--image-builder build -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.tags)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/ -e podman", + "build": "run-script-if --bool \"$(build-env containerImages.build)\" --then \"pnpm m2-repo-via-http:container:run\" \"pnpm copy:assets\" \"pnpm image:docker:build\" --finally \"pnpm m2-repo-via-http:container:kill\"", + "build:dev": "pnpm build", + "build:prod": "pnpm build", + "copy:assets": "rimraf dist-dev && mkdir -p ./dist-dev && pnpm copy:quarkus-app && pnpm copy:maven-m2-repo-via-http-image--settings-xml", + "copy:maven-m2-repo-via-http-image--settings-xml": "run-script-os", + "copy:maven-m2-repo-via-http-image--settings-xml:linux:darwin": "M2_REPO_VIA_HTTP_URL_WITHOUT_PROTOCOL=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' m2-repo-via-http) envsubst < ./node_modules/@kie-tools/maven-m2-repo-via-http-image/settings.xml.envsubst > dist-dev/settings.xml", + "copy:maven-m2-repo-via-http-image--settings-xml:win32": "pnpm powershell \"(Get-Content ./node_modules/@kie-tools/maven-m2-repo-via-http-image/settings.xml.envsubst) -replace '$M2_REPO_VIA_HTTP_URL_WITHOUT_PROTOCOL', $(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' m2-repo-via-http) | Set-Content ./dist-dev/settings.xml\"", + "copy:quarkus-app": "run-script-os", + "copy:quarkus-app:linux:darwin": " cp -R ./node_modules/@kie-tools/dev-deployment-kogito-quarkus-blank-app/ ./dist-dev/quarkus-app && rm -rf ./dist-dev/quarkus-app/node_modules ./dist-dev/quarkus-app/install.js ./dist-dev/quarkus-app/env ./dist-dev/quarkus-app/package.json", + "copy:quarkus-app:win32": "pnpm powershell \"New-Item -ItemType Directory -Force -Path ./dist-dev/quarkus-app; Copy-Item -R ./node_modules/@kie-tools/dev-deployment-kogito-quarkus-blank-app/* ./dist-dev/quarkus-app -Exclude @('node_modules')\"", + "create-test-image:build-only": "kie-tools--image-builder build --allowHostNetworkAccess -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", + "create-test-image:kind": "kie-tools--image-builder kind -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/ --kind-cluster-name kie-sandbox-dev-cluster", + "create-test-image:minikube": "kie-tools--image-builder minikube -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", + "create-test-image:openshift": "kie-tools--image-builder openshift -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", + "image:docker:build": "kie-tools--image-builder build --allowHostNetworkAccess -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/", + "image:podman:build": "kie-tools--image-builder build --allowHostNetworkAccess -r \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.registry)\" -a \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.account)\" -n \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.name)\" -t \"$(build-env devDeploymentKogitoQuarkusBlankAppImage.buildTag)\" --build-arg BUILDER_IMAGE_ARG=\"$(build-env devDeploymentKogitoQuarkusBlankAppImage.builderImage)\" --build-arg ROOT_PATH=/ -e podman", + "install": "node install.js", + "m2-repo-via-http:container:kill": "(docker container kill m2-repo-via-http || true) && (docker container rm m2-repo-via-http || true)", + "m2-repo-via-http:container:run": "(pnpm m2-repo-via-http:container:kill || true) && docker run --name m2-repo-via-http -v \"$(mvn help:evaluate -Dexpression=settings.localRepository -q -DforceStdout):/var/www/html\" -dit $(build-env devDeploymentKogitoQuarkusBlankAppImage.dev.mavenM2RepoViaHttpImage)", "powershell": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command" }, "dependencies": { @@ -34,6 +41,7 @@ "devDependencies": { "@kie-tools/image-builder": "workspace:*", "@kie-tools/maven-config-setup-helper": "workspace:*", + "@kie-tools/maven-m2-repo-via-http-image": "workspace:*", "@kie-tools/root-env": "workspace:*", "rimraf": "^3.0.2", "run-script-os": "^1.1.6" diff --git a/packages/dev-deployment-kogito-quarkus-blank-app-image/pom.xml b/packages/dev-deployment-kogito-quarkus-blank-app-image/pom.xml new file mode 100644 index 00000000000..7a114b351c5 --- /dev/null +++ b/packages/dev-deployment-kogito-quarkus-blank-app-image/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + org.kie.kogito + dev-deployment-kogito-quarkus-blank-app-image + ${revision} + + + + + + org.kie + kie-tools-maven-base + ${project.version} + pom + + + diff --git a/packages/dev-deployment-kogito-quarkus-blank-app/README.md b/packages/dev-deployment-kogito-quarkus-blank-app/README.md new file mode 100644 index 00000000000..1275ffe34b0 --- /dev/null +++ b/packages/dev-deployment-kogito-quarkus-blank-app/README.md @@ -0,0 +1,48 @@ + + +## @kie-tools/dev-deployment-kogito-quarkus-blank-app + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-kogito-quarkus-blank-app/package.json b/packages/dev-deployment-kogito-quarkus-blank-app/package.json index e87983b9d05..7e7e7c320f8 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app/package.json +++ b/packages/dev-deployment-kogito-quarkus-blank-app/package.json @@ -29,6 +29,7 @@ "quarkus:dev:darwin:linux": "mvn clean package quarkus:dev -DskipTests", "quarkus:dev:win32": "mvn clean package quarkus:dev `-DskipTests" }, + "dependencies": {}, "devDependencies": { "@kie-tools/maven-config-setup-helper": "workspace:*", "@kie-tools/root-env": "workspace:*", diff --git a/packages/dev-deployment-kogito-quarkus-blank-app/pom.xml b/packages/dev-deployment-kogito-quarkus-blank-app/pom.xml index 67a6258cd19..13be51c1f55 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app/pom.xml +++ b/packages/dev-deployment-kogito-quarkus-blank-app/pom.xml @@ -22,51 +22,35 @@ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > + + + + + org.apache + apache + 32 + + 4.0.0 org.kie.kogito dev-deployment-kogito-quarkus-blank-app ${revision} - - - - 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 + 3.13.0 true 17 17 UTF-8 UTF-8 3.2.5 + 3.12.1 + 3.2.0 + 3.4.1 4.13.2 true + 1.26.1 + 0.5 @@ -94,6 +78,11 @@ + + org.apache.commons + commons-compress + ${version.org.apache.commons.commons-compress} + io.quarkus quarkus-resteasy @@ -128,6 +117,13 @@ + + + io.quarkus quarkus-junit5 @@ -152,6 +148,73 @@ src/main/resources + + + + org.apache.maven.plugins + maven-site-plugin + ${maven.site.plugin.version} + + + org.apache.commons + commons-compress + ${version.org.apache.commons.commons-compress} + + + org.iq80.snappy + snappy + ${version.org.iq80.snappy} + + + + + org.apache.maven.plugins + maven-remote-resources-plugin + ${maven.remote.resources.plugin.version} + + + org.apache.commons + commons-compress + ${version.org.apache.commons.commons-compress} + + + org.iq80.snappy + snappy + ${version.org.iq80.snappy} + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + org.iq80.snappy + snappy + ${version.org.iq80.snappy} + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${surefire-plugin.version} + + + org.iq80.snappy + snappy + ${version.org.iq80.snappy} + + + org.apache.commons + commons-compress + ${version.org.apache.commons.commons-compress} + + + + + io.quarkus @@ -186,7 +249,7 @@ org.codehaus.mojo flatten-maven-plugin - 1.3.0 + 1.6.0 true resolveCiFriendliesOnly diff --git a/packages/dev-deployment-kogito-quarkus-blank-app/src/main/resources/application.properties b/packages/dev-deployment-kogito-quarkus-blank-app/src/main/resources/application.properties index 2b7a3191ed0..23cb22ac901 100644 --- a/packages/dev-deployment-kogito-quarkus-blank-app/src/main/resources/application.properties +++ b/packages/dev-deployment-kogito-quarkus-blank-app/src/main/resources/application.properties @@ -1,6 +1,26 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + quarkus.swagger-ui.always-include=true quarkus.http.cors=true -quarkus.http.cors.origins=/.*/ +quarkus.http.cors.origins=* +quarkus.dev-ui.cors.enabled=false kogito.service.url=http://localhost:8080 quarkus.http.host=0.0.0.0 diff --git a/packages/dev-deployment-upload-service/Makefile b/packages/dev-deployment-upload-service/Makefile index 1103cd9701c..0b57edda99d 100644 --- a/packages/dev-deployment-upload-service/Makefile +++ b/packages/dev-deployment-upload-service/Makefile @@ -4,19 +4,19 @@ DDUS_VERSION?=0.0.0 build-all: linux-amd64 darwin-amd64 darwin-arm64 win32-amd64 checksum linux-amd64: - cross-env GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-linux-amd64 main.go + cross-env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-linux-amd64 main.go tar -zcvf dist/dev-deployment-upload-service-linux-amd64-$(DDUS_VERSION).tar.gz -C dist dev-deployment-upload-service-linux-amd64 darwin-amd64: - cross-env GOOS=darwin GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-darwin-amd64 main.go + cross-env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-darwin-amd64 main.go tar -zcvf dist/dev-deployment-upload-service-darwin-amd64-$(DDUS_VERSION).tar.gz -C dist dev-deployment-upload-service-darwin-amd64 darwin-arm64: - cross-env GOOS=darwin GOARCH=arm64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-darwin-arm64 main.go + cross-env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-darwin-arm64 main.go tar -zcvf dist/dev-deployment-upload-service-darwin-arm64-$(DDUS_VERSION).tar.gz -C dist dev-deployment-upload-service-darwin-arm64 win32-amd64: - cross-env GOOS=windows GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-windows-amd64.exe main.go + cross-env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags $(LDFLAGS) -o dist/dev-deployment-upload-service-windows-amd64.exe main.go tar -zcvf dist/dev-deployment-upload-service-windows-amd64-$(DDUS_VERSION).tar.gz -C dist dev-deployment-upload-service-windows-amd64.exe checksum: diff --git a/packages/dev-deployment-upload-service/README.md b/packages/dev-deployment-upload-service/README.md index bcffe013488..a18df1de15f 100644 --- a/packages/dev-deployment-upload-service/README.md +++ b/packages/dev-deployment-upload-service/README.md @@ -1,3 +1,20 @@ + + # Dev Deployment Upload Service This package is used on the KIE Sandbox's Dev deployments feature and should be the first command to run when a deployed container spins up. @@ -90,3 +107,33 @@ DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY='dev' \ DEV_DEPLOYMENT__UPLOAD_SERVICE_ROOT_PATH='/' \ pnpm start ``` + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-buildtime-install b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-buildtime-install index 8597df5f4b8..edc61a9a30f 100644 --- a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-buildtime-install +++ b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-buildtime-install @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.3 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.4 ARG DDUS_FILESERVER_IP="" ARG DDUS_VERSION="0.0.0" @@ -28,7 +28,7 @@ ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_PORT=8091 ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=dev ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_ROOT_PATH="/" -RUN microdnf install -y tar gzip findutils +RUN microdnf --disableplugin=subscription-manager install -y tar gzip findutils RUN curl ${DDUS_FILESERVER_IP}:${DDUS_FILESERVER_PORT}/apache/incubator-kie-tools/releases/download/${DDUS_VERSION}/getDevDeploymentUploadService.sh | sed 's/localhost:${DDUS_FILESERVER_PORT}/${DDUS_FILESERVER_IP}:${DDUS_FILESERVER_PORT}/; s/https:\/\/github.com/http:\/\/${DDUS_FILESERVER_IP}:${DDUS_FILESERVER_PORT}/' | bash diff --git a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-fileserver b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-fileserver index 01a3a529581..1b7441db6d4 100644 --- a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-fileserver +++ b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-fileserver @@ -15,11 +15,11 @@ # specific language governing permissions and limitations # under the License. -FROM --platform=linux/amd64 registry.access.redhat.com/ubi9/ubi-minimal:9.3 +FROM --platform=linux/amd64 registry.access.redhat.com/ubi9/ubi-minimal:9.4 ARG DDUS_VERSION="0.0.0" -RUN microdnf -y install httpd \ +RUN microdnf --disableplugin=subscription-manager install -y httpd \ && echo "Mutex posixsem" >> /etc/httpd/conf/httpd.conf \ && sed -i -e 's/Listen 80/Listen 8090/' /etc/httpd/conf/httpd.conf \ && sed -i -e 's/#ServerName www.example.com:80/ServerName 127.0.0.1:8090/' /etc/httpd/conf/httpd.conf \ diff --git a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-runtime-install b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-runtime-install index 7731d1a359a..9a1257528da 100644 --- a/packages/dev-deployment-upload-service/dev/Containerfile.ddus-runtime-install +++ b/packages/dev-deployment-upload-service/dev/Containerfile.ddus-runtime-install @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. -FROM registry.access.redhat.com/ubi9/ubi-minimal:9.3 +FROM registry.access.redhat.com/ubi9/ubi-minimal:9.4 ENV DDUS_FILESERVER_IP="" ENV DDUS_VERSION="0.0.0" @@ -28,7 +28,7 @@ ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_PORT=8092 ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_API_KEY=dev ENV DEV_DEPLOYMENT__UPLOAD_SERVICE_ROOT_PATH="/" -RUN microdnf install -y tar gzip findutils +RUN microdnf --disableplugin=subscription-manager install -y tar gzip findutils CMD curl $DDUS_FILESERVER_IP:${DDUS_FILESERVER_PORT}/apache/incubator-kie-tools/releases/download/$DDUS_VERSION/getDevDeploymentUploadService.sh | sed 's/localhost:${DDUS_FILESERVER_PORT}/$DDUS_FILESERVER_IP:${DDUS_FILESERVER_PORT}/; s/https:\/\/github.com/http:\/\/$DDUS_FILESERVER_IP:${DDUS_FILESERVER_PORT}/' | bash \ && dev-deployment-upload-service \ diff --git a/packages/dev-deployment-upload-service/env/index.js b/packages/dev-deployment-upload-service/env/index.js index be73994502f..92d6a814441 100644 --- a/packages/dev-deployment-upload-service/env/index.js +++ b/packages/dev-deployment-upload-service/env/index.js @@ -19,11 +19,11 @@ const { varsWithName, composeEnv, getOrDefault } = require("@kie-tools-scripts/build-env"); -const buildEnv = require("@kie-tools/root-env/env"); +const rootEnv = require("@kie-tools/root-env/env"); const version = require("../package.json").version; -module.exports = composeEnv([buildEnv], { +module.exports = composeEnv([rootEnv], { vars: varsWithName({ DEV_DEPLOYMENT_UPLOAD_SERVICE__downloadPath: { default: `apache/incubator-kie-tools/releases/download/${version}`, diff --git a/packages/dev-deployment-upload-service/getDevDeploymentUploadService.sh b/packages/dev-deployment-upload-service/getDevDeploymentUploadService.sh index ee4bffc6c1a..2a06cfbbaed 100755 --- a/packages/dev-deployment-upload-service/getDevDeploymentUploadService.sh +++ b/packages/dev-deployment-upload-service/getDevDeploymentUploadService.sh @@ -1,5 +1,5 @@ #!/bin/bash - +# # 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 @@ -8,7 +8,7 @@ # "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 +# 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 @@ -16,6 +16,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# # The install script is based off of the Apache-licensed script from helm, # a tool for managing Charts: https://github.com/helm/helm/blob/main/scripts/get-helm-3 diff --git a/packages/dev-deployment-upload-service/main.go b/packages/dev-deployment-upload-service/main.go index dcef96ec51c..ef7bac20cc7 100644 --- a/packages/dev-deployment-upload-service/main.go +++ b/packages/dev-deployment-upload-service/main.go @@ -1,3 +1,22 @@ +/* + * 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 main import ( diff --git a/packages/dev-deployment-upload-service/package.json b/packages/dev-deployment-upload-service/package.json index 95afd3e580b..9c60e5b198f 100644 --- a/packages/dev-deployment-upload-service/package.json +++ b/packages/dev-deployment-upload-service/package.json @@ -30,7 +30,7 @@ "start-test-servers": "run-script-if --bool \"$(build-env containerImages.build)\" --then \"node scripts/runTestServers.js\"", "stop-test-servers": "run-script-if --bool \"$(build-env containerImages.build)\" --then \"node scripts/runTestServers.js --cleanup\"", "test": "run-script-os", - "test:linux": "run-script-if --bool \"$(build-env containerImages.build)\" --bool \"$(build-env tests.run)\" --ignore-errors \"$(build-env tests.ignoreFailures)\" --then \"jest --verbose --silent=false\"", + "test:linux": "run-script-if --bool \"$(build-env containerImages.build)\" --bool \"$(build-env endToEndTests.run)\" --ignore-errors \"$(build-env tests.ignoreFailures)\" --then \"jest --verbose --silent=false\"", "test:win32:darwin": "echo 'Not supported'" }, "devDependencies": { diff --git a/packages/dmn-editor-envelope/README.md b/packages/dmn-editor-envelope/README.md index c5e5dad0d9f..952659bf561 100644 --- a/packages/dmn-editor-envelope/README.md +++ b/packages/dmn-editor-envelope/README.md @@ -1,3 +1,50 @@ -## DMN Editor Envelopedmn + + +## DMN Editor Envelope Package responsible for creating the necessary pumbling for `dmn-editor` to be used inside an Envelope. + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dmn-editor-envelope/package.json b/packages/dmn-editor-envelope/package.json index 026f7e09739..bc897c8afb1 100644 --- a/packages/dmn-editor-envelope/package.json +++ b/packages/dmn-editor-envelope/package.json @@ -27,6 +27,7 @@ "@kie-tools-core/editor": "workspace:*", "@kie-tools-core/envelope": "workspace:*", "@kie-tools-core/envelope-bus": "workspace:*", + "@kie-tools-core/keyboard-shortcuts": "workspace:*", "@kie-tools-core/notifications": "workspace:*", "@kie-tools-core/react-hooks": "workspace:*", "@kie-tools-core/workspace": "workspace:*", @@ -35,6 +36,7 @@ "@kie-tools/dmn-marshaller": "workspace:*", "@kie-tools/pmml-editor-marshaller": "workspace:*", "@kie-tools/xml-parser-ts": "workspace:*", + "@patternfly/react-core": "^4.276.6", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx b/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx index b5012e4cb2f..411287583be 100644 --- a/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx +++ b/packages/dmn-editor-envelope/src/DmnEditorFactory.tsx @@ -150,6 +150,7 @@ function DmnEditorRootWrapper({ onOpenFileFromNormalizedPosixPathRelativeToTheWorkspaceRoot } workspaceRootAbsolutePosixPath={workspaceRootAbsolutePosixPath} + keyboardShortcutsService={envelopeContext?.services.keyboardShortcuts} /> ); } diff --git a/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx b/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx index 50907d890a0..2cb5e5dafd7 100644 --- a/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx +++ b/packages/dmn-editor-envelope/src/DmnEditorRoot.tsx @@ -19,8 +19,10 @@ import * as __path from "path"; import * as React from "react"; +import { useEffect, useMemo, useState } from "react"; import * as DmnEditor from "@kie-tools/dmn-editor/dist/DmnEditor"; -import { getMarshaller } from "@kie-tools/dmn-marshaller"; +import { normalize, Normalized } from "@kie-tools/dmn-editor/dist/normalization/normalize"; +import { DMN_LATEST_VERSION, DmnLatestModel, DmnMarshaller, getMarshaller } from "@kie-tools/dmn-marshaller"; import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { ContentType, @@ -30,10 +32,8 @@ import { WorkspaceEdit, } from "@kie-tools-core/workspace/dist/api"; import { DMN15_SPEC } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; -import { DMN_LATEST_VERSION, DmnLatestModel, DmnMarshaller } from "@kie-tools/dmn-marshaller"; import { domParser } from "@kie-tools/xml-parser-ts"; import { ns as dmn15ns } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/meta"; -import { useEffect, useMemo, useState } from "react"; import { XML2PMML } from "@kie-tools/pmml-editor-marshaller"; import { getPmmlNamespace } from "@kie-tools/dmn-editor/dist/pmml/pmml"; import { getNamespaceOfDmnImport } from "@kie-tools/dmn-editor/dist/includedModels/importNamespaces"; @@ -41,6 +41,10 @@ import { imperativePromiseHandle, PromiseImperativeHandle, } from "@kie-tools-core/react-hooks/dist/useImperativePromiseHandler"; +import { KeyboardShortcutsService } from "@kie-tools-core/keyboard-shortcuts/dist/envelope/KeyboardShortcutsService"; +import { Flex } from "@patternfly/react-core/dist/js/layouts/Flex"; +import { EmptyState, EmptyStateBody, EmptyStateIcon } from "@patternfly/react-core/dist/js/components/EmptyState"; +import { Title } from "@patternfly/react-core/dist/js/components/Title"; export const EXTERNAL_MODELS_SEARCH_GLOB_PATTERN = "**/*.{dmn,pmml}"; @@ -60,16 +64,20 @@ export type DmnEditorRootProps = { onRequestWorkspaceFileContent: WorkspaceChannelApi["kogitoWorkspace_resourceContentRequest"]; onOpenFileFromNormalizedPosixPathRelativeToTheWorkspaceRoot: WorkspaceChannelApi["kogitoWorkspace_openFile"]; workspaceRootAbsolutePosixPath: string; + keyboardShortcutsService: KeyboardShortcutsService | undefined; }; export type DmnEditorRootState = { marshaller: DmnMarshaller | undefined; - stack: DmnLatestModel[]; + stack: Normalized[]; pointer: number; openFilenormalizedPosixPathRelativeToTheWorkspaceRoot: string | undefined; externalModelsByNamespace: DmnEditor.ExternalModelsIndex; readonly: boolean; externalModelsManagerDoneBootstraping: boolean; + keyboardShortcutsRegisterIds: number[]; + keyboardShortcutsRegistred: boolean; + error: Error | undefined; }; export class DmnEditorRoot extends React.Component { @@ -89,6 +97,9 @@ export class DmnEditorRoot extends React.Component { - const marshaller = getMarshaller(content || EMPTY_DMN(), { upgradeTo: "latest" }); + const marshaller = this.getMarshaller(content); // Save stack - let savedStackPointer: DmnLatestModel[] = []; + let savedStackPointer: Normalized[] = []; // Set the model and path for external models manager. this.setState((prev) => { savedStackPointer = [...prev.stack]; - return { stack: [marshaller.parser.parse()], openFilenormalizedPosixPathRelativeToTheWorkspaceRoot, pointer: 0 }; + return { + stack: [normalize(marshaller.parser.parse())], + openFilenormalizedPosixPathRelativeToTheWorkspaceRoot, + pointer: 0, + }; }); // Wait the external manager models to load. @@ -145,7 +160,7 @@ export class DmnEditorRoot extends React.Component | undefined { + return this.state.stack[this.state.pointer]; + } + // Internal methods - public get model(): DmnLatestModel | undefined { - return this.state.stack[this.state.pointer]; + private getMarshaller(content: string) { + try { + return getMarshaller(content || EMPTY_DMN(), { upgradeTo: "latest" }); + } catch (e) { + this.setState((s) => ({ + ...s, + error: e, + })); + throw e; + } } private setExternalModelsByNamespace = (externalModelsByNamespace: DmnEditor.ExternalModelsIndex) => { @@ -247,7 +274,7 @@ export class DmnEditorRoot extends React.Component, + prevState: Readonly, + snapshot?: any + ): void { + if (this.props.keyboardShortcutsService === undefined || this.state.keyboardShortcutsRegistred === true) { + return; + } + + const commands = this.dmnEditorRef.current?.getCommands(); + if (commands === undefined) { + return; + } + const cancelAction = this.props.keyboardShortcutsService.registerKeyPress("Escape", "Edit | Unselect", async () => + commands.cancelAction() + ); + const deleteSelectionBackspace = this.props.keyboardShortcutsService.registerKeyPress( + "Backspace", + "Edit | Delete selection", + async () => {} + ); + const deleteSelectionDelete = this.props.keyboardShortcutsService.registerKeyPress( + "Delete", + "Edit | Delete selection", + async () => {} + ); + const selectAll = this.props.keyboardShortcutsService?.registerKeyPress( + "A", + "Edit | Select/Deselect all", + async () => commands.selectAll() + ); + const createGroup = this.props.keyboardShortcutsService?.registerKeyPress( + "G", + "Edit | Create group wrapping selection", + async () => { + console.log(" KEY GROUP PRESSED, ", commands); + return commands.createGroup(); + } + ); + const hideFromDrd = this.props.keyboardShortcutsService?.registerKeyPress("X", "Edit | Hide from DRD", async () => + commands.hideFromDrd() + ); + const copy = this.props.keyboardShortcutsService?.registerKeyPress("Ctrl+C", "Edit | Copy nodes", async () => + commands.copy() + ); + const cut = this.props.keyboardShortcutsService?.registerKeyPress("Ctrl+X", "Edit | Cut nodes", async () => + commands.cut() + ); + const paste = this.props.keyboardShortcutsService?.registerKeyPress("Ctrl+V", "Edit | Paste nodes", async () => + commands.paste() + ); + const togglePropertiesPanel = this.props.keyboardShortcutsService?.registerKeyPress( + "I", + "Misc | Open/Close properties panel", + async () => commands.togglePropertiesPanel() + ); + const toggleHierarchyHighlight = this.props.keyboardShortcutsService?.registerKeyPress( + "H", + "Misc | Toggle hierarchy highlights", + async () => commands.toggleHierarchyHighlight() + ); + const moveUp = this.props.keyboardShortcutsService.registerKeyPress( + "Up", + "Move | Move selection up", + async () => {} + ); + const moveDown = this.props.keyboardShortcutsService.registerKeyPress( + "Down", + "Move | Move selection down", + async () => {} + ); + const moveLeft = this.props.keyboardShortcutsService.registerKeyPress( + "Left", + "Move | Move selection left", + async () => {} + ); + const moveRight = this.props.keyboardShortcutsService.registerKeyPress( + "Right", + "Move | Move selection right", + async () => {} + ); + const bigMoveUp = this.props.keyboardShortcutsService.registerKeyPress( + "Shift + Up", + "Move | Move selection up a big distance", + async () => {} + ); + const bigMoveDown = this.props.keyboardShortcutsService.registerKeyPress( + "Shift + Down", + "Move | Move selection down a big distance", + async () => {} + ); + const bigMoveLeft = this.props.keyboardShortcutsService.registerKeyPress( + "Shift + Left", + "Move | Move selection left a big distance", + async () => {} + ); + const bigMoveRight = this.props.keyboardShortcutsService.registerKeyPress( + "Shift + Right", + "Move | Move selection right a big distance", + async () => {} + ); + const focusOnBounds = this.props.keyboardShortcutsService?.registerKeyPress( + "B", + "Navigate | Focus on selection", + async () => commands.focusOnSelection() + ); + const resetPosition = this.props.keyboardShortcutsService?.registerKeyPress( + "Space", + "Navigate | Reset position to origin", + async () => commands.resetPosition() + ); + const pan = this.props.keyboardShortcutsService?.registerKeyPress( + "Right Mouse Button", + "Navigate | Hold and drag to Pan", + async () => {} + ); + const zoom = this.props.keyboardShortcutsService?.registerKeyPress( + "Ctrl", + "Navigate | Hold and scroll to zoom in/out", + async () => {} + ); + const navigateHorizontally = this.props.keyboardShortcutsService?.registerKeyPress( + "Shift", + "Navigate | Hold and scroll to navigate horizontally", + async () => {} + ); + + this.setState((prev) => ({ + ...prev, + keyboardShortcutsRegistred: true, + keyboardShortcutsRegisterIds: [ + bigMoveDown, + bigMoveLeft, + bigMoveRight, + bigMoveUp, + cancelAction, + copy, + createGroup, + cut, + deleteSelectionBackspace, + deleteSelectionDelete, + focusOnBounds, + hideFromDrd, + moveDown, + moveLeft, + moveRight, + moveUp, + navigateHorizontally, + pan, + paste, + resetPosition, + selectAll, + toggleHierarchyHighlight, + togglePropertiesPanel, + zoom, + ], + })); + } + + public componentWillUnmount() { + const keyboardShortcuts = this.dmnEditorRef.current?.getCommands(); + if (keyboardShortcuts === undefined) { + return; + } + + this.state.keyboardShortcutsRegisterIds.forEach((id) => { + this.props.keyboardShortcutsService?.deregister(id); + }); + } + public render() { return ( <> + {this.state.error && } {this.model && ( <> ; onChange: (externalModelsByNamespace: DmnEditor.ExternalModelsIndex) => void; onRequestWorkspaceFileContent: WorkspaceChannelApi["kogitoWorkspace_resourceContentRequest"]; onRequestWorkspaceFilesList: WorkspaceChannelApi["kogitoWorkspace_resourceListRequest"]; @@ -425,7 +623,7 @@ function ExternalModelsManager({ externalModelsIndex[namespace] = { normalizedPosixPathRelativeToTheOpenFile, - model: getMarshaller(content, { upgradeTo: "latest" }).parser.parse(), + model: normalize(getMarshaller(content, { upgradeTo: "latest" }).parser.parse()), type: "dmn", svg: "", }; @@ -467,3 +665,18 @@ function ExternalModelsManager({ return <>; } + +function DmnMarshallerFallbackError({ error }: { error: Error }) { + return ( + + +
😕
} /> + + Unable to open file. + +
+ Error details: {error.message} +
+
+ ); +} diff --git a/packages/dmn-editor/README.md b/packages/dmn-editor/README.md new file mode 100644 index 00000000000..94d05a410f9 --- /dev/null +++ b/packages/dmn-editor/README.md @@ -0,0 +1,48 @@ + + +## @kie-tools/dmn-editor + +--- + +Apache KIE (incubating) is an effort undergoing incubation at The Apache Software +Foundation (ASF), sponsored by the name of Apache Incubator. Incubation is +required of all newly accepted projects until a further review indicates that +the infrastructure, communications, and decision making process have stabilized +in a manner consistent with other successful ASF projects. While incubation +status is not necessarily a reflection of the completeness or stability of the +code, it does indicate that the project has yet to be fully endorsed by the ASF. + +Some of the incubating project’s releases may not be fully compliant with ASF +policy. For example, releases may have incomplete or un-reviewed licensing +conditions. What follows is a list of known issues the project is currently +aware of (note that this list, by definition, is likely to be incomplete): + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may be missing the ASF Licensing Header +- + +- Hibernate, an LGPL project, is being used. Hibernate is in the process of + relicensing to ASL v2 +- Some files, particularly test files, and those not supporting comments, may + be missing the ASF Licensing Header + +If you are planning to incorporate this work into your product/project, please +be aware that you will need to conduct a thorough licensing review to determine +the overall implications of including this work. For the current status of this +project through the Apache Incubator visit: +https://incubator.apache.org/projects/kie.html diff --git a/packages/dmn-editor/package.json b/packages/dmn-editor/package.json index b3a425f7f5d..6f0f8782566 100644 --- a/packages/dmn-editor/package.json +++ b/packages/dmn-editor/package.json @@ -87,7 +87,7 @@ "@types/jest": "^26.0.23", "@types/jest-when": "^2.7.4", "@types/lodash": "^4.14.168", - "@types/node": "^18.13.0", + "@types/node": "^20.14.2", "@types/react": "^17.0.6", "@types/react-dom": "^17.0.5", "@types/testing-library__jest-dom": "^5.9.1", @@ -104,7 +104,7 @@ "lodash": "^4.17.21", "rimraf": "^3.0.2", "run-script-os": "^1.1.6", - "start-server-and-test": "^1.12.1", + "start-server-and-test": "^2.0.3", "storybook": "^7.3.2", "ts-jest": "^26.5.6", "typescript": "^4.6.2", diff --git a/packages/dmn-editor/src/DmnEditor.css b/packages/dmn-editor/src/DmnEditor.css index 1011c465840..f8973428d4c 100644 --- a/packages/dmn-editor/src/DmnEditor.css +++ b/packages/dmn-editor/src/DmnEditor.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + /* (begin) global */ .kie-dmn-editor--root { height: 100%; @@ -71,6 +90,7 @@ z-index: 1; } .kie-dmn-editor--input-data-node { + outline: none; width: 100%; height: 100%; } @@ -152,7 +172,7 @@ /* (end) decisionService and group nodes */ /* (begin) external nodes */ -.kie-dmn-editor--node.external { +.kie-dmn-editor--node-external { text-decoration: underline; } /* (end) external node */ diff --git a/packages/dmn-editor/src/DmnEditor.tsx b/packages/dmn-editor/src/DmnEditor.tsx index 58b6f73ce4d..3232ab0b562 100644 --- a/packages/dmn-editor/src/DmnEditor.tsx +++ b/packages/dmn-editor/src/DmnEditor.tsx @@ -38,7 +38,7 @@ import { BoxedExpressionScreen } from "./boxedExpressions/BoxedExpressionScreen" import { DataTypes } from "./dataTypes/DataTypes"; import { Diagram, DiagramRef } from "./diagram/Diagram"; import { DmnVersionLabel } from "./diagram/DmnVersionLabel"; -import { normalize } from "./normalization/normalize"; +import { Normalized, normalize } from "./normalization/normalize"; import { BoxedExpressionPropertiesPanel } from "./propertiesPanel/BoxedExpressionPropertiesPanel"; import { DmnEditorContextProvider, useDmnEditor } from "./DmnEditorContext"; import { DmnEditorErrorFallback } from "./DmnEditorErrorFallback"; @@ -57,6 +57,7 @@ import { INITIAL_COMPUTED_CACHE } from "./store/computed/initial"; import "@kie-tools/dmn-marshaller/dist/kie-extensions"; // This is here because of the KIE Extension for DMN. import "./DmnEditor.css"; // Leave it for last, as this overrides some of the PF and RF styles. +import { Commands, CommandsContextProvider, useCommands } from "./commands/CommandsContextProvider"; const ON_MODEL_CHANGE_DEBOUNCE_TIME_IN_MS = 500; @@ -65,11 +66,12 @@ const SVG_PADDING = 20; export type DmnEditorRef = { reset: (mode: DmnLatestModel) => void; getDiagramSvg: () => Promise; + getCommands: () => Commands; }; export type EvaluationResults = Record; export type ValidationMessages = Record; -export type OnDmnModelChange = (model: DmnLatestModel) => void; +export type OnDmnModelChange = (model: Normalized) => void; export type OnRequestToJumpToPath = (normalizedPosixPathRelativeToTheOpenFile: string) => void; export type OnRequestToResolvePath = (normalizedPosixPathRelativeToTheOpenFile: string) => string; @@ -87,7 +89,11 @@ export type ExternalModelsIndex = Record< export type ExternalModel = ({ type: "dmn" } & ExternalDmn) | ({ type: "pmml" } & ExternalPmml); export type ExternalDmnsIndex = Map; -export type ExternalDmn = { model: DmnLatestModel; normalizedPosixPathRelativeToTheOpenFile: string; svg: string }; +export type ExternalDmn = { + model: Normalized; + normalizedPosixPathRelativeToTheOpenFile: string; + svg: string; +}; export type ExternalPmmlsIndex = Map; export type ExternalPmml = { model: PMML; normalizedPosixPathRelativeToTheOpenFile: string }; @@ -171,14 +177,13 @@ export const DmnEditorInternal = ({ const navigationTab = useDmnEditorStore((s) => s.navigation.tab); const dmn = useDmnEditorStore((s) => s.dmn); const isDiagramEditingInProgress = useDmnEditorStore((s) => s.computed(s).isDiagramEditingInProgress()); - const dmnEditorStoreApi = useDmnEditorStoreApi(); + const { commandsRef } = useCommands(); const { dmnModelBeforeEditingRef, dmnEditorRootElementRef } = useDmnEditor(); const { externalModelsByNamespace } = useExternalModels(); // Refs - const diagramRef = useRef(null); const diagramContainerRef = useRef(null); const beeContainerRef = useRef(null); @@ -189,7 +194,7 @@ export const DmnEditorInternal = ({ () => ({ reset: (model) => { const state = dmnEditorStoreApi.getState(); - return state.dispatch(state).dmn.reset(model); + return state.dispatch(state).dmn.reset(normalize(model)); }, getDiagramSvg: async () => { const nodes = diagramRef.current?.getReactFlowInstance()?.getNodes(); @@ -233,8 +238,9 @@ export const DmnEditorInternal = ({ return new XMLSerializer().serializeToString(svg); }, + getCommands: () => commandsRef.current, }), - [dmnEditorStoreApi, externalModelsByNamespace] + [dmnEditorStoreApi, externalModelsByNamespace, commandsRef] ); // Make sure the DMN Editor reacts to props changing. @@ -244,7 +250,12 @@ export const DmnEditorInternal = ({ if (model === original(state.dmn.model)) { return; } + + state.diagram.autoLayout.canAutoGenerateDrd = + model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] === undefined && + model.definitions.drgElement !== undefined; state.dmn.model = normalize(model); + dmnModelBeforeEditingRef.current = state.dmn.model; }); }, [dmnEditorStoreApi, model]); @@ -407,7 +418,9 @@ export const DmnEditor = React.forwardRef((props: DmnEditorProps, ref: React.Ref - + + + diff --git a/packages/dmn-editor/src/DmnEditorContext.tsx b/packages/dmn-editor/src/DmnEditorContext.tsx index 21c8e4bf4bc..e90381f8bd9 100644 --- a/packages/dmn-editor/src/DmnEditorContext.tsx +++ b/packages/dmn-editor/src/DmnEditorContext.tsx @@ -21,6 +21,7 @@ import * as React from "react"; import { useContext, useMemo, useRef } from "react"; import { DmnEditorProps } from "./DmnEditor"; import { DmnLatestModel } from "@kie-tools/dmn-marshaller"; +import { Normalized } from "./normalization/normalize"; export type DmnEditorContextProviderProps = Pick< DmnEditorProps, diff --git a/packages/dmn-editor/src/DmnEditorErrorFallback.tsx b/packages/dmn-editor/src/DmnEditorErrorFallback.tsx index 3b18a217643..2a2cdc26ad6 100644 --- a/packages/dmn-editor/src/DmnEditorErrorFallback.tsx +++ b/packages/dmn-editor/src/DmnEditorErrorFallback.tsx @@ -19,7 +19,6 @@ import * as React from "react"; import { useDmnEditor } from "./DmnEditorContext"; -import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye"; import { Flex } from "@patternfly/react-core/dist/js/layouts/Flex"; import { EmptyState, diff --git a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx index 0bf61e814c2..c1a09f169eb 100644 --- a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx +++ b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx @@ -17,543 +17,54 @@ * under the License. */ -import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; -import { DC__Bounds } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; -import OptimizeIcon from "@patternfly/react-icons/dist/js/icons/optimize-icon"; -import ELK, * as Elk from "elkjs/lib/elk.bundled.js"; import * as React from "react"; -import { PositionalNodeHandleId } from "../diagram/connections/PositionalNodeHandles"; -import { EdgeType, NodeType } from "../diagram/connections/graphStructure"; -import { getAdjMatrix, traverse } from "../diagram/graph/graph"; -import { getContainmentRelationship } from "../diagram/maths/DmnMaths"; -import { DEFAULT_NODE_SIZES, MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; -import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import OptimizeIcon from "@patternfly/react-icons/dist/js/icons/optimize-icon"; +import { useDmnEditorStoreApi } from "../store/StoreContext"; +import { getAutoLayoutedInfo } from "./autoLayoutInfo"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; -import { addEdge } from "../mutations/addEdge"; -import { repositionNode } from "../mutations/repositionNode"; -import { resizeNode } from "../mutations/resizeNode"; -import { updateDecisionServiceDividerLine } from "../mutations/updateDecisionServiceDividerLine"; -import { useDmnEditorStore, useDmnEditorStoreApi } from "../store/StoreContext"; - -const elk = new ELK(); - -export const ELK_OPTIONS = { - "elk.algorithm": "layered", - "elk.direction": "UP", - // By making width a lot bigger than height, we make sure disjoint graph components are placed horizontally, never vertically - "elk.aspectRatio": "9999999999", - // spacing - "elk.spacing.nodeNode": "60", - "elk.spacing.componentComponent": "200", - "layered.spacing.edgeEdgeBetweenLayers": "0", - "layered.spacing.edgeNodeBetweenLayers": "0", - "layered.spacing.nodeNodeBetweenLayers": "100", - // edges - "elk.edgeRouting": "ORTHOGONAL", - "elk.layered.mergeEdges": "true", // we need this to make sure space is consistent between layers. - "elk.layered.mergeHierarchyEdges": "true", - // positioning - "elk.partitioning.activate": "true", - "elk.nodePlacement.favorStraightEdges": "true", - "elk.nodePlacement.bk.fixedAlignment": "LEFTDOWN", - "elk.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS", - // - "layering.strategy": "LONGEST_PATH_SOURCE", -}; - -const PARENT_NODE_ELK_OPTIONS = { - "elk.padding": "[left=60, top=60, right=80, bottom=60]", - "elk.spacing.componentComponent": "60", -}; - -export interface AutolayoutParentNode { - decisionServiceSection: "output" | "encapsulated" | "n/a"; - elkNode: Elk.ElkNode; - contained: Set; - dependents: Set; - dependencies: Set; - contains: (otherNode: { id: string; bounds: DC__Bounds | undefined }) => { - isInside: boolean; - decisionServiceSection: AutolayoutParentNode["decisionServiceSection"]; - }; - hasDependencyTo: (otherNode: { id: string }) => boolean; - isDependencyOf: (otherNode: { id: string }) => boolean; -} - -const FAKE_MARKER = "__$FAKE$__"; +import { applyAutoLayoutToDrd } from "../mutations/applyAutoLayoutToDrd"; export function AutolayoutButton() { const dmnEditorStoreApi = useDmnEditorStoreApi(); const { externalModelsByNamespace } = useExternalModels(); - const isAlternativeInputDataShape = useDmnEditorStore((s) => s.computed(s).isAlternativeInputDataShape()); - - const onApply = React.useCallback(async () => { - const parentNodesById = new Map(); - const nodeParentsById = new Map>(); - - /** - Used to tell ELK that dependencies of nodes' children should be considered the node's dependency too. - This allows us to not rely on INCLUDE_STRATEGY hierarchy handling on ELK, keeping disjoint graph components separate, rendering side-by-side. - */ - const fakeEdgesForElk = new Set(); + const onClick = React.useCallback(async () => { const state = dmnEditorStoreApi.getState(); - const snapGrid = state.diagram.snapGrid; const nodesById = state.computed(state).getDiagramData(externalModelsByNamespace).nodesById; const edgesById = state.computed(state).getDiagramData(externalModelsByNamespace).edgesById; const nodes = state.computed(state).getDiagramData(externalModelsByNamespace).nodes; - const edges = state.computed(state).getDiagramData(externalModelsByNamespace).edges; const drgEdges = state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges; - - const adjMatrix = getAdjMatrix(drgEdges); - - // 1. First we populate the `parentNodesById` map so that we know exactly what parent nodes we're dealing with. Decision Service nodes have two fake nodes to represent Output and Encapsulated sections. - for (const node of nodes) { - const dependencies = new Set(); - const dependents = new Set(); - - if (node.data?.dmnObject?.__$$element === "decisionService") { - const outputs = new Set([...(node.data.dmnObject.outputDecision ?? []).map((s) => s["@_href"])]); - const encapsulated = new Set([...(node.data.dmnObject.encapsulatedDecision ?? []).map((s) => s["@_href"])]); - - const idOfFakeNodeForOutputSection = `${node.id}${FAKE_MARKER}dsOutput`; - const idOfFakeNodeForEncapsulatedSection = `${node.id}${FAKE_MARKER}dsEncapsulated`; - - const dsSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({ snapGrid }); - parentNodesById.set(node.id, { - elkNode: { - id: node.id, - width: dsSize["@_width"], - height: dsSize["@_height"], - children: [ - { - id: idOfFakeNodeForOutputSection, - width: dsSize["@_width"], - height: dsSize["@_height"] / 2, - children: [], - layoutOptions: { - ...ELK_OPTIONS, - ...PARENT_NODE_ELK_OPTIONS, - }, - }, - { - id: idOfFakeNodeForEncapsulatedSection, - width: dsSize["@_width"], - height: dsSize["@_height"] / 2, - children: [], - layoutOptions: { - ...ELK_OPTIONS, - ...PARENT_NODE_ELK_OPTIONS, - }, - }, - ], - layoutOptions: { - "elk.algorithm": "layered", - "elk.direction": "UP", - "elk.aspectRatio": "9999999999", - "elk.partitioning.activate": "true", - "elk.spacing.nodeNode": "0", - "elk.spacing.componentComponent": "0", - "layered.spacing.edgeEdgeBetweenLayers": "0", - "layered.spacing.edgeNodeBetweenLayers": "0", - "layered.spacing.nodeNodeBetweenLayers": "0", - "elk.padding": "[left=0, top=0, right=0, bottom=0]", - }, - }, - decisionServiceSection: "output", - dependencies, - dependents, - contained: outputs, - contains: ({ id }) => ({ - isInside: outputs.has(id) || encapsulated.has(id), - decisionServiceSection: outputs.has(id) ? "output" : encapsulated.has(id) ? "encapsulated" : "n/a", - }), - isDependencyOf: ({ id }) => dependents.has(id), - hasDependencyTo: ({ id }) => dependencies.has(id), - }); - - fakeEdgesForElk.add({ - id: `${node.id}${FAKE_MARKER}fakeOutputEncapsulatedEdge`, - sources: [idOfFakeNodeForEncapsulatedSection], - targets: [idOfFakeNodeForOutputSection], - }); - } else if (node.data?.dmnObject?.__$$element === "group") { - const groupSize = DEFAULT_NODE_SIZES[NODE_TYPES.group]({ snapGrid }); - const groupBounds = node.data.shape["dc:Bounds"]; - parentNodesById.set(node.id, { - decisionServiceSection: "n/a", - elkNode: { - id: node.id, - width: groupBounds?.["@_width"] ?? groupSize["@_width"], - height: groupBounds?.["@_height"] ?? groupSize["@_height"], - children: [], - layoutOptions: { - ...ELK_OPTIONS, - ...PARENT_NODE_ELK_OPTIONS, - }, - }, - dependencies, - dependents, - contained: new Set(), - contains: ({ id, bounds }) => ({ - isInside: getContainmentRelationship({ - bounds: bounds!, - container: groupBounds!, - snapGrid, - isAlternativeInputDataShape, - containerMinSizes: MIN_NODE_SIZES[NODE_TYPES.group], - boundsMinSizes: MIN_NODE_SIZES[nodesById.get(id)?.type as NodeType], - }).isInside, - decisionServiceSection: "n/a", - }), - isDependencyOf: ({ id }) => dependents.has(id), - hasDependencyTo: ({ id }) => dependencies.has(id), - }); - } - } - - // 2. Then we map all the nodes to elkNodes, including the parents. We mutate parents on the fly when iterating over the nodes list. - const elkNodes = nodes.flatMap((node) => { - const parent = parentNodesById.get(node.id); - if (parent) { - return []; - } - - const defaultSize = DEFAULT_NODE_SIZES[node.type as NodeType]({ snapGrid, isAlternativeInputDataShape }); - const elkNode: Elk.ElkNode = { - id: node.id, - width: node.data.shape["dc:Bounds"]?.["@_width"] ?? defaultSize["@_width"], - height: node.data.shape["dc:Bounds"]?.["@_height"] ?? defaultSize["@_height"], - children: [], - layoutOptions: { - "partitioning.partition": - // Since textAnnotations and knowledgeSources are not related to the logic, we leave them at the bottom. - (node.type as NodeType) === NODE_TYPES.textAnnotation || - (node.type as NodeType) === NODE_TYPES.knowledgeSource - ? "0" - : "1", - }, - }; - - // FIXME: Tiago --> Improve performance here as part of https://github.com/apache/incubator-kie-issues/issues/451. - const parents = [...parentNodesById.values()].filter( - (p) => p.contains({ id: elkNode.id, bounds: node.data.shape["dc:Bounds"] }).isInside - ); - if (parents.length > 0) { - const decisionServiceSection = parents[0].contains({ - id: elkNode.id, - bounds: node.data.shape["dc:Bounds"], - }).decisionServiceSection; - - // The only relationship that ELK will know about is the first matching container for this node. - if (decisionServiceSection === "n/a") { - parents[0].elkNode.children?.push(elkNode); - } else if (decisionServiceSection === "output") { - parents[0].elkNode.children?.[0].children?.push(elkNode); - } else if (decisionServiceSection === "encapsulated") { - parents[0].elkNode.children?.[1].children?.push(elkNode); - } else { - throw new Error(`Unknown decisionServiceSection ${decisionServiceSection}`); - } - - for (const p of parents) { - p.contained?.add(elkNode.id); // We need to keep track of nodes that are contained by multiple groups, but ELK will only know about one of those containment relationships. - nodeParentsById.set(node.id, new Set([...(nodeParentsById.get(node.id) ?? []), p.elkNode.id])); - } - return []; - } - - return [elkNode]; + const isAlternativeInputDataShape = state.computed(state).isAlternativeInputDataShape(); + + const { __readonly_autoLayoutedInfo, __readonly_parentNodesById } = await getAutoLayoutedInfo({ + __readonly_snapGrid: snapGrid, + __readonly_nodesById: nodesById, + __readonly_edgesById: edgesById, + __readonly_nodes: nodes, + __readonly_drgEdges: drgEdges, + __readonly_isAlternativeInputDataShape: isAlternativeInputDataShape, }); - // 3. After we have all containment relationships defined, we can proceed to resolving the hierarchical relationships. - for (const [_, parentNode] of parentNodesById) { - traverse(adjMatrix, parentNode.contained, [...parentNode.contained], "down", (n) => { - parentNode.dependencies.add(n); - }); - traverse(adjMatrix, parentNode.contained, [...parentNode.contained], "up", (n) => { - parentNode.dependents.add(n); - }); - - const p = nodesById.get(parentNode.elkNode.id); - if (p?.type === NODE_TYPES.group && parentNode.elkNode.children?.length === 0) { - continue; // Ignore empty group nodes. - } else { - elkNodes.push(parentNode.elkNode); - } - } - - // 4. After we have all containment and hierarchical relationships defined, we can add the fake edges so that ELK creates the structure correctly. - for (const node of nodes) { - const parentNodes = [...parentNodesById.values()]; - - const dependents = parentNodes.filter((p) => p.hasDependencyTo({ id: node.id })); - for (const dependent of dependents) { - // Not all nodes are present in all DRD - if (nodesById.has(node.id) && nodesById.has(dependent.elkNode.id)) { - fakeEdgesForElk.add({ - id: `${generateUuid()}${FAKE_MARKER}__fake`, - sources: [node.id], - targets: [dependent.elkNode.id], - }); - } - - for (const p of nodeParentsById.get(node.id) ?? []) { - // Not all nodes are present in all DRD - if (nodesById.has(p) && nodesById.has(dependent.elkNode.id)) { - fakeEdgesForElk.add({ - id: `${generateUuid()}${FAKE_MARKER}__fake`, - sources: [p], - targets: [dependent.elkNode.id], - }); - } - } - } - - const dependencies = parentNodes.filter((p) => p.isDependencyOf({ id: node.id })); - for (const dependency of dependencies) { - // Not all nodes are present in all DRD - if (nodesById.has(node.id) && nodesById.has(dependency.elkNode.id)) { - fakeEdgesForElk.add({ - id: `${generateUuid()}${FAKE_MARKER}__fake`, - sources: [dependency.elkNode.id], - targets: [node.id], - }); - } - - for (const p of nodeParentsById.get(node.id) ?? []) { - // Not all nodes are present in all DRD - if (nodesById.has(p) && nodesById.has(dependency.elkNode.id)) { - fakeEdgesForElk.add({ - id: `${generateUuid()}${FAKE_MARKER}__fake`, - sources: [dependency.elkNode.id], - targets: [p], - }); - } - } - } - } - - // 5. Concatenate real and fake edges to pass to ELK. - const elkEdges = [ - ...fakeEdgesForElk, - ...[...edgesById.values()].flatMap((e) => { - // Not all nodes are present in all DRD - if (nodesById.has(e.source) && nodesById.has(e.target)) { - return { - id: e.id, - sources: [e.source], - targets: [e.target], - }; - } else { - return []; - } - }), - ]; - - // 6. Run ELK. - const autolayouted = await runElk(elkNodes, elkEdges, ELK_OPTIONS); - - // 7. Update all nodes positions skipping empty groups, which will be positioned manually after all nodes are done being repositioned. dmnEditorStoreApi.setState((s) => { - const autolayoutedElkNodesById = new Map(); - - for (const topLevelElkNode of autolayouted.nodes ?? []) { - visitNodeAndNested(topLevelElkNode, { x: 100, y: 100 }, (elkNode, positionOffset) => { - if (elkNode.id.includes(FAKE_MARKER)) { - return; - } - - autolayoutedElkNodesById.set(elkNode.id, elkNode); - - const nodeId = elkNode.id; - const node = s.computed(s).getDiagramData(externalModelsByNamespace).nodesById.get(nodeId)!; - - repositionNode({ - definitions: s.dmn.model.definitions, - drdIndex: s.diagram.drdIndex, - controlWaypointsByEdge: new Map(), - change: { - nodeType: node.type as NodeType, - type: "absolute", - position: { - x: elkNode.x! + positionOffset.x, - y: elkNode.y! + positionOffset.y, - }, - selectedEdges: [...edgesById.keys()], - shapeIndex: node.data?.shape.index, - sourceEdgeIndexes: edges.flatMap((e) => - e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] - ), - targetEdgeIndexes: edges.flatMap((e) => - e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] - ), - }, - }); - }); - } - - // 8. Resize all nodes using the sizes calculated by ELK. - for (const topLevelElkNode of autolayouted.nodes ?? []) { - visitNodeAndNested(topLevelElkNode, { x: 0, y: 0 }, (elkNode) => { - if (elkNode.id.includes(FAKE_MARKER)) { - return; - } - - const nodeId = elkNode.id; - const node = s.computed(s).getDiagramData(externalModelsByNamespace).nodesById.get(nodeId)!; - - resizeNode({ - definitions: s.dmn.model.definitions, - drdIndex: s.diagram.drdIndex, - dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref, - snapGrid, - change: { - index: node.data.index, - isExternal: !!node.data.dmnObjectQName.prefix, - nodeType: node.type as NodeType, - dimension: { - "@_width": elkNode.width!, - "@_height": elkNode.height!, - }, - shapeIndex: node.data?.shape.index, - sourceEdgeIndexes: edges.flatMap((e) => - e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] - ), - targetEdgeIndexes: edges.flatMap((e) => - e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] - ), - }, - }); - }); - } - - // 9. Updating Decision Service divider lines after all nodes are repositioned and resized. - for (const [parentNodeId] of parentNodesById) { - const parentNode = s.computed(s).getDiagramData(externalModelsByNamespace).nodesById.get(parentNodeId); - if (parentNode?.type !== NODE_TYPES.decisionService) { - continue; - } - - const elkNode = autolayoutedElkNodesById.get(parentNodeId); - if (!elkNode) { - throw new Error(`Couldn't find Decision Service with id ${parentNode.id} at the autolayouted nodes map`); - } - - /** - * The second children of a Decision Service elkNode is a node representing the Encapsulated section. - * It's Y position will be exactly where the divider line should be. - */ - const dividerLinerLocalYPosition = elkNode.children?.[1]?.y; - if (!dividerLinerLocalYPosition) { - throw new Error( - `Couldn't find second child (which represents the Encapuslated Decision section) of Decision Service with id ${parentNode.id} at the autolayouted nodes map` - ); - } - - updateDecisionServiceDividerLine({ - definitions: s.dmn.model.definitions, - drdIndex: s.diagram.drdIndex, - dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref, - drgElementIndex: parentNode.data.index, - shapeIndex: parentNode.data.shape.index, - snapGrid, - localYPosition: dividerLinerLocalYPosition, - }); - } - - // 10. Update the edges. Edges always go from top to bottom, removing waypoints. - for (const elkEdge of autolayouted.edges ?? []) { - if (elkEdge.id.includes(FAKE_MARKER)) { - continue; - } - - const edge = s.computed(s).getDiagramData(externalModelsByNamespace).edgesById.get(elkEdge.id)!; - - const sourceNode = s.computed(s).getDiagramData(externalModelsByNamespace).nodesById.get(elkEdge.sources[0])!; - const targetNode = s.computed(s).getDiagramData(externalModelsByNamespace).nodesById.get(elkEdge.targets[0])!; - - // If the target is an external node, we don't have to create the edge. - if (targetNode.data.dmnObjectQName.prefix) { - continue; - } - - addEdge({ - definitions: s.dmn.model.definitions, - drdIndex: s.diagram.drdIndex, - edge: { - autoPositionedEdgeMarker: undefined, - type: edge.type as EdgeType, - targetHandle: PositionalNodeHandleId.Bottom, - sourceHandle: PositionalNodeHandleId.Top, - }, - sourceNode: { - type: sourceNode.type as NodeType, - href: sourceNode.id, - data: sourceNode.data, - bounds: sourceNode.data.shape["dc:Bounds"]!, - shapeId: sourceNode.data.shape["@_id"], - }, - targetNode: { - type: targetNode.type as NodeType, - href: targetNode.id, - data: targetNode.data, - bounds: targetNode.data.shape["dc:Bounds"]!, - index: targetNode.data.index, - shapeId: targetNode.data.shape["@_id"], - }, - keepWaypoints: false, - }); - } + applyAutoLayoutToDrd({ + state: s, + __readonly_dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref, + __readonly_edges: s.computed(s).getDiagramData(externalModelsByNamespace).edges, + __readonly_edgesById: s.computed(s).getDiagramData(externalModelsByNamespace).edgesById, + __readonly_nodesById: s.computed(s).getDiagramData(externalModelsByNamespace).nodesById, + __readonly_autoLayoutedInfo, + __readonly_parentNodesById, + __readonly_drdIndex: s.computed(s).getDrdIndex(), + __readonly_dmnObjectNamespace: s.dmn.model.definitions["@_namespace"], + __readonly_externalDmnsIndex: s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns, + }); }); - }, [dmnEditorStoreApi, externalModelsByNamespace, isAlternativeInputDataShape]); + }, [dmnEditorStoreApi, externalModelsByNamespace]); return ( - ); } - -// - -export async function runElk( - nodes: Elk.ElkNode[], - edges: { id: string; sources: string[]; targets: string[] }[], - options: Elk.LayoutOptions = {} -): Promise<{ isHorizontal: boolean; nodes: Elk.ElkNode[] | undefined; edges: Elk.ElkExtendedEdge[] | undefined }> { - const isHorizontal = options?.["elk.direction"] === "RIGHT"; - - const graph: Elk.ElkNode = { - id: "root", - layoutOptions: options, - children: nodes, - edges, - }; - - const layoutedGraph = await elk.layout(graph); - return { - isHorizontal, - nodes: layoutedGraph.children, - edges: layoutedGraph.edges as any[], - }; -} - -function visitNodeAndNested( - elkNode: Elk.ElkNode, - positionOffset: { x: number; y: number }, - visitor: (elkNode: Elk.ElkNode, positionOffset: { x: number; y: number }) => void -) { - visitor(elkNode, positionOffset); - for (const nestedNode of elkNode.children ?? []) { - visitNodeAndNested( - nestedNode, - { - x: elkNode.x! + positionOffset.x, - y: elkNode.y! + positionOffset.y, - }, - visitor - ); - } -} diff --git a/packages/dmn-editor/src/autolayout/autoLayoutInfo.ts b/packages/dmn-editor/src/autolayout/autoLayoutInfo.ts new file mode 100644 index 00000000000..74cd1b79ba2 --- /dev/null +++ b/packages/dmn-editor/src/autolayout/autoLayoutInfo.ts @@ -0,0 +1,407 @@ +/* + * 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. + */ + +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; +import { DC__Bounds } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import ELK, * as Elk from "elkjs/lib/elk.bundled.js"; +import { Edge, Node } from "reactflow"; +import { NodeType } from "../diagram/connections/graphStructure"; +import { DmnDiagramEdgeData } from "../diagram/edges/Edges"; +import { DrgEdge, getAdjMatrix, traverse } from "../diagram/graph/graph"; +import { getContainmentRelationship } from "../diagram/maths/DmnMaths"; +import { DEFAULT_NODE_SIZES, MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; +import { DmnDiagramNodeData } from "../diagram/nodes/Nodes"; +import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import { SnapGrid } from "../store/Store"; +import { addNamespaceToHref, parseXmlHref } from "../xml/xmlHrefs"; + +const elk = new ELK(); + +export const ELK_OPTIONS = { + "elk.algorithm": "layered", + "elk.direction": "UP", + // By making width a lot bigger than height, we make sure disjoint graph components are placed horizontally, never vertically + "elk.aspectRatio": "9999999999", + // spacing + "elk.spacing.nodeNode": "60", + "elk.spacing.componentComponent": "200", + "layered.spacing.edgeEdgeBetweenLayers": "0", + "layered.spacing.edgeNodeBetweenLayers": "0", + "layered.spacing.nodeNodeBetweenLayers": "100", + // edges + "elk.edgeRouting": "ORTHOGONAL", + "elk.layered.mergeEdges": "true", // we need this to make sure space is consistent between layers. + "elk.layered.mergeHierarchyEdges": "true", + // positioning + "elk.partitioning.activate": "true", + "elk.nodePlacement.favorStraightEdges": "true", + "elk.nodePlacement.bk.fixedAlignment": "LEFTDOWN", + "elk.nodePlacement.bk.edgeStraightening": "IMPROVE_STRAIGHTNESS", + // + "layering.strategy": "LONGEST_PATH_SOURCE", +}; + +const PARENT_NODE_ELK_OPTIONS = { + "elk.padding": "[left=60, top=60, right=80, bottom=60]", + "elk.spacing.componentComponent": "60", +}; + +export interface AutolayoutParentNode { + decisionServiceSection: "output" | "encapsulated" | "n/a"; + elkNode: Elk.ElkNode; + contained: Set; + dependents: Set; + dependencies: Set; + contains: (otherNode: { id: string; bounds: DC__Bounds | undefined }) => { + isInside: boolean; + decisionServiceSection: AutolayoutParentNode["decisionServiceSection"]; + }; + hasDependencyTo: (otherNode: { id: string }) => boolean; + isDependencyOf: (otherNode: { id: string }) => boolean; +} + +export const FAKE_MARKER = "__$FAKE$__"; + +export async function getAutoLayoutedInfo({ + __readonly_snapGrid, + __readonly_nodesById, + __readonly_edgesById, + __readonly_nodes, + __readonly_drgEdges, + __readonly_isAlternativeInputDataShape, +}: { + __readonly_snapGrid: SnapGrid; + __readonly_nodesById: Map>; + __readonly_edgesById: Map>; + __readonly_nodes: Node[]; + __readonly_drgEdges: DrgEdge[]; + __readonly_isAlternativeInputDataShape: boolean; +}) { + const parentNodesById = new Map(); + const nodeParentsById = new Map>(); + + /** + Used to tell ELK that dependencies of nodes' children should be considered the node's dependency too. + This allows us to not rely on INCLUDE_STRATEGY hierarchy handling on ELK, keeping disjoint graph components separate, rendering side-by-side. + */ + const fakeEdgesForElk = new Set(); + + const adjMatrix = getAdjMatrix(__readonly_drgEdges); + + // 1. First we populate the `parentNodesById` map so that we know exactly what parent nodes we're dealing with. Decision Service nodes have two fake nodes to represent Output and Encapsulated sections. + for (const node of __readonly_nodes) { + const dependencies = new Set(); + const dependents = new Set(); + + if (node.data?.dmnObject?.__$$element === "decisionService") { + const { namespace } = parseXmlHref(node.id); + const outputs = new Set([ + ...(node.data.dmnObject.outputDecision ?? []).map((s) => addNamespaceToHref({ href: s["@_href"], namespace })), + ]); + const encapsulated = new Set([ + ...(node.data.dmnObject.encapsulatedDecision ?? []).map((s) => + addNamespaceToHref({ href: s["@_href"], namespace }) + ), + ]); + + const idOfFakeNodeForOutputSection = `${node.id}${FAKE_MARKER}dsOutput`; + const idOfFakeNodeForEncapsulatedSection = `${node.id}${FAKE_MARKER}dsEncapsulated`; + + const dsSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({ snapGrid: __readonly_snapGrid }); + parentNodesById.set(node.id, { + elkNode: { + id: node.id, + width: dsSize["@_width"], + height: dsSize["@_height"], + children: [ + { + id: idOfFakeNodeForOutputSection, + width: dsSize["@_width"], + height: dsSize["@_height"] / 2, + children: [], + layoutOptions: { + ...ELK_OPTIONS, + ...PARENT_NODE_ELK_OPTIONS, + }, + }, + { + id: idOfFakeNodeForEncapsulatedSection, + width: dsSize["@_width"], + height: dsSize["@_height"] / 2, + children: [], + layoutOptions: { + ...ELK_OPTIONS, + ...PARENT_NODE_ELK_OPTIONS, + }, + }, + ], + layoutOptions: { + "elk.algorithm": "layered", + "elk.direction": "UP", + "elk.aspectRatio": "9999999999", + "elk.partitioning.activate": "true", + "elk.spacing.nodeNode": "0", + "elk.spacing.componentComponent": "0", + "layered.spacing.edgeEdgeBetweenLayers": "0", + "layered.spacing.edgeNodeBetweenLayers": "0", + "layered.spacing.nodeNodeBetweenLayers": "0", + "elk.padding": "[left=0, top=0, right=0, bottom=0]", + }, + }, + decisionServiceSection: "output", + dependencies, + dependents, + contained: outputs, + contains: ({ id }) => ({ + isInside: outputs.has(id) || encapsulated.has(id), + decisionServiceSection: outputs.has(id) ? "output" : encapsulated.has(id) ? "encapsulated" : "n/a", + }), + isDependencyOf: ({ id }) => dependents.has(id), + hasDependencyTo: ({ id }) => dependencies.has(id), + }); + + fakeEdgesForElk.add({ + id: `${node.id}${FAKE_MARKER}fakeOutputEncapsulatedEdge`, + sources: [idOfFakeNodeForEncapsulatedSection], + targets: [idOfFakeNodeForOutputSection], + }); + } else if (node.data?.dmnObject?.__$$element === "group") { + const groupSize = DEFAULT_NODE_SIZES[NODE_TYPES.group]({ snapGrid: __readonly_snapGrid }); + const groupBounds = node.data.shape["dc:Bounds"]; + parentNodesById.set(node.id, { + decisionServiceSection: "n/a", + elkNode: { + id: node.id, + width: groupBounds?.["@_width"] ?? groupSize["@_width"], + height: groupBounds?.["@_height"] ?? groupSize["@_height"], + children: [], + layoutOptions: { + ...ELK_OPTIONS, + ...PARENT_NODE_ELK_OPTIONS, + }, + }, + dependencies, + dependents, + contained: new Set(), + contains: ({ id, bounds }) => ({ + isInside: getContainmentRelationship({ + bounds: bounds!, + container: groupBounds!, + snapGrid: __readonly_snapGrid, + isAlternativeInputDataShape: __readonly_isAlternativeInputDataShape, + containerMinSizes: MIN_NODE_SIZES[NODE_TYPES.group], + boundsMinSizes: MIN_NODE_SIZES[__readonly_nodesById.get(id)?.type as NodeType], + }).isInside, + decisionServiceSection: "n/a", + }), + isDependencyOf: ({ id }) => dependents.has(id), + hasDependencyTo: ({ id }) => dependencies.has(id), + }); + } + } + + // 2. Then we map all the nodes to elkNodes, including the parents. We mutate parents on the fly when iterating over the nodes list. + const elkNodes = __readonly_nodes.flatMap((node) => { + const parent = parentNodesById.get(node.id); + if (parent) { + return []; + } + + const defaultSize = DEFAULT_NODE_SIZES[node.type as NodeType]({ + snapGrid: __readonly_snapGrid, + isAlternativeInputDataShape: __readonly_isAlternativeInputDataShape, + }); + const elkNode: Elk.ElkNode = { + id: node.id, + width: node.data.shape["dc:Bounds"]?.["@_width"] ?? defaultSize["@_width"], + height: node.data.shape["dc:Bounds"]?.["@_height"] ?? defaultSize["@_height"], + children: [], + layoutOptions: { + "partitioning.partition": + // Since textAnnotations and knowledgeSources are not related to the logic, we leave them at the bottom. + (node.type as NodeType) === NODE_TYPES.textAnnotation || + (node.type as NodeType) === NODE_TYPES.knowledgeSource + ? "0" + : "1", + }, + }; + + // FIXME: Tiago --> Improve performance here as part of https://github.com/apache/incubator-kie-issues/issues/451. + const parents = [...parentNodesById.values()].filter( + (p) => p.contains({ id: elkNode.id, bounds: node.data.shape["dc:Bounds"] }).isInside + ); + if (parents.length > 0) { + const decisionServiceSection = parents[0].contains({ + id: elkNode.id, + bounds: node.data.shape["dc:Bounds"], + }).decisionServiceSection; + + // The only relationship that ELK will know about is the first matching container for this node. + if (decisionServiceSection === "n/a") { + parents[0].elkNode.children?.push(elkNode); + } else if (decisionServiceSection === "output") { + parents[0].elkNode.children?.[0].children?.push(elkNode); + } else if (decisionServiceSection === "encapsulated") { + parents[0].elkNode.children?.[1].children?.push(elkNode); + } else { + throw new Error(`Unknown decisionServiceSection ${decisionServiceSection}`); + } + + for (const p of parents) { + p.contained?.add(elkNode.id); // We need to keep track of nodes that are contained by multiple groups, but ELK will only know about one of those containment relationships. + nodeParentsById.set(node.id, new Set([...(nodeParentsById.get(node.id) ?? []), p.elkNode.id])); + } + return []; + } + + return [elkNode]; + }); + + // 3. After we have all containment relationships defined, we can proceed to resolving the hierarchical relationships. + for (const [_, parentNode] of parentNodesById) { + traverse(adjMatrix, parentNode.contained, [...parentNode.contained], "down", (n) => { + parentNode.dependencies.add(n); + }); + traverse(adjMatrix, parentNode.contained, [...parentNode.contained], "up", (n) => { + parentNode.dependents.add(n); + }); + + const p = __readonly_nodesById.get(parentNode.elkNode.id); + if (p?.type === NODE_TYPES.group && parentNode.elkNode.children?.length === 0) { + continue; // Ignore empty group nodes. + } else { + elkNodes.push(parentNode.elkNode); + } + } + + // 4. After we have all containment and hierarchical relationships defined, we can add the fake edges so that ELK creates the structure correctly. + for (const node of __readonly_nodes) { + const parentNodes = [...parentNodesById.values()]; + + const dependents = parentNodes.filter((p) => p.hasDependencyTo({ id: node.id })); + for (const dependent of dependents) { + // Not all nodes are present in all DRD + if (__readonly_nodesById.has(node.id) && __readonly_nodesById.has(dependent.elkNode.id)) { + fakeEdgesForElk.add({ + id: `${generateUuid()}${FAKE_MARKER}__fake`, + sources: [node.id], + targets: [dependent.elkNode.id], + }); + } + + for (const p of nodeParentsById.get(node.id) ?? []) { + // Not all nodes are present in all DRD + if (__readonly_nodesById.has(p) && __readonly_nodesById.has(dependent.elkNode.id)) { + fakeEdgesForElk.add({ + id: `${generateUuid()}${FAKE_MARKER}__fake`, + sources: [p], + targets: [dependent.elkNode.id], + }); + } + } + } + + const dependencies = parentNodes.filter((p) => p.isDependencyOf({ id: node.id })); + for (const dependency of dependencies) { + // Not all nodes are present in all DRD + if (__readonly_nodesById.has(node.id) && __readonly_nodesById.has(dependency.elkNode.id)) { + fakeEdgesForElk.add({ + id: `${generateUuid()}${FAKE_MARKER}__fake`, + sources: [dependency.elkNode.id], + targets: [node.id], + }); + } + + for (const p of nodeParentsById.get(node.id) ?? []) { + // Not all nodes are present in all DRD + if (__readonly_nodesById.has(p) && __readonly_nodesById.has(dependency.elkNode.id)) { + fakeEdgesForElk.add({ + id: `${generateUuid()}${FAKE_MARKER}__fake`, + sources: [dependency.elkNode.id], + targets: [p], + }); + } + } + } + } + + // 5. Concatenate real and fake edges to pass to ELK. + const elkEdges = [ + ...fakeEdgesForElk, + ...[...__readonly_edgesById.values()].flatMap((e) => { + // Not all nodes are present in all DRD + if (__readonly_nodesById.has(e.source) && __readonly_nodesById.has(e.target)) { + return { + id: e.id, + sources: [e.source], + targets: [e.target], + }; + } else { + return []; + } + }), + ]; + + // 6. Run ELK. + const autoLayoutedInfo = await runElk(elkNodes, elkEdges, ELK_OPTIONS); + return { + __readonly_autoLayoutedInfo: autoLayoutedInfo, + __readonly_parentNodesById: parentNodesById, + }; +} + +async function runElk( + nodes: Elk.ElkNode[], + edges: { id: string; sources: string[]; targets: string[] }[], + options: Elk.LayoutOptions = {} +): Promise<{ isHorizontal: boolean; nodes: Elk.ElkNode[] | undefined; edges: Elk.ElkExtendedEdge[] | undefined }> { + const isHorizontal = options?.["elk.direction"] === "RIGHT"; + + const graph: Elk.ElkNode = { + id: "root", + layoutOptions: options, + children: nodes, + edges, + }; + + const layoutedGraph = await elk.layout(graph); + return { + isHorizontal, + nodes: layoutedGraph.children, + edges: layoutedGraph.edges as any[], + }; +} + +export function visitNodeAndNested( + elkNode: Elk.ElkNode, + positionOffset: { x: number; y: number }, + visitor: (elkNode: Elk.ElkNode, positionOffset: { x: number; y: number }) => void +) { + visitor(elkNode, positionOffset); + for (const nestedNode of elkNode.children ?? []) { + visitNodeAndNested( + nestedNode, + { + x: elkNode.x! + positionOffset.x, + y: elkNode.y! + positionOffset.y, + }, + visitor + ); + } +} diff --git a/packages/dmn-editor/src/boxedExpressions/BoxedExpressionScreen.tsx b/packages/dmn-editor/src/boxedExpressions/BoxedExpressionScreen.tsx index dbc155484f7..3e35591cb4d 100644 --- a/packages/dmn-editor/src/boxedExpressions/BoxedExpressionScreen.tsx +++ b/packages/dmn-editor/src/boxedExpressions/BoxedExpressionScreen.tsx @@ -80,6 +80,7 @@ import { DmnEditorTab } from "../store/Store"; import { useDmnEditorStore, useDmnEditorStoreApi } from "../store/StoreContext"; import { getDefaultColumnWidth } from "./getDefaultColumnWidth"; import { getDefaultBoxedExpression } from "./getDefaultBoxedExpression"; +import { Normalized } from "../normalization/normalize"; export function BoxedExpressionScreen({ container }: { container: React.RefObject }) { const { externalModelsByNamespace } = useExternalModels(); @@ -101,6 +102,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec (s) => s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace).pmmls ); const isAlternativeInputDataShape = useDmnEditorStore((s) => s.computed(s).isAlternativeInputDataShape()); + const drdIndex = useDmnEditorStore((s) => s.computed(s).getDrdIndex()); const onRequestFeelVariables = useCallback(() => { const externalModels = new Map(); @@ -142,7 +144,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec // recalculated, breaking batching. const widthsById = useMemo(() => { return ( - thisDmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.[diagram.drdIndex]["di:extension"]?.[ + thisDmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.[drdIndex]["di:extension"]?.[ "kie:ComponentsWidthsExtension" ]?.["kie:ComponentWidths"] ?? [] ).reduce((acc, c) => { @@ -155,7 +157,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec ); } }, new Map()); - }, [diagram.drdIndex, thisDmn.model.definitions]); + }, [drdIndex, thisDmn.model.definitions]); const expression = useMemo(() => { if (!drgElement) { @@ -171,7 +173,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec }, [drgElement, drgElementIndex]); const widthsByIdRef = useRef>(widthsById); - const boxedExpressionRef = useRef(expression?.boxedExpression); + const boxedExpressionRef = useRef | undefined>(expression?.boxedExpression); useEffect(() => { widthsByIdRef.current = widthsById; @@ -193,7 +195,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec updateExpressionWidths({ definitions: state.dmn.model.definitions, - drdIndex: state.diagram.drdIndex, + drdIndex: state.computed(state).getDrdIndex(), widthsById: newWidthsById, }); }); @@ -201,7 +203,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec [dmnEditorStoreApi] ); - const onExpressionChange: React.Dispatch> = useCallback( + const onExpressionChange: React.Dispatch>> = useCallback( (newExpressionAction) => { dmnEditorStoreApi.setState((state) => { const newExpression = @@ -384,11 +386,7 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec isResetSupportedOnRootExpression={isResetSupportedOnRootExpression} expressionHolderId={activeDrgElementId!} expressionHolderName={drgElement?.variable?.["@_name"] ?? drgElement?.["@_name"] ?? ""} - expressionHolderTypeRef={ - drgElement?.variable?.["@_typeRef"] ?? - expression?.boxedExpression?.["@_typeRef"] ?? - DmnBuiltInDataType.Undefined - } + expressionHolderTypeRef={drgElement?.variable?.["@_typeRef"] ?? expression?.boxedExpression?.["@_typeRef"]} expression={expression?.boxedExpression} onExpressionChange={onExpressionChange} dataTypes={dataTypes} @@ -405,18 +403,20 @@ export function BoxedExpressionScreen({ container }: { container: React.RefObjec export function drgElementToBoxedExpression( expressionHolder: - | (DMN15__tDecision & { __$$element: "decision" }) - | (DMN15__tBusinessKnowledgeModel & { __$$element: "businessKnowledgeModel" }) -): BoxedExpression | undefined { + | (Normalized & { __$$element: "decision" }) + | (Normalized & { __$$element: "businessKnowledgeModel" }) +): Normalized | undefined { if (expressionHolder.__$$element === "businessKnowledgeModel") { return expressionHolder.encapsulatedLogic ? { __$$element: "functionDefinition", + "@_label": expressionHolder.encapsulatedLogic["@_label"] ?? expressionHolder["@_name"], + "@_typeRef": expressionHolder.encapsulatedLogic["@_typeRef"] ?? expressionHolder.variable?.["@_typeRef"], ...expressionHolder.encapsulatedLogic, } : { __$$element: "functionDefinition", - "@_id": expressionHolder.variable?.["@_id"] ?? generateUuid(), + "@_id": generateUuid(), "@_kind": "FEEL", expression: undefined!, // SPEC DISCREPANCY: Starting without an expression gives users the ability to select the expression type. formalParameter: [], @@ -431,7 +431,9 @@ export function drgElementToBoxedExpression( expressionHolder?.variable?.["@_name"] ?? expressionHolder.expression["@_label"] ?? expressionHolder?.["@_name"], - "@_typeRef": expressionHolder?.variable?.["@_typeRef"] ?? expressionHolder.expression["@_typeRef"], + "@_typeRef": expressionHolder?.variable + ? expressionHolder?.variable["@_typeRef"] + : expressionHolder.expression["@_typeRef"], } : undefined; } else { @@ -442,7 +444,7 @@ export function drgElementToBoxedExpression( } function determineInputsForDecision( - decision: DMN15__tDecision, + decision: Normalized, allTopLevelDataTypesByFeelName: DataTypeIndex, nodesById: Map> ) { @@ -486,7 +488,7 @@ function flattenItemComponents({ itemDefinition, acc, }: { - itemDefinition: DMN15__tItemDefinition; + itemDefinition: Normalized; acc: string; }): { name: string; typeRef: string | undefined }[] { if (!isStruct(itemDefinition)) { diff --git a/packages/dmn-editor/src/boxedExpressions/boxedExpressionIndex.ts b/packages/dmn-editor/src/boxedExpressions/boxedExpressionIndex.ts index ec3d46742d5..6e8618d40e8 100644 --- a/packages/dmn-editor/src/boxedExpressions/boxedExpressionIndex.ts +++ b/packages/dmn-editor/src/boxedExpressions/boxedExpressionIndex.ts @@ -31,6 +31,7 @@ import { DMN15__tRelation, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { AllExpressionTypes, AllExpressions, AllExpressionsWithoutTypes } from "../dataTypes/DataTypeSpec"; +import { Normalized } from "../normalization/normalize"; interface PathType { type: AllExpressionTypes; @@ -411,134 +412,134 @@ export function getDmnObjectByPath( if (path.type === "conditional") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tConditional; + return expressionToEdit as Normalized; } if (path.row === "if") { - return (expressionToEdit as DMN15__tConditional).if.expression; + return (expressionToEdit as Normalized).if.expression; } if (path.row === "else") { - return (expressionToEdit as DMN15__tConditional).else.expression; + return (expressionToEdit as Normalized).else.expression; } - return (expressionToEdit as DMN15__tConditional).then.expression; + return (expressionToEdit as Normalized).then.expression; } if (path.type === "context") { // root if (path.row === undefined || path.column === undefined) { - return expressionToEdit as DMN15__tContext; + return expressionToEdit as Normalized; } if (path.column === "expression") { - return (expressionToEdit as DMN15__tContext).contextEntry?.[path.row].expression; + return (expressionToEdit as Normalized).contextEntry?.[path.row].expression; } - return (expressionToEdit as DMN15__tContext).contextEntry?.[path.row].variable; + return (expressionToEdit as Normalized).contextEntry?.[path.row].variable; } if (path.type === "decisionTable") { // root if (path.row === undefined || path.column === undefined) { - return expressionToEdit as DMN15__tDecisionTable; + return expressionToEdit as Normalized; } if (path.header === "input") { if (path.row < 0) { - return (expressionToEdit as DMN15__tDecisionTable).input?.[path.column]; + return (expressionToEdit as Normalized).input?.[path.column]; } - return (expressionToEdit as DMN15__tDecisionTable).rule?.[path.row].inputEntry?.[path.column]; + return (expressionToEdit as Normalized).rule?.[path.row].inputEntry?.[path.column]; } if (path.row < 0) { - return (expressionToEdit as DMN15__tDecisionTable).output?.[path.column]; + return (expressionToEdit as Normalized).output?.[path.column]; } - return (expressionToEdit as DMN15__tDecisionTable).rule?.[path.row].outputEntry?.[path.column]; + return (expressionToEdit as Normalized).rule?.[path.row].outputEntry?.[path.column]; } if (path.type === "every") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tQuantified; + return expressionToEdit as Normalized; } if (path.row === "variable") { - return expressionToEdit as DMN15__tQuantified; + return expressionToEdit as Normalized; } if (path.row === "in") { - return (expressionToEdit as DMN15__tQuantified).in.expression; + return (expressionToEdit as Normalized).in.expression; } - return (expressionToEdit as DMN15__tQuantified).satisfies.expression; + return (expressionToEdit as Normalized).satisfies.expression; } if (path.type === "filter") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tFilter; + return expressionToEdit as Normalized; } if (path.row === "in") { - return (expressionToEdit as DMN15__tFilter).in.expression; + return (expressionToEdit as Normalized).in.expression; } - return (expressionToEdit as DMN15__tFilter).match.expression; + return (expressionToEdit as Normalized).match.expression; } if (path.type === "for") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tFor; + return expressionToEdit as Normalized; } if (path.row === "variable") { - return expressionToEdit as DMN15__tFor; + return expressionToEdit as Normalized; } if (path.row === "in") { - return (expressionToEdit as DMN15__tFor).in.expression; + return (expressionToEdit as Normalized).in.expression; } - return (expressionToEdit as DMN15__tFor).return.expression; + return (expressionToEdit as Normalized).return.expression; } if (path.type === "functionDefinition") { // root if (!path.parameterIndex) { - return expressionToEdit as DMN15__tFunctionDefinition; + return expressionToEdit as Normalized; } if (path.parameterIndex < 0) { - return (expressionToEdit as DMN15__tFunctionDefinition).expression; + return (expressionToEdit as Normalized).expression; } - return expressionToEdit as DMN15__tFunctionDefinition; + return expressionToEdit as Normalized; } if (path.type === "invocation") { // root if (path.row === undefined || path.column === undefined) { - return expressionToEdit as DMN15__tInvocation; + return expressionToEdit as Normalized; } if (path.column === "parameter") { - return (expressionToEdit as DMN15__tInvocation).binding?.[path.row].parameter; + return (expressionToEdit as Normalized).binding?.[path.row].parameter; } if (path.column === "expression" && path.row >= 0) { - return (expressionToEdit as DMN15__tInvocation).binding?.[path.row].expression; + return (expressionToEdit as Normalized).binding?.[path.row].expression; } // function call - return (expressionToEdit as DMN15__tInvocation).expression; + return (expressionToEdit as Normalized).expression; } if (path.type === "list") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tList; + return expressionToEdit as Normalized; } - return (expressionToEdit as DMN15__tList).expression?.[path.row]; + return (expressionToEdit as Normalized).expression?.[path.row]; } if (path.type === "literalExpression") { - return expressionToEdit as DMN15__tLiteralExpression; + return expressionToEdit as Normalized; } if (path.type === "relation") { // root if (path.row === undefined || path.column === undefined) { - return expressionToEdit as DMN15__tRelation; + return expressionToEdit as Normalized; } if (path.row < 0) { - return (expressionToEdit as DMN15__tRelation).column?.[path.column]; + return (expressionToEdit as Normalized).column?.[path.column]; } - return (expressionToEdit as DMN15__tRelation).row?.[path.row].expression?.[path.column]; + return (expressionToEdit as Normalized).row?.[path.row].expression?.[path.column]; } if (path.type === "some") { // root if (path.row === undefined) { - return expressionToEdit as DMN15__tQuantified; + return expressionToEdit as Normalized; } if (path.row === "variable") { - return expressionToEdit as DMN15__tQuantified; + return expressionToEdit as Normalized; } if (path.row === "in") { - return (expressionToEdit as DMN15__tQuantified).in.expression; + return (expressionToEdit as Normalized).in.expression; } - return (expressionToEdit as DMN15__tQuantified).satisfies.expression; + return (expressionToEdit as Normalized).satisfies.expression; } }, expressionRoot); } diff --git a/packages/dmn-editor/src/boxedExpressions/getDefaultBoxedExpression.tsx b/packages/dmn-editor/src/boxedExpressions/getDefaultBoxedExpression.tsx index 3410cf7d2e7..e47a3e687d8 100644 --- a/packages/dmn-editor/src/boxedExpressions/getDefaultBoxedExpression.tsx +++ b/packages/dmn-editor/src/boxedExpressions/getDefaultBoxedExpression.tsx @@ -18,39 +18,36 @@ */ import { - DmnBuiltInDataType, + BoxedConditional, + BoxedContext, + BoxedDecisionTable, + BoxedEvery, BoxedExpression, - generateUuid, - BoxedLiteral, + BoxedFilter, + BoxedFor, BoxedFunction, - BoxedContext, - BoxedList, BoxedInvocation, + BoxedList, + BoxedLiteral, BoxedRelation, - BoxedDecisionTable, - BoxedConditional, - BoxedFor, BoxedSome, - BoxedEvery, - BoxedFilter, + DmnBuiltInDataType, + generateUuid, } from "@kie-tools/boxed-expression-component/dist/api"; import { - LITERAL_EXPRESSION_MIN_WIDTH, + BEE_TABLE_ROW_INDEX_COLUMN_WIDTH, CONTEXT_ENTRY_VARIABLE_MIN_WIDTH, + DECISION_TABLE_ANNOTATION_DEFAULT_WIDTH, DECISION_TABLE_INPUT_DEFAULT_WIDTH, DECISION_TABLE_OUTPUT_DEFAULT_WIDTH, - DECISION_TABLE_ANNOTATION_DEFAULT_WIDTH, + LITERAL_EXPRESSION_MIN_WIDTH, RELATION_EXPRESSION_COLUMN_DEFAULT_WIDTH, - BEE_TABLE_ROW_INDEX_COLUMN_WIDTH, } from "@kie-tools/boxed-expression-component/dist/resizing/WidthConstants"; import { DECISION_TABLE_INPUT_DEFAULT_VALUE, DECISION_TABLE_OUTPUT_DEFAULT_VALUE, } from "@kie-tools/boxed-expression-component/dist/expressions/DecisionTableExpression/DecisionTableExpression"; -import { - INVOCATION_EXPRESSION_DEFAULT_PARAMETER_NAME, - INVOCATION_EXPRESSION_DEFAULT_PARAMETER_DATA_TYPE, -} from "@kie-tools/boxed-expression-component/dist/expressions/InvocationExpression/InvocationExpression"; +import { INVOCATION_EXPRESSION_DEFAULT_PARAMETER_NAME } from "@kie-tools/boxed-expression-component/dist/expressions/InvocationExpression/InvocationExpression"; import { RELATION_EXPRESSION_DEFAULT_VALUE } from "@kie-tools/boxed-expression-component/dist/expressions/RelationExpression/RelationExpression"; import { DataTypeIndex } from "../dataTypes/DataTypes"; import { isStruct } from "../dataTypes/DataTypeSpec"; @@ -58,6 +55,7 @@ import { DMN15__tContextEntry, DMN15__tOutputClause, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; export function getDefaultBoxedExpression({ logicType, @@ -67,17 +65,17 @@ export function getDefaultBoxedExpression({ getInputs, getDefaultColumnWidth, }: { - logicType: BoxedExpression["__$$element"] | undefined; - typeRef: string; + logicType: Normalized["__$$element"] | undefined; + typeRef: string | undefined; allTopLevelDataTypesByFeelName: DataTypeIndex; getInputs?: () => { name: string; typeRef: string | undefined }[] | undefined; getDefaultColumnWidth?: (args: { name: string; typeRef: string | undefined }) => number | undefined; widthsById: Map; -}): BoxedExpression { - const dataType = allTopLevelDataTypesByFeelName.get(typeRef); +}): Normalized { + const dataType = allTopLevelDataTypesByFeelName.get(typeRef ?? DmnBuiltInDataType.Undefined); if (logicType === "literalExpression") { - const literalExpression: BoxedLiteral = { + const literalExpression: Normalized = { __$$element: "literalExpression", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -88,7 +86,7 @@ export function getDefaultBoxedExpression({ } // else if (logicType === "functionDefinition") { - const functionExpression: BoxedFunction = { + const functionExpression: Normalized = { __$$element: "functionDefinition", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -101,7 +99,7 @@ export function getDefaultBoxedExpression({ else if (logicType === "context") { let maxWidthBasedOnEntryNames = CONTEXT_ENTRY_VARIABLE_MIN_WIDTH; - let contextEntries: DMN15__tContextEntry[]; + let contextEntries: Normalized[]; if (!dataType || !isStruct(dataType.itemDefinition)) { contextEntries = [ { @@ -109,7 +107,6 @@ export function getDefaultBoxedExpression({ variable: { "@_id": generateUuid(), "@_name": "ContextEntry-1", - "@_typeRef": DmnBuiltInDataType.Undefined, }, expression: undefined!, // SPEC DISCREPANCY: Starting without an expression gives users the ability to select the expression type. }, @@ -117,7 +114,7 @@ export function getDefaultBoxedExpression({ } else { contextEntries = (dataType.itemDefinition.itemComponent ?? []).map((ic) => { const name = ic["@_name"]; - const typeRef = isStruct(ic) ? DmnBuiltInDataType.Any : ic.typeRef?.__$$text ?? DmnBuiltInDataType.Undefined; + const typeRef = isStruct(ic) ? DmnBuiltInDataType.Any : ic.typeRef?.__$$text; maxWidthBasedOnEntryNames = Math.max( maxWidthBasedOnEntryNames, getDefaultColumnWidth?.({ name, typeRef }) ?? CONTEXT_ENTRY_VARIABLE_MIN_WIDTH @@ -140,7 +137,7 @@ export function getDefaultBoxedExpression({ expression: undefined!, // SPEC DISCREPANCY: Starting without an expression gives users the ability to select the expression type. }); - const contextExpression: BoxedContext = { + const contextExpression: Normalized = { __$$element: "context", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -150,7 +147,7 @@ export function getDefaultBoxedExpression({ widthsById.set(contextExpression["@_id"]!, [maxWidthBasedOnEntryNames]); return contextExpression; } else if (logicType === "list") { - const listExpression: BoxedList = { + const listExpression: Normalized = { __$$element: "list", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -160,7 +157,7 @@ export function getDefaultBoxedExpression({ }; return listExpression; } else if (logicType === "invocation") { - const invocationExpression: BoxedInvocation = { + const invocationExpression: Normalized = { __$$element: "invocation", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -169,7 +166,7 @@ export function getDefaultBoxedExpression({ parameter: { "@_id": generateUuid(), "@_name": INVOCATION_EXPRESSION_DEFAULT_PARAMETER_NAME, - "@_typeRef": INVOCATION_EXPRESSION_DEFAULT_PARAMETER_DATA_TYPE, + "@_typeRef": undefined, }, expression: undefined!, // SPEC DISCREPANCY: Starting without an expression gives users the ability to select the expression type. }, @@ -185,7 +182,7 @@ export function getDefaultBoxedExpression({ } else if (logicType === "relation") { const isSimple = !dataType || !isStruct(dataType.itemDefinition); - const relationExpression: BoxedRelation = { + const relationExpression: Normalized = { __$$element: "relation", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -206,14 +203,12 @@ export function getDefaultBoxedExpression({ { "@_id": generateUuid(), "@_name": dataType?.itemDefinition["@_name"] ?? "column-1", - "@_typeRef": dataType?.feelName ?? DmnBuiltInDataType.Undefined, + "@_typeRef": dataType?.feelName, }, ] : (dataType.itemDefinition.itemComponent ?? []).map((ic) => { const name = ic["@_name"]; - const typeRef = isStruct(ic) - ? DmnBuiltInDataType.Any - : ic.typeRef?.__$$text ?? DmnBuiltInDataType.Undefined; + const typeRef = isStruct(ic) ? DmnBuiltInDataType.Any : ic.typeRef?.__$$text; return { "@_id": generateUuid(), "@_name": name, @@ -236,11 +231,11 @@ export function getDefaultBoxedExpression({ } else if (logicType === "decisionTable") { const singleOutputColumn = { name: "Output-1", - typeRef: dataType?.feelName ?? DmnBuiltInDataType.Undefined, + typeRef: dataType?.feelName, }; const singleInputColumn = { name: "Input-1", - typeRef: DmnBuiltInDataType.Undefined, + typeRef: undefined, }; const input = getInputs?.()?.map((input) => ({ @@ -248,7 +243,7 @@ export function getDefaultBoxedExpression({ inputExpression: { "@_id": generateUuid(), text: { __$$text: input.name }, - "@_typeRef": input.typeRef ?? DmnBuiltInDataType.Undefined, + "@_typeRef": input.typeRef, }, })) ?? [ { @@ -256,12 +251,12 @@ export function getDefaultBoxedExpression({ inputExpression: { "@_id": generateUuid(), text: { __$$text: singleInputColumn.name }, - "@_typeRef": singleInputColumn.typeRef ?? DmnBuiltInDataType.Undefined, + "@_typeRef": singleInputColumn.typeRef, }, }, ]; - const output: DMN15__tOutputClause[] = + const output: Normalized[] = !dataType || !isStruct(dataType.itemDefinition) ? [ { @@ -273,10 +268,10 @@ export function getDefaultBoxedExpression({ : (dataType.itemDefinition.itemComponent ?? []).map((ic) => ({ "@_id": generateUuid(), "@_name": ic["@_name"], - "@_typeRef": isStruct(ic) ? DmnBuiltInDataType.Any : ic.typeRef?.__$$text ?? DmnBuiltInDataType.Undefined, + "@_typeRef": isStruct(ic) ? DmnBuiltInDataType.Any : ic.typeRef?.__$$text, })); - const decisionTableExpression: BoxedDecisionTable = { + const decisionTableExpression: Normalized = { __$$element: "decisionTable", "@_id": generateUuid(), "@_typeRef": typeRef, @@ -325,7 +320,8 @@ export function getDefaultBoxedExpression({ return decisionTableExpression; } else if (logicType === "conditional") { - const conditionalExpression: BoxedConditional = { + const conditionalExpression: Normalized = { + "@_id": generateUuid(), __$$element: "conditional", if: { "@_id": generateUuid(), @@ -343,7 +339,8 @@ export function getDefaultBoxedExpression({ return conditionalExpression; } else if (logicType === "for") { - const forExpression: BoxedFor = { + const forExpression: Normalized = { + "@_id": generateUuid(), __$$element: "for", return: { "@_id": generateUuid(), @@ -356,7 +353,8 @@ export function getDefaultBoxedExpression({ }; return forExpression; } else if (logicType == "some") { - const someExpression: BoxedSome = { + const someExpression: Normalized = { + "@_id": generateUuid(), __$$element: "some", satisfies: { "@_id": generateUuid(), @@ -369,7 +367,8 @@ export function getDefaultBoxedExpression({ }; return someExpression; } else if (logicType === "every") { - const everyExpression: BoxedEvery = { + const everyExpression: Normalized = { + "@_id": generateUuid(), __$$element: "every", satisfies: { "@_id": generateUuid(), @@ -382,7 +381,8 @@ export function getDefaultBoxedExpression({ }; return everyExpression; } else if (logicType === "filter") { - const filterExpression: BoxedFilter = { + const filterExpression: Normalized = { + "@_id": generateUuid(), __$$element: "filter", match: { "@_id": generateUuid(), diff --git a/packages/dmn-editor/src/clipboard/Clipboard.ts b/packages/dmn-editor/src/clipboard/Clipboard.ts index b1b1e7e87d7..8704f2c5999 100644 --- a/packages/dmn-editor/src/clipboard/Clipboard.ts +++ b/packages/dmn-editor/src/clipboard/Clipboard.ts @@ -37,6 +37,7 @@ import { KIE__tComponentWidths } from "@kie-tools/dmn-marshaller/dist/schemas/ki import { DataType } from "../dataTypes/DataTypes"; import { parseXmlHref } from "../xml/xmlHrefs"; import { getNewDmnIdRandomizer } from "../idRandomizer/dmnIdRandomizer"; +import { Normalized } from "../normalization/normalize"; export const DMN_EDITOR_DIAGRAM_CLIPBOARD_MIME_TYPE = "application/json+kie-dmn-editor--diagram" as const; export const DMN_EDITOR_BOXED_EXPRESSION_CLIPBOARD_MIME_TYPE = @@ -47,17 +48,17 @@ export type DmnEditorDataTypesClipboard = { mimeType: typeof DMN_EDITOR_DATA_TYPES_CLIPBOARD_MIME_TYPE; namespaceWhereClipboardWasCreatedFrom: string; namespace: string; - itemDefinitions: DMN15__tItemDefinition[]; + itemDefinitions: Normalized[]; }; export type DmnEditorDiagramClipboard = { mimeType: typeof DMN_EDITOR_DIAGRAM_CLIPBOARD_MIME_TYPE; namespaceWhereClipboardWasCreatedFrom: string; - drgElements: NonNullable>[]; - artifacts: NonNullable>[]; + drgElements: NonNullable["drgElement"]>>[]; + artifacts: NonNullable["artifact"]>>[]; widths: Namespaced[]; - shapes: DMNDI15__DMNShape[]; - edges: DMNDI15__DMNEdge[]; + shapes: Normalized[]; + edges: Normalized[]; }; export function buildClipboardFromDiagram(rfState: RF.ReactFlowState, dmnEditorState: State) { @@ -89,12 +90,12 @@ export function buildClipboardFromDiagram(rfState: RF.ReactFlowState, dmnEditorS return; } - const dmnObject = JSON.parse(JSON.stringify(node.data.dmnObject)) as DMN15__tDecision; // Casting to `DMN15__tDecision` because it has all requirement types. + const dmnObject = JSON.parse(JSON.stringify(node.data.dmnObject)) as Normalized; // Casting to `DMN15__tDecision` because it has all requirement types. // This is going to get repopulated when this data is pasted somewhere. if (node.data.dmnObject?.__$$element === "decisionService") { - (dmnObject as DMN15__tDecisionService).inputData = []; - (dmnObject as DMN15__tDecisionService).inputDecision = []; + (dmnObject as Normalized).inputData = []; + (dmnObject as Normalized).inputDecision = []; } if (dmnObject.authorityRequirement) { @@ -188,9 +189,9 @@ export function buildClipboardFromDiagram(rfState: RF.ReactFlowState, dmnEditorS .getOriginalIds(); clipboard.widths = ( - dmnEditorState.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.[dmnEditorState.diagram.drdIndex][ - "di:extension" - ]?.["kie:ComponentsWidthsExtension"]?.["kie:ComponentWidths"] ?? [] + dmnEditorState.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.[ + dmnEditorState.computed(dmnEditorState).getDrdIndex() + ]["di:extension"]?.["kie:ComponentsWidthsExtension"]?.["kie:ComponentWidths"] ?? [] ).filter((w: KIE__tComponentWidths) => idsOnDrgElementTrees.has(w["@_dmnElementRef"]!)); const artifacts = dmnEditorState.dmn.model.definitions.artifact ?? []; diff --git a/packages/dmn-editor/src/commands/CommandsContextProvider.tsx b/packages/dmn-editor/src/commands/CommandsContextProvider.tsx new file mode 100644 index 00000000000..1a8cdaf7f82 --- /dev/null +++ b/packages/dmn-editor/src/commands/CommandsContextProvider.tsx @@ -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. + */ + +import * as React from "react"; +import { useContext, useRef } from "react"; + +export interface Commands { + hideFromDrd: () => void; + toggleHierarchyHighlight: () => void; + togglePropertiesPanel: () => void; + createGroup: () => void; + selectAll: () => void; + paste: () => void; + copy: () => void; + cut: () => void; + cancelAction: () => void; + focusOnSelection: () => void; + resetPosition: () => void; +} + +const CommandsContext = React.createContext<{ + commandsRef: React.MutableRefObject; +}>({} as any); + +export function useCommands() { + return useContext(CommandsContext); +} + +export function CommandsContextProvider(props: React.PropsWithChildren<{}>) { + const commandsRef = useRef({ + hideFromDrd: () => { + throw new Error("DMN EDITOR: hideFromDrd command not implemented."); + }, + toggleHierarchyHighlight: () => { + throw new Error("DMN EDITOR: toggleHierarchyHighlight command not implemented."); + }, + togglePropertiesPanel: () => { + throw new Error("DMN EDITOR: togglePropertiesPanel command not implemented."); + }, + createGroup: () => { + throw new Error("DMN EDITOR: createGroup command not implemented."); + }, + selectAll: () => { + throw new Error("DMN EDITOR: selectAll command not implemented."); + }, + paste: () => { + throw new Error("DMN EDITOR: paste command not implemented."); + }, + copy: () => { + throw new Error("DMN EDITOR: copy command not implemented."); + }, + cut: () => { + throw new Error("DMN EDITOR: cut command not implemented."); + }, + cancelAction: () => { + throw new Error("DMN EDITOR: cancelAction command not implemented."); + }, + focusOnSelection: () => { + throw new Error("DMN EDITOR: focusOnSelection command not implemented."); + }, + resetPosition: () => { + throw new Error("DMN EDITOR: resetPosition command not implemented."); + }, + }); + + return {props.children}; +} diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/Constraint.css b/packages/dmn-editor/src/dataTypes/ConstraintComponents/Constraint.css index 475a9c948dd..0a544421bf9 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/Constraint.css +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/Constraint.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--constraint-input { outline: none; border-color: transparent; diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.css b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.css index 03306d6b02e..3b6074d862d 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.css +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--constraint-date button::after { outline: none; border-color: transparent; diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.tsx b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.tsx index e4cb2e573e9..368f8f9ceda 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.tsx +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDate.tsx @@ -22,6 +22,7 @@ import { DatePicker } from "@patternfly/react-core/dist/js/components/DatePicker import "./Constraint.css"; import "./ConstraintDate.css"; import { ConstraintProps } from "./Constraint"; +import moment from "moment"; export function ConstraintDate({ value, onChange, isValid }: ConstraintProps) { return ( @@ -32,7 +33,16 @@ export function ConstraintDate({ value, onChange, isValid }: ConstraintProps) { }`} inputProps={{ className: "kie-dmn-editor--constraint-input" }} value={value} - onChange={(e, value) => onChange(value)} + onChange={(e, value) => { + if (moment(value, "YYYY-MM-DD", true).isValid()) { + onChange(value); + } + }} + onBlur={(e, value) => { + if (moment(value, "YYYY-MM-DD", true).isValid()) { + onChange(value); + } + }} /> ); diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDateTime.css b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDateTime.css index c1b9f733bfa..327c689433c 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDateTime.css +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintDateTime.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--constraint-date-time { display: flex; flex-direction: row; diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.css b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.css index 7090420fc0a..ac7fc3938b1 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.css +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--constraint-time button::after { outline: none; border-color: transparent; diff --git a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.tsx b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.tsx index 346b3eb4a82..dd2a7ea481d 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.tsx +++ b/packages/dmn-editor/src/dataTypes/ConstraintComponents/ConstraintTime.tsx @@ -26,6 +26,8 @@ import { ConstraintProps } from "./Constraint"; import { Select, SelectOption, SelectVariant } from "@patternfly/react-core/dist/js/components/Select"; import { useDmnEditor } from "../../DmnEditorContext"; import { useInViewSelect } from "../../responsiveness/useInViewSelect"; +import { Tooltip } from "@patternfly/react-core/dist/js/components/Tooltip"; +import moment from "moment"; // Time zone list from https://www.timeanddate.com/time/zones/ const UTC_POSITEVE_TIMEZONES = [ @@ -75,11 +77,19 @@ const UTC_NEGATIVE_TIMEZONES = [ "-01:00", ]; +const TIME_REGEXP = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?([+|-](0[0-9]|1[0-9]|2[0-3]):[0-9][0-9])?$/; + export function ConstraintTime({ value, onChange, isValid }: ConstraintProps) { const time = useMemo(() => { + if (value.match(TIME_REGEXP) === null) { + return ""; + } return value.includes("+") ? value.split("+")[0] : value.split("-")[0]; }, [value]); const timezone = useMemo(() => { + if (value.match(TIME_REGEXP) === null) { + return UTC_POSITEVE_TIMEZONES[0]; + } return value.includes("+") ? `+${value.split("+")[1]}` : value.includes("-") @@ -123,36 +133,62 @@ export function ConstraintTime({ value, onChange, isValid }: ConstraintProps) { className={`kie-dmn-editor--constraint-time kie-dmn-editor--constraint-input ${ isValid ? "" : "kie-dmn-editor--constraint-invalid" }`} - inputProps={{ className: "kie-dmn-editor--constraint-input" }} + inputProps={{ + className: "kie-dmn-editor--constraint-input", + onBlur: (e: React.FocusEvent) => { + if (moment(value, "HH:mm", true).isValid() || moment(value, "HH:mm:ss", true).isValid()) { + onChangeTime(value); + } + }, + }} time={time} - onChange={(e, value, hour, minute, seconds, isValid) => { - if (isValid) { + onChange={(e, value) => { + if (moment(value, "HH:mm", true).isValid()) { + onChangeTime(`${value}:00`); + } + if (moment(value, "HH:mm:ss", true).isValid()) { onChangeTime(value); } }} includeSeconds={true} /> - + {time === "" ? ( + + + + ) : ( + + )}
); } diff --git a/packages/dmn-editor/src/dataTypes/Constraints.tsx b/packages/dmn-editor/src/dataTypes/Constraints.tsx index 812bb9c801d..cc871b56179 100644 --- a/packages/dmn-editor/src/dataTypes/Constraints.tsx +++ b/packages/dmn-editor/src/dataTypes/Constraints.tsx @@ -18,7 +18,7 @@ */ import * as React from "react"; -import { useMemo, useCallback } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { ConstraintsExpression } from "./ConstraintsExpression"; import { DMN15__tItemDefinition, @@ -28,9 +28,9 @@ import { DmnBuiltInDataType, generateUuid } from "@kie-tools/boxed-expression-co import { ConstraintsEnum, isEnum } from "./ConstraintsEnum"; import { ConstraintsRange, isRange } from "./ConstraintsRange"; import { KIE__tConstraintType } from "@kie-tools/dmn-marshaller/dist/schemas/kie-1_0/ts-gen/types"; -import { EditItemDefinition } from "./DataTypes"; +import { DataTypeIndex, EditItemDefinition } from "./DataTypes"; import { ToggleGroup, ToggleGroupItem } from "@patternfly/react-core/dist/js/components/ToggleGroup"; -import { constrainableBuiltInFeelTypes } from "./DataTypeSpec"; +import { constrainableBuiltInFeelTypes, isCollection, isStruct } from "./DataTypeSpec"; import moment from "moment"; import { TextInput } from "@patternfly/react-core/dist/js/components/TextInput"; import { ConstraintDate } from "./ConstraintComponents/ConstraintDate"; @@ -46,6 +46,11 @@ import { } from "./ConstraintComponents/ConstraintYearsMonthsDuration"; import { invalidInlineFeelNameStyle } from "../feel/InlineFeelNameInput"; import { ConstraintProps } from "./ConstraintComponents/Constraint"; +import { useDmnEditorStore } from "../store/StoreContext"; +import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; +import { UniqueNameIndex } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; +import { builtInFeelTypeNames } from "./BuiltInFeelTypes"; +import { Normalized } from "../normalization/normalize"; export type TypeHelper = { check: (value: string) => boolean; @@ -56,6 +61,7 @@ export type TypeHelper = { }; export interface ConstraintComponentProps { + id: string; isReadonly: boolean; value?: string; expressionValue?: string; @@ -73,31 +79,70 @@ enum ConstraintsType { NONE = "None", } -export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => { - return { +// Recurse the `itemDefinition` until find `typeRef` attribute +// that is part of the built in FEEL types. +// If the found `itemDefinition` is a collection, it will have a early stop. +export function recursivelyGetRootItemDefinition( + itemDefinition: Normalized, + allDataTypesById: DataTypeIndex, + allTopLevelItemDefinitionUniqueNames: UniqueNameIndex +): Normalized { + const typeRef: DmnBuiltInDataType = itemDefinition.typeRef?.__$$text as DmnBuiltInDataType; + + if (builtInFeelTypeNames.has(typeRef) === false) { + const parentDataType = allDataTypesById.get(allTopLevelItemDefinitionUniqueNames.get(typeRef) ?? ""); + if (parentDataType !== undefined && isCollection(parentDataType.itemDefinition)) { + // Parent `itemDefinition` is a collection. Early stop. + return parentDataType.itemDefinition; + } else if (parentDataType !== undefined) { + return recursivelyGetRootItemDefinition( + parentDataType.itemDefinition, + allDataTypesById, + allTopLevelItemDefinitionUniqueNames + ); + } + // Something wrong. Caller `itemDefinition` isn't a built-in FEEL type and doesn't have parent. + return itemDefinition; + } + // Caller `itemDefinition` is a built-in FEEL type + return itemDefinition; +} + +export const constraintTypeHelper = ( + itemDefinition: Normalized, + allDataTypesById?: DataTypeIndex, + allTopLevelItemDefinitionUniqueNames?: UniqueNameIndex +): TypeHelper => { + const typeRef = + (allDataTypesById !== undefined && allTopLevelItemDefinitionUniqueNames !== undefined + ? recursivelyGetRootItemDefinition(itemDefinition, allDataTypesById, allTopLevelItemDefinitionUniqueNames).typeRef + ?.__$$text + : itemDefinition.typeRef?.__$$text) ?? DmnBuiltInDataType.Undefined; + + const typeHelper = { // check if the value has the correct type - check: (value: string) => { - const recoveredValue = constraintTypeHelper(typeRef).recover(value); - switch (typeRef) { + check: (value: string, type?: DmnBuiltInDataType) => { + const recoveredValue = typeHelper.recover(value); + switch (type ?? typeRef) { case DmnBuiltInDataType.Any: return true; case DmnBuiltInDataType.String: if (recoveredValue === "") { return true; } - if (constraintTypeHelper(DmnBuiltInDataType.Date).check(value)) { + if (typeHelper.check(value, DmnBuiltInDataType.Date)) { return false; } - if (constraintTypeHelper(DmnBuiltInDataType.DateTime).check(value)) { + if (typeHelper.check(value, DmnBuiltInDataType.DateTime)) { return false; } - if (constraintTypeHelper(DmnBuiltInDataType.DateTimeDuration).check(value)) { + if (typeHelper.check(value, DmnBuiltInDataType.DateTimeDuration)) { return false; } - if (constraintTypeHelper(DmnBuiltInDataType.Time).check(value)) { + if (typeHelper.check(value, DmnBuiltInDataType.Time)) { return false; } - if (constraintTypeHelper(DmnBuiltInDataType.YearsMonthsDuration).check(value)) { + if (typeHelper.check(value, DmnBuiltInDataType.YearsMonthsDuration)) { return false; } return typeof recoveredValue === "string"; @@ -106,6 +151,7 @@ export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => case DmnBuiltInDataType.DateTime: return ( moment(recoveredValue, "YYYY-MM-DDTHH:mm:ssZZ", true).isValid() || + moment(recoveredValue, "YYYY-MM-DDTHH:mmZZ", true).isValid() || moment(recoveredValue, "YYYY-MM-DD", true).isValid() || value === "" || recoveredValue === "" @@ -115,7 +161,12 @@ export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => case DmnBuiltInDataType.Number: return !isNaN(parseFloat(recoveredValue ?? "")) || value === "" || recoveredValue === ""; case DmnBuiltInDataType.Time: - return moment(recoveredValue, "HH:mm:ssZZ", true).isValid() || value === "" || recoveredValue === ""; + return ( + moment(recoveredValue, "HH:mmZZ", true).isValid() || + moment(recoveredValue, "HH:mm:ssZZ", true).isValid() || + value === "" || + recoveredValue === "" + ); case DmnBuiltInDataType.YearsMonthsDuration: return REGEX_YEARS_MONTH_DURATION.test(recoveredValue ?? "") || value === "" || recoveredValue === ""; default: @@ -125,7 +176,7 @@ export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => // parse the value to the type // useful for comparisons parse: (value: string) => { - const recoveredValue = constraintTypeHelper(typeRef).recover(value); + const recoveredValue = typeHelper.recover(value); switch (typeRef) { case DmnBuiltInDataType.Number: return parseFloat(recoveredValue ?? ""); @@ -163,7 +214,7 @@ export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => } }, // recover the value before use it - recover: (value?: string) => { + recover: (value: string | undefined) => { if (value === undefined) { return undefined; } @@ -248,41 +299,39 @@ export const constraintTypeHelper = (typeRef: DmnBuiltInDataType): TypeHelper => } }, }; + return typeHelper; }; export function useConstraint({ constraint, itemDefinition, - isCollectionConstraintEnable, + isCollectionConstraintEnabled, + constraintTypeHelper, + enabledConstraints, }: { - constraint: DMN15__tUnaryTests | undefined; - itemDefinition: DMN15__tItemDefinition; - isCollectionConstraintEnable: boolean; + constraint: Normalized | undefined; + itemDefinition: Normalized; + isCollectionConstraintEnabled: boolean; + constraintTypeHelper: TypeHelper; + enabledConstraints: KIE__tConstraintType[] | undefined; }) { - const constraintValue = useMemo(() => constraint?.text.__$$text, [constraint?.text.__$$text]); - const kieConstraintType = useMemo(() => constraint?.["@_kie:constraintType"], [constraint]); - const isCollection = useMemo(() => itemDefinition["@_isCollection"] ?? false, [itemDefinition]); - const itemDefinitionId = useMemo(() => itemDefinition["@_id"], [itemDefinition]); - - const typeRef: DmnBuiltInDataType = useMemo( - () => (itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined, - [itemDefinition?.typeRef?.__$$text] - ); + const constraintValue = constraint?.text.__$$text; + const kieConstraintType = constraint?.["@_kie:constraintType"]; const isConstraintEnum = useMemo( () => - isCollection === true && isCollectionConstraintEnable === true // collection doesn't support enumeration constraint + isCollection(itemDefinition) === true && isCollectionConstraintEnabled === true // collection doesn't support enumeration constraint ? undefined - : isEnum(constraintValue, constraintTypeHelper(typeRef).check), - [constraintValue, isCollectionConstraintEnable, isCollection, typeRef] + : isEnum(constraintValue, constraintTypeHelper.check), + [constraintTypeHelper.check, constraintValue, isCollectionConstraintEnabled, itemDefinition] ); const isConstraintRange = useMemo( () => - isCollection === true && isCollectionConstraintEnable === true // collection doesn't support range constraint + isCollection(itemDefinition) === true && isCollectionConstraintEnabled === true // collection doesn't support range constraint ? undefined - : isRange(constraintValue, constraintTypeHelper(typeRef).check), - [constraintValue, isCollectionConstraintEnable, isCollection, typeRef] + : isRange(constraintValue, constraintTypeHelper.check), + [constraintTypeHelper.check, constraintValue, isCollectionConstraintEnabled, itemDefinition] ); const enumToKieConstraintType: (selection: ConstraintsType) => KIE__tConstraintType | undefined = useCallback( @@ -303,21 +352,20 @@ export function useConstraint({ ); const isConstraintEnabled = useMemo(() => { - const enabledConstraints = constrainableBuiltInFeelTypes.get(typeRef); return { enumeration: - !(isCollection === true && isCollectionConstraintEnable === true) && + !(isCollection(itemDefinition) === true && isCollectionConstraintEnabled === true) && (enabledConstraints ?? []).includes(enumToKieConstraintType(ConstraintsType.ENUMERATION)!), range: - !(isCollection === true && isCollectionConstraintEnable === true) && + !(isCollection(itemDefinition) === true && isCollectionConstraintEnabled === true) && (enabledConstraints ?? []).includes(enumToKieConstraintType(ConstraintsType.RANGE)!), expression: - (isCollection === true && isCollectionConstraintEnable === true) || + (isCollection(itemDefinition) === true && isCollectionConstraintEnabled === true) || (enabledConstraints ?? []).includes(enumToKieConstraintType(ConstraintsType.EXPRESSION)!), }; - }, [typeRef, isCollection, isCollectionConstraintEnable, enumToKieConstraintType]); + }, [enabledConstraints, enumToKieConstraintType, isCollectionConstraintEnabled, itemDefinition]); - const selectedConstraint = useMemo(() => { + const selectedKieConstraintType = useMemo(() => { if (isConstraintEnabled.enumeration && kieConstraintType === "enumeration") { return ConstraintsType.ENUMERATION; } @@ -347,27 +395,14 @@ export function useConstraint({ kieConstraintType, ]); - return useMemo(() => { - return { - constraintValue, - typeRef, - isConstraintEnum, - isConstraintRange, - isConstraintEnabled, - itemDefinitionId, - selectedConstraint, - enumToKieConstraintType, - }; - }, [ + return { constraintValue, - isConstraintEnabled, isConstraintEnum, isConstraintRange, - itemDefinitionId, - selectedConstraint, - typeRef, + isConstraintEnabled, + selectedKieConstraintType, enumToKieConstraintType, - ]); + }; } export function ConstraintsFromAllowedValuesAttribute({ @@ -377,106 +412,133 @@ export function ConstraintsFromAllowedValuesAttribute({ renderOnPropertiesPanel, }: { isReadonly: boolean; - itemDefinition: DMN15__tItemDefinition; + itemDefinition: Normalized; editItemDefinition: EditItemDefinition; renderOnPropertiesPanel?: boolean; isEnumDisabled?: boolean; isRangeDisabled?: boolean; }) { + const { externalModelsByNamespace } = useExternalModels(); + const allDataTypesById = useDmnEditorStore( + (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allDataTypesById + ); + const allTopLevelItemDefinitionUniqueNames = useDmnEditorStore( + (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allTopLevelItemDefinitionUniqueNames + ); + const allowedValues = useMemo(() => itemDefinition?.allowedValues, [itemDefinition?.allowedValues]); + const itemDefinitionId = itemDefinition["@_id"]!; + const typeRef = itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType; + const typeRefConstraintTypeHelper = useMemo( + () => constraintTypeHelper(itemDefinition, allDataTypesById, allTopLevelItemDefinitionUniqueNames), + [allDataTypesById, allTopLevelItemDefinitionUniqueNames, itemDefinition] + ); + + const rootItemDefinition = useMemo( + () => recursivelyGetRootItemDefinition(itemDefinition, allDataTypesById, allTopLevelItemDefinitionUniqueNames), + [allDataTypesById, allTopLevelItemDefinitionUniqueNames, itemDefinition] + ); + + const enabledConstraints = useMemo( + () => + isStruct(rootItemDefinition) + ? (["expression"] as KIE__tConstraintType[]) + : constrainableBuiltInFeelTypes.get( + (rootItemDefinition.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined + ), + [rootItemDefinition] + ); + + // Collection constraint on the `allowedValues` must be enabled on cases where `rootItemDefinition` is a collection + const isCollectionConstraintEnable = useMemo(() => { + if (itemDefinitionId !== rootItemDefinition["@_id"]) { + return rootItemDefinition["@_isCollection"] ?? false; + } + return false; + }, [itemDefinitionId, rootItemDefinition]); const { constraintValue, - typeRef, isConstraintEnum, isConstraintRange, isConstraintEnabled, - itemDefinitionId, - selectedConstraint, + selectedKieConstraintType, enumToKieConstraintType, } = useConstraint({ constraint: allowedValues, itemDefinition, - isCollectionConstraintEnable: false, // allowedValues doesn't support constraint to the collection itself + isCollectionConstraintEnabled: isCollectionConstraintEnable, + constraintTypeHelper: typeRefConstraintTypeHelper, + enabledConstraints, }); const onConstraintChange = useCallback( - (value?: string) => { - editItemDefinition(itemDefinitionId!, (itemDefinition) => { - itemDefinition.allowedValues ??= { text: { __$$text: "" } }; - itemDefinition.allowedValues.text.__$$text = value ?? ""; - itemDefinition.allowedValues["@_id"] = itemDefinition.allowedValues?.["@_id"] ?? generateUuid(); - return; + (value: string | undefined, selectedConstraint: ConstraintsType) => { + editItemDefinition(itemDefinitionId, (itemDefinition) => { + if (value === "" || value === undefined) { + itemDefinition.allowedValues = undefined; + } else { + itemDefinition.allowedValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; + itemDefinition.allowedValues.text.__$$text = value; + itemDefinition.allowedValues["@_id"] = itemDefinition.allowedValues?.["@_id"] ?? generateUuid(); + itemDefinition.allowedValues["@_kie:constraintType"] = enumToKieConstraintType(selectedConstraint); + } }); }, - [editItemDefinition, itemDefinitionId] + [editItemDefinition, enumToKieConstraintType, itemDefinitionId] ); const onToggleGroupChange = useCallback( - (newSelection: boolean, event: React.KeyboardEvent | MouseEvent | React.MouseEvent) => { + (newSelection: boolean, selectedConstraint: ConstraintsType) => { if (!newSelection) { return; } - const selection = event.currentTarget.id as ConstraintsType; - if (selection === ConstraintsType.NONE) { - editItemDefinition(itemDefinitionId!, (itemDefinition) => { + + editItemDefinition(itemDefinitionId, (itemDefinition) => { + if (selectedConstraint === ConstraintsType.NONE) { itemDefinition.allowedValues = undefined; - }); - return; - } + return; + } - editItemDefinition(itemDefinitionId!, (itemDefinition) => { - itemDefinition.allowedValues ??= { text: { __$$text: "" } }; - const previousKieContraintType = itemDefinition.allowedValues["@_kie:constraintType"]; - itemDefinition.allowedValues["@_kie:constraintType"] = enumToKieConstraintType(selection); + if (itemDefinition.allowedValues) { + itemDefinition.allowedValues["@_kie:constraintType"] = enumToKieConstraintType(selectedConstraint); + } - if (selection === ConstraintsType.EXPRESSION) { + if (selectedConstraint === ConstraintsType.EXPRESSION) { return; } if ( - previousKieContraintType === "expression" && - selection === ConstraintsType.ENUMERATION && - isEnum( - itemDefinition.allowedValues.text.__$$text, - constraintTypeHelper( - (itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined - ).check - ) + selectedConstraint === ConstraintsType.ENUMERATION && + isEnum(itemDefinition.allowedValues?.text.__$$text, typeRefConstraintTypeHelper.check) ) { return; } if ( - previousKieContraintType === "expression" && - selection === ConstraintsType.RANGE && - isRange( - itemDefinition.allowedValues.text.__$$text, - constraintTypeHelper( - (itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined - ).check - ) + selectedConstraint === ConstraintsType.RANGE && + isRange(itemDefinition.allowedValues?.text.__$$text, typeRefConstraintTypeHelper.check) ) { return; } - itemDefinition.allowedValues.text.__$$text = ""; - return; + itemDefinition.allowedValues = undefined; }); }, - [editItemDefinition, enumToKieConstraintType, itemDefinitionId] + [editItemDefinition, itemDefinitionId, enumToKieConstraintType, typeRefConstraintTypeHelper.check] ); return ( ; editItemDefinition: EditItemDefinition; renderOnPropertiesPanel?: boolean; defaultsToAllowedValues: boolean; }) { + const { externalModelsByNamespace } = useExternalModels(); + const allDataTypesById = useDmnEditorStore( + (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allDataTypesById + ); + const allTopLevelItemDefinitionUniqueNames = useDmnEditorStore( + (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allTopLevelItemDefinitionUniqueNames + ); + const itemDefinitionId = itemDefinition["@_id"]!; const typeConstraint = useMemo( () => defaultsToAllowedValues @@ -505,96 +575,114 @@ export function ConstraintsFromTypeConstraintAttribute({ [defaultsToAllowedValues, itemDefinition?.allowedValues, itemDefinition?.typeConstraint] ); + const typeRef = itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType; + const typeRefConstraintTypeHelper = useMemo( + () => constraintTypeHelper(itemDefinition, allDataTypesById, allTopLevelItemDefinitionUniqueNames), + [allDataTypesById, allTopLevelItemDefinitionUniqueNames, itemDefinition] + ); + + const rootItemDefinition = useMemo( + () => recursivelyGetRootItemDefinition(itemDefinition, allDataTypesById, allTopLevelItemDefinitionUniqueNames), + [allDataTypesById, allTopLevelItemDefinitionUniqueNames, itemDefinition] + ); + + const enabledConstraints = useMemo(() => { + if (isStruct(rootItemDefinition)) { + return ["expression"] as KIE__tConstraintType[]; + } + if (isCollection(rootItemDefinition)) { + return ["expression"] as KIE__tConstraintType[]; + } + return constrainableBuiltInFeelTypes.get(rootItemDefinition.typeRef?.__$$text as DmnBuiltInDataType); + }, [rootItemDefinition]); + const { constraintValue, - typeRef, isConstraintEnum, isConstraintRange, isConstraintEnabled, - itemDefinitionId, - selectedConstraint, + selectedKieConstraintType, enumToKieConstraintType, } = useConstraint({ constraint: typeConstraint, - itemDefinition, - isCollectionConstraintEnable: true, // typeConstraint enables to add a constraint to the collection itself + itemDefinition: itemDefinition, + isCollectionConstraintEnabled: true, // typeConstraint enables to add a constraint to the collection itself + constraintTypeHelper: typeRefConstraintTypeHelper, + enabledConstraints, }); const onConstraintChange = useCallback( - (value?: string) => { - editItemDefinition(itemDefinitionId!, (itemDefinition) => { - itemDefinition.typeConstraint ??= { text: { __$$text: "" } }; - itemDefinition.typeConstraint.text.__$$text = value ?? ""; - itemDefinition.typeConstraint["@_id"] = itemDefinition.typeConstraint?.["@_id"] ?? generateUuid(); + (value: string | undefined, selectedConstraint: ConstraintsType) => { + editItemDefinition(itemDefinitionId, (itemDefinition) => { + if (value === "" || value === undefined) { + itemDefinition.typeConstraint = undefined; + } else { + itemDefinition.typeConstraint ??= { "@_id": generateUuid(), text: { __$$text: "" } }; + itemDefinition.typeConstraint.text.__$$text = value; + itemDefinition.typeConstraint["@_id"] = itemDefinition.typeConstraint?.["@_id"] ?? generateUuid(); + itemDefinition.typeConstraint["@_kie:constraintType"] = enumToKieConstraintType(selectedConstraint); + } }); }, - [editItemDefinition, itemDefinitionId] + [editItemDefinition, enumToKieConstraintType, itemDefinitionId] ); const onToggleGroupChange = useCallback( - (newSelection: boolean, event: React.KeyboardEvent | MouseEvent | React.MouseEvent) => { + (newSelection: boolean, selectedConstraint: ConstraintsType) => { if (!newSelection) { return; } - const selection = event.currentTarget.id as ConstraintsType; - if (selection === ConstraintsType.NONE) { - editItemDefinition(itemDefinitionId!, (itemDefinition) => { + + editItemDefinition(itemDefinitionId, (itemDefinition) => { + if (selectedConstraint === ConstraintsType.NONE) { itemDefinition.typeConstraint = undefined; - }); - return; - } + return; + } - editItemDefinition(itemDefinitionId!, (itemDefinition) => { - itemDefinition.typeConstraint ??= { text: { __$$text: "" } }; - const previousKieContraintType = itemDefinition.typeConstraint["@_kie:constraintType"]; - itemDefinition.typeConstraint["@_kie:constraintType"] = enumToKieConstraintType(selection); + if (!itemDefinition.typeConstraint && itemDefinition.allowedValues) { + itemDefinition.typeConstraint = itemDefinition.allowedValues; + itemDefinition.allowedValues = undefined; + } - if (selection === ConstraintsType.EXPRESSION) { + if (itemDefinition.typeConstraint) { + itemDefinition.typeConstraint["@_kie:constraintType"] = enumToKieConstraintType(selectedConstraint); + } + + if (selectedConstraint === ConstraintsType.EXPRESSION) { return; } if ( - previousKieContraintType === "expression" && - selection === ConstraintsType.ENUMERATION && - isEnum( - itemDefinition.typeConstraint.text.__$$text, - constraintTypeHelper( - (itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined - ).check - ) + selectedConstraint === ConstraintsType.ENUMERATION && + isEnum(itemDefinition.typeConstraint?.text.__$$text, typeRefConstraintTypeHelper.check) ) { return; } if ( - previousKieContraintType === "expression" && - selection === ConstraintsType.RANGE && - isRange( - itemDefinition.typeConstraint.text.__$$text, - constraintTypeHelper( - (itemDefinition?.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined - ).check - ) + selectedConstraint === ConstraintsType.RANGE && + isRange(itemDefinition.typeConstraint?.text.__$$text, typeRefConstraintTypeHelper.check) ) { return; } - itemDefinition.typeConstraint.text.__$$text = ""; + itemDefinition.typeConstraint = undefined; }); }, - [editItemDefinition, enumToKieConstraintType, itemDefinitionId] + [editItemDefinition, itemDefinitionId, enumToKieConstraintType, typeRefConstraintTypeHelper.check] ); return ( | MouseEvent | React.MouseEvent - ) => void; - onConstraintChange: (value?: string) => void; + onToggleGroupChange: (selected: boolean, selectedConstraint: ConstraintsType) => void; + onConstraintChange: (value: string | undefined, selectedConstraint: ConstraintsType) => void; }) { + const [internalSelectedConstraint, setInternalSelectedConstraint] = useState<{ + selectedConstraint: ConstraintsType; + itemDefinitionId: string; + }>({ selectedConstraint: selectedKieConstraintType, itemDefinitionId }); + + // Updates the `selectedConstraint` only after changing the active item definition + // Both `internalSelectedConstraint` and `selectedKieConstraintType` should not be coupled together + useEffect(() => { + setInternalSelectedConstraint((prev) => { + if (selectedKieConstraintType === ConstraintsType.NONE && prev.itemDefinitionId === itemDefinitionId) { + return prev; + } + return { selectedConstraint: selectedKieConstraintType, itemDefinitionId }; + }); + }, [itemDefinitionId, selectedKieConstraintType]); + + const onToggleGroupChangeInternal = useCallback( + (selected: boolean, event: React.KeyboardEvent | MouseEvent | React.MouseEvent) => { + const selectedConstraint = event.currentTarget.id as ConstraintsType; + setInternalSelectedConstraint((prev) => ({ selectedConstraint, itemDefinitionId: prev.itemDefinitionId })); + onToggleGroupChange(selected, selectedConstraint); + }, + [onToggleGroupChange] + ); + + const onConstraintChangeInternal = useCallback( + (value: string | undefined) => { + if (constraintValue !== value) { + onConstraintChange(value, internalSelectedConstraint.selectedConstraint); + } + }, + [onConstraintChange, internalSelectedConstraint, constraintValue] + ); + return ( <> {isConstraintEnabled.expression === false && @@ -656,8 +777,8 @@ export function Constraints({
- {selectedConstraint === ConstraintsType.ENUMERATION && ( + {internalSelectedConstraint.selectedConstraint === ConstraintsType.ENUMERATION && ( )} - {selectedConstraint === ConstraintsType.RANGE && ( + {internalSelectedConstraint.selectedConstraint === ConstraintsType.RANGE && ( )} - {selectedConstraint === ConstraintsType.EXPRESSION && ( + {internalSelectedConstraint.selectedConstraint === ConstraintsType.EXPRESSION && ( )} - {selectedConstraint === ConstraintsType.NONE && ( + {internalSelectedConstraint.selectedConstraint === ConstraintsType.NONE && (



- + )}

diff --git a/packages/dmn-editor/src/dataTypes/ConstraintsExpression.css b/packages/dmn-editor/src/dataTypes/ConstraintsExpression.css index 2898f16df32..2e9390a9746 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintsExpression.css +++ b/packages/dmn-editor/src/dataTypes/ConstraintsExpression.css @@ -1,3 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ .feel-input, .feel-input > div { height: 88px; diff --git a/packages/dmn-editor/src/dataTypes/ConstraintsExpression.tsx b/packages/dmn-editor/src/dataTypes/ConstraintsExpression.tsx index ecd7e13727b..75d53af21b9 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintsExpression.tsx +++ b/packages/dmn-editor/src/dataTypes/ConstraintsExpression.tsx @@ -18,7 +18,7 @@ */ import * as React from "react"; -import { useMemo, useState, useCallback } from "react"; +import { useMemo, useState, useCallback, useRef, useEffect } from "react"; import { Title } from "@patternfly/react-core/dist/js/components/Title"; import { FeelInput } from "@kie-tools/feel-input-component/dist"; import "./ConstraintsExpression.css"; @@ -28,10 +28,12 @@ import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/a import { TypeHelper } from "./Constraints"; export function ConstraintsExpression({ + id, isReadonly, value, onSave, }: { + id: string; isReadonly: boolean; value?: string; savedValue?: string; @@ -41,15 +43,43 @@ export function ConstraintsExpression({ isDisabled?: boolean; }) { const [preview, setPreview] = useState(value ?? ""); - const [editingValue, setEditingValue] = useState(value); + const [isEditing, setEditing] = useState(false); + const valueCopy = useRef(value); + + const onFeelBlur = useCallback((valueOnBlur: string) => { + setEditing(false); + }, []); + const onFeelChange = useCallback( (_, content, preview) => { - onSave?.(content.trim()); setPreview(preview); + onSave?.(content.trim()); }, [onSave] ); + const onPreviewChanged = useCallback((newPreview: string) => setPreview(newPreview), []); + + useEffect(() => { + valueCopy.current = isEditing ? valueCopy.current : value; + }, [isEditing, value]); + + const onKeyDown = useCallback( + (e) => { + // When inside FEEL Input, all keyboard events should be kept inside it. + // Exceptions to this strategy are handled on `onFeelKeyDown`. + if (!isReadonly && isEditing) { + e.stopPropagation(); + } + + // This is used to start editing a cell without being in edit mode. + if (!isReadonly && !isEditing) { + setEditing(true); + } + }, + [isEditing, isReadonly] + ); + const monacoOptions = useMemo( () => ({ fixedOverflowWidgets: true, @@ -64,7 +94,9 @@ export function ConstraintsExpression({ ); return ( -
+ // FeelInput doens't react to `onFeelChange` updates + // making it necessary to add a key to force a re-render; +
{isReadonly && ( Equivalent FEEL expression: @@ -85,9 +117,10 @@ export function ConstraintsExpression({ <p style={{ fontStyle: "italic" }}>{`<None>`}</p> ))} <FeelInput - value={isReadonly ? value : editingValue} + value={isEditing ? valueCopy.current : value} onChange={onFeelChange} - onPreviewChanged={setPreview} + onBlur={onFeelBlur} + onPreviewChanged={onPreviewChanged} enabled={!isReadonly} options={monacoOptions as any} /> diff --git a/packages/dmn-editor/src/dataTypes/ConstraintsRange.tsx b/packages/dmn-editor/src/dataTypes/ConstraintsRange.tsx index d04e6c85658..b18aca1498e 100644 --- a/packages/dmn-editor/src/dataTypes/ConstraintsRange.tsx +++ b/packages/dmn-editor/src/dataTypes/ConstraintsRange.tsx @@ -31,6 +31,7 @@ const CONSTRAINT_START_ID = "start"; const CONSTRAINT_END_ID = "end"; export function ConstraintsRange({ + id, isReadonly, value, expressionValue, @@ -312,7 +313,7 @@ export function ConstraintsRange({ {!renderOnPropertiesPanel && ( <> <br /> - <ConstraintsExpression isReadonly={true} value={expressionValue ?? ""} type={type} /> + <ConstraintsExpression id={id} isReadonly={true} value={expressionValue ?? ""} type={type} /> </> )} </div> diff --git a/packages/dmn-editor/src/dataTypes/DataTypeName.tsx b/packages/dmn-editor/src/dataTypes/DataTypeName.tsx index 14e5e9aeb34..965d211f2dc 100644 --- a/packages/dmn-editor/src/dataTypes/DataTypeName.tsx +++ b/packages/dmn-editor/src/dataTypes/DataTypeName.tsx @@ -30,6 +30,8 @@ import { buildFeelQNameFromNamespace } from "../feel/buildFeelQName"; import { InlineFeelNameInput, OnInlineFeelNameRenamed } from "../feel/InlineFeelNameInput"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; import { State } from "../store/Store"; +import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; +import { Normalized } from "../normalization/normalize"; export function DataTypeName({ isReadonly, @@ -43,7 +45,7 @@ export function DataTypeName({ }: { isReadonly: boolean; editMode: "hover" | "double-click"; - itemDefinition: DMN15__tItemDefinition; + itemDefinition: Normalized<DMN15__tItemDefinition>; isActive: boolean; relativeToNamespace: string; shouldCommitOnBlur?: boolean; @@ -135,7 +137,7 @@ export function DataTypeName({ /> {!isEditingLabel && ( <TypeRefLabel - typeRef={itemDefinition.typeRef?.__$$text} + typeRef={itemDefinition.typeRef?.__$$text ?? DmnBuiltInDataType.Undefined} isCollection={itemDefinition["@_isCollection"]} relativeToNamespace={relativeToNamespace} /> diff --git a/packages/dmn-editor/src/dataTypes/DataTypePanel.tsx b/packages/dmn-editor/src/dataTypes/DataTypePanel.tsx index 9279932c753..2be7adc9ac1 100644 --- a/packages/dmn-editor/src/dataTypes/DataTypePanel.tsx +++ b/packages/dmn-editor/src/dataTypes/DataTypePanel.tsx @@ -52,6 +52,8 @@ import { useDmnEditor } from "../DmnEditorContext"; import { useResolvedTypeRef } from "./useResolvedTypeRef"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; import { Alert } from "@patternfly/react-core/dist/js/components/Alert/Alert"; +import { Popover } from "@patternfly/react-core/dist/js/components/Popover"; +import { InfoAltIcon } from "@patternfly/react-icons/dist/js/icons/info-alt-icon"; export function DataTypePanel({ isReadonly, @@ -123,7 +125,7 @@ export function DataTypePanel({ } editItemDefinition(dataType.itemDefinition["@_id"]!, (itemDefinition) => { - itemDefinition.typeRef = { __$$text: typeRef }; + itemDefinition.typeRef = typeRef ? { __$$text: typeRef } : undefined; const originalItemDefinition = original(itemDefinition); if (originalItemDefinition?.typeRef?.__$$text !== typeRef) { itemDefinition.typeConstraint = undefined; @@ -185,6 +187,8 @@ export function DataTypePanel({ const allTopLevelItemDefinitionUniqueNames = useDmnEditorStore( (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allTopLevelItemDefinitionUniqueNames ); + const [isCollectionConstraintPopoverOpen, setIsCollectionConstraintPopoverOpen] = useState(false); + const [isCollectionItemConstraintPopoverOpen, setIsCollectionItemConstraintPopoverOpen] = useState(false); const allUniqueNames = useMemo( () => @@ -357,14 +361,36 @@ export function DataTypePanel({ isDisabled={isReadonly} typeRef={resolvedTypeRef} onChange={changeTypeRef} + removeDataTypes={[dataType]} /> <br /> <br /> {dataType.itemDefinition["@_isCollection"] === true ? ( <> - <Title size={"md"} headingLevel="h4"> - Collection constraint - + + + Collection constraint + + setIsCollectionConstraintPopoverOpen(false)} + headerContent="Collection Constraints (Type Constraint)" + headerIcon={} + headerComponent="h1" + bodyContent={ +

+ As per the DMN specification, the Type Constraint attribute lists the possible values +
+ or ranges of values in the base type that are allowed in this ItemDefinition. +

+ } + > + setIsCollectionConstraintPopoverOpen(true)} + onMouseLeave={() => setIsCollectionConstraintPopoverOpen(false)} + /> +
+


- - Collection item constraint - + + + Collection item constraint + + setIsCollectionItemConstraintPopoverOpen(false)} + headerContent="Collection Item Constraints (Allowed Values)" + headerIcon={} + headerComponent="h1" + bodyContent={ +

+ As per the DMN specification, the Allowed Values attribute lists the possible values +
+ or ranges of values in the base type that are allowed in this ItemDefinition. +

+ } + > + setIsCollectionItemConstraintPopoverOpen(true)} + onMouseLeave={() => setIsCollectionItemConstraintPopoverOpen(false)} + /> +
+

Creating constraints for the collection items directly on the collection itself is deprecated since diff --git a/packages/dmn-editor/src/dataTypes/DataTypeSpec.ts b/packages/dmn-editor/src/dataTypes/DataTypeSpec.ts index 1525d4ccec3..d53e5039992 100644 --- a/packages/dmn-editor/src/dataTypes/DataTypeSpec.ts +++ b/packages/dmn-editor/src/dataTypes/DataTypeSpec.ts @@ -37,6 +37,7 @@ import { import { DMN15_SPEC } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; import { DataTypeIndex } from "./DataTypes"; import { KIE__tConstraintType } from "@kie-tools/dmn-marshaller/dist/schemas/kie-1_0/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; export function findDataTypeById({ definitions, @@ -45,7 +46,7 @@ export function findDataTypeById({ }: { allDataTypesById: DataTypeIndex; itemDefinitionId: string; - definitions: DMN15__tDefinitions; + definitions: Normalized; }) { const indexesPath: number[] = []; let current = allDataTypesById.get(itemDefinitionId); @@ -65,7 +66,9 @@ export function findDataTypeById({ return { items, itemDefinition, index: last }; } -export function getNewItemDefinition(partial?: Partial) { +export function getNewItemDefinition( + partial?: Partial> +): Normalized { return { "@_id": generateUuid(), "@_name": "New data type", @@ -75,11 +78,11 @@ export function getNewItemDefinition(partial?: Partial) }; } -export function isCollection(itemDefinition: DMN15__tItemDefinition) { +export function isCollection(itemDefinition: Normalized) { return itemDefinition["@_isCollection"] ?? false; } -export function isStruct(itemDefinition: DMN15__tItemDefinition) { +export function isStruct(itemDefinition: Normalized) { return !itemDefinition.typeRef && !!itemDefinition.itemComponent; } @@ -96,7 +99,7 @@ export const constrainableBuiltInFeelTypes = new Map) { return ( isCollection(itemDefinition) || (!isStruct(itemDefinition) && @@ -105,8 +108,8 @@ export function canHaveConstraints(itemDefinition: DMN15__tItemDefinition) { } export function traverseItemDefinitions( - items: DMN15__tItemDefinition[], - consumer: (itemDefinition: DMN15__tItemDefinition) => void + items: Normalized[], + consumer: (itemDefinition: Normalized) => void ) { for (let i = 0; i < (items.length ?? 0); i++) { consumer(items[i]); @@ -114,7 +117,7 @@ export function traverseItemDefinitions( } } -export type AllExpressions = NonNullable; +export type AllExpressions = NonNullable["expression"]>; export type AllExpressionsWithoutTypes = Omit; export type AllExpressionTypes = AllExpressions["__$$element"]; @@ -122,8 +125,8 @@ export type AllExpressionTypes = AllExpressions["__$$element"]; export function traverseTypeRefedInExpressionHolders( expressionHolder: - | (DMN15__tDecision & { __$$element: "decision" }) - | (DMN15__tBusinessKnowledgeModel & { __$$element: "businessKnowledgeModel" }), + | (Normalized & { __$$element: "decision" }) + | (Normalized & { __$$element: "businessKnowledgeModel" }), consumer: (typed: { "@_typeRef"?: string }) => void ) { if (expressionHolder.__$$element === "decision") { @@ -154,11 +157,11 @@ export function traverseTypeRefedInExpressions( if (__$$element === "literalExpression") { // Leaf expression. } else if (__$$element === "decisionTable") { - for (const e of (expression as DMN15__tDecisionTable).input ?? []) { + for (const e of (expression as Normalized).input ?? []) { traverseTypeRefedInExpressions(e.inputExpression, "literalExpression", consumer); } - for (const e of (expression as DMN15__tDecisionTable).output ?? []) { + for (const e of (expression as Normalized).output ?? []) { consumer(e); if (e.defaultOutputEntry) { consumer(e.defaultOutputEntry); @@ -168,52 +171,52 @@ export function traverseTypeRefedInExpressions( } } } else if (__$$element === "relation") { - for (const e of (expression as DMN15__tRelation).column ?? []) { + for (const e of (expression as Normalized).column ?? []) { consumer(e); } // Leaf expression. } else if (__$$element === "list") { - for (const e of (expression as DMN15__tList).expression ?? []) { + for (const e of (expression as Normalized).expression ?? []) { traverseTypeRefedInExpressions(e, e.__$$element, consumer); } } else if (__$$element === "context") { - for (const e of (expression as DMN15__tContext).contextEntry ?? []) { + for (const e of (expression as Normalized).contextEntry ?? []) { if (e.variable) { consumer(e.variable); } traverseTypeRefedInExpressions(e.expression, e.expression?.__$$element, consumer); } } else if (__$$element === "invocation") { - for (const e of (expression as DMN15__tInvocation).binding ?? []) { + for (const e of (expression as Normalized).binding ?? []) { if (e.parameter) { consumer(e.parameter); } traverseTypeRefedInExpressions(e.expression, e.expression?.__$$element, consumer); } } else if (__$$element === "functionDefinition") { - const e = expression as DMN15__tFunctionDefinition; + const e = expression as Normalized; traverseTypeRefedInExpressions(e.expression, e.expression?.__$$element, consumer); } else if (__$$element === "conditional") { - const e = expression as DMN15__tConditional; + const e = expression as Normalized; traverseTypeRefedInExpressions(e.if.expression, e.if.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.then.expression, e.then.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.else.expression, e.else.expression?.__$$element, consumer); } else if (__$$element === "every") { - const e = expression as DMN15__tQuantified; + const e = expression as Normalized; consumer(e.in); traverseTypeRefedInExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.satisfies.expression, e.satisfies.expression?.__$$element, consumer); } else if (__$$element === "some") { - const e = expression as DMN15__tQuantified; + const e = expression as Normalized; consumer(e.in); traverseTypeRefedInExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.satisfies.expression, e.satisfies.expression?.__$$element, consumer); } else if (__$$element === "filter") { - const e = expression as DMN15__tFilter; + const e = expression as Normalized; traverseTypeRefedInExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.match.expression, e.match.expression?.__$$element, consumer); } else if (__$$element === "for") { - const e = expression as DMN15__tFor; + const e = expression as Normalized; consumer(e.in); traverseTypeRefedInExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseTypeRefedInExpressions(e.return.expression, e.return.expression?.__$$element, consumer); @@ -226,8 +229,8 @@ export function traverseTypeRefedInExpressions( export function traverseExpressionsInExpressionHolders( expressionHolder: - | (DMN15__tDecision & { __$$element: "decision" }) - | (DMN15__tBusinessKnowledgeModel & { __$$element: "businessKnowledgeModel" }), + | (Normalized & { __$$element: "decision" }) + | (Normalized & { __$$element: "businessKnowledgeModel" }), consumer: (expression: AllExpressionsWithoutTypes | undefined, __$$element: AllExpressionTypes | undefined) => void ) { if (expressionHolder.__$$element === "decision") { @@ -257,45 +260,45 @@ export function traverseExpressions( if (__$$element === "literalExpression") { // No nested expressions. } else if (__$$element === "decisionTable") { - for (const e of (expression as DMN15__tDecisionTable).input ?? []) { + for (const e of (expression as Normalized).input ?? []) { traverseExpressions(e.inputExpression, "literalExpression", consumer); } } else if (__$$element === "relation") { // No nested expressions. } else if (__$$element === "list") { - for (const e of (expression as DMN15__tList).expression ?? []) { + for (const e of (expression as Normalized).expression ?? []) { traverseExpressions(e, e.__$$element, consumer); } } else if (__$$element === "context") { - for (const e of (expression as DMN15__tContext).contextEntry ?? []) { + for (const e of (expression as Normalized).contextEntry ?? []) { traverseExpressions(e.expression, e.expression?.__$$element, consumer); } } else if (__$$element === "invocation") { - for (const e of (expression as DMN15__tInvocation).binding ?? []) { + for (const e of (expression as Normalized).binding ?? []) { traverseExpressions(e.expression, e.expression?.__$$element, consumer); } } else if (__$$element === "functionDefinition") { - const e = expression as DMN15__tFunctionDefinition; + const e = expression as Normalized; traverseExpressions(e.expression, e.expression?.__$$element, consumer); } else if (__$$element === "conditional") { - const e = expression as DMN15__tConditional; + const e = expression as Normalized; traverseExpressions(e.if.expression, e.if.expression?.__$$element, consumer); traverseExpressions(e.then.expression, e.then.expression?.__$$element, consumer); traverseExpressions(e.else.expression, e.else.expression?.__$$element, consumer); } else if (__$$element === "every") { - const e = expression as DMN15__tQuantified; + const e = expression as Normalized; traverseExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseExpressions(e.satisfies.expression, e.satisfies.expression?.__$$element, consumer); } else if (__$$element === "some") { - const e = expression as DMN15__tQuantified; + const e = expression as Normalized; traverseExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseExpressions(e.satisfies.expression, e.satisfies.expression?.__$$element, consumer); } else if (__$$element === "filter") { - const e = expression as DMN15__tFilter; + const e = expression as Normalized; traverseExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseExpressions(e.match.expression, e.match.expression?.__$$element, consumer); } else if (__$$element === "for") { - const e = expression as DMN15__tFor; + const e = expression as Normalized; traverseExpressions(e.in.expression, e.in.expression?.__$$element, consumer); traverseExpressions(e.return.expression, e.return.expression?.__$$element, consumer); } else { diff --git a/packages/dmn-editor/src/dataTypes/DataTypes.tsx b/packages/dmn-editor/src/dataTypes/DataTypes.tsx index 3daf48bb213..95bf4a5eae5 100644 --- a/packages/dmn-editor/src/dataTypes/DataTypes.tsx +++ b/packages/dmn-editor/src/dataTypes/DataTypes.tsx @@ -57,9 +57,10 @@ import { getNewDmnIdRandomizer } from "../idRandomizer/dmnIdRandomizer"; import { addTopLevelItemDefinition as _addTopLevelItemDefinition } from "../mutations/addTopLevelItemDefinition"; import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../normalization/normalize"; export type DataType = { - itemDefinition: DMN15__tItemDefinition; + itemDefinition: Normalized; parentId: string | undefined; parents: Set; index: number; @@ -70,16 +71,20 @@ export type DataType = { export type DataTypeTreeViewDataItem = {}; export type DataTypeIndex = Map; -export type AddItemComponent = (id: string, how: "unshift" | "push", partial?: Partial) => void; -export type AddTopLevelItemDefinition = (partial: Partial) => void; +export type AddItemComponent = ( + id: string, + how: "unshift" | "push", + partial?: Partial> +) => void; +export type AddTopLevelItemDefinition = (partial: Partial>) => void; export type EditItemDefinition = ( id: string, consumer: ( - itemDefinition: DMN15__tItemDefinition, - items: DMN15__tItemDefinition[], + itemDefinition: Normalized, + items: Normalized[], index: number, - all: DMN15__tItemDefinition[], + all: Normalized[], state: State ) => void ) => void; @@ -87,7 +92,7 @@ export type EditItemDefinition = ( export function DataTypes() { const thisDmnsNamespace = useDmnEditorStore((s) => s.dmn.model.definitions["@_namespace"]); const dmnEditorStoreApi = useDmnEditorStoreApi(); - const { activeItemDefinitionId } = useDmnEditorStore((s) => s.dataTypesEditor); + const activeItemDefinitionId = useDmnEditorStore((s) => s.dataTypesEditor.activeItemDefinitionId); const [filter, setFilter] = useState(""); const { externalModelsByNamespace } = useExternalModels(); @@ -171,7 +176,7 @@ export function DataTypes() { <> {(dataTypesTree.length <= 0 && ( addTopLevelItemDefinition({ typeRef: { __$$text: DmnBuiltInDataType.Undefined } })} + onAdd={() => addTopLevelItemDefinition({ typeRef: undefined })} onPaste={pasteTopLevelItemDefinition} /> )) || ( @@ -203,9 +208,7 @@ export function DataTypes() { {...extraPropsForDropdownToggleAction} key="add-data-type-action" aria-label="Add Data Type" - onClick={() => - addTopLevelItemDefinition({ typeRef: { __$$text: DmnBuiltInDataType.Undefined } }) - } + onClick={() => addTopLevelItemDefinition({ typeRef: undefined })} > , diff --git a/packages/dmn-editor/src/dataTypes/ItemComponentsTable.tsx b/packages/dmn-editor/src/dataTypes/ItemComponentsTable.tsx index 7778708f412..fe0cb9319ed 100644 --- a/packages/dmn-editor/src/dataTypes/ItemComponentsTable.tsx +++ b/packages/dmn-editor/src/dataTypes/ItemComponentsTable.tsx @@ -39,27 +39,28 @@ import { AngleDownIcon } from "@patternfly/react-icons/dist/js/icons/angle-down- import { AngleRightIcon } from "@patternfly/react-icons/dist/js/icons/angle-right-icon"; import { EyeIcon } from "@patternfly/react-icons/dist/js/icons/eye-icon"; import { TrashIcon } from "@patternfly/react-icons/dist/js/icons/trash-icon"; -import { DataType, EditItemDefinition, AddItemComponent, DataTypeIndex } from "./DataTypes"; +import { AddItemComponent, DataType, DataTypeIndex, EditItemDefinition } from "./DataTypes"; import { DataTypeName } from "./DataTypeName"; -import { isStruct, canHaveConstraints, getNewItemDefinition } from "./DataTypeSpec"; +import { canHaveConstraints, getNewItemDefinition, isStruct } from "./DataTypeSpec"; import { Flex, FlexItem } from "@patternfly/react-core/dist/js/layouts/Flex"; import { Title } from "@patternfly/react-core/dist/js/components/Title"; import { UniqueNameIndex } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; import { + buildClipboardFromDataType, DMN_EDITOR_DATA_TYPES_CLIPBOARD_MIME_TYPE, DmnEditorDataTypesClipboard, - buildClipboardFromDataType, getClipboard, } from "../clipboard/Clipboard"; import { getNewDmnIdRandomizer } from "../idRandomizer/dmnIdRandomizer"; import { isEnum } from "./ConstraintsEnum"; import { isRange } from "./ConstraintsRange"; -import { constraintTypeHelper } from "./Constraints"; +import { constraintTypeHelper, recursivelyGetRootItemDefinition } from "./Constraints"; import { builtInFeelTypeNames } from "./BuiltInFeelTypes"; import { useDmnEditor } from "../DmnEditorContext"; import { DMN15__tItemDefinition } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { resolveTypeRef } from "./resolveTypeRef"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../normalization/normalize"; export const BRIGHTNESS_DECREASE_STEP_IN_PERCENTAGE_PER_NESTING_LEVEL = 5; export const STARTING_BRIGHTNESS_LEVEL_IN_PERCENTAGE = 95; @@ -95,6 +96,9 @@ export function ItemComponentsTable({ const allTopLevelDataTypesByFeelName = useDmnEditorStore( (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allTopLevelDataTypesByFeelName ); + const allTopLevelItemDefinitionUniqueNames = useDmnEditorStore( + (s) => s.computed(s).getDataTypes(externalModelsByNamespace).allTopLevelItemDefinitionUniqueNames + ); const importsByNamespace = useDmnEditorStore((s) => s.computed(s).importsByNamespace()); const thisDmnsNamespace = useDmnEditorStore((s) => s.dmn.model.definitions["@_namespace"]); @@ -107,6 +111,7 @@ export function ItemComponentsTable({ const flatTree = useMemo(() => { const ret: { dataType: DataType; allUniqueNamesAtLevel: UniqueNameIndex }[] = []; + function traverse(dataType: DataType[], allUniqueNamesAtLevel: UniqueNameIndex) { for (let i = 0; i < (dataType?.length ?? 0); i++) { ret.push({ dataType: dataType[i], allUniqueNamesAtLevel }); @@ -159,7 +164,7 @@ export function ItemComponentsTable({ onClick={() => addItemComponent(parent.itemDefinition["@_id"]!, "unshift", { "@_name": "New property", - typeRef: { __$$text: DmnBuiltInDataType.Undefined }, + typeRef: undefined, }) } > @@ -288,21 +293,37 @@ export function ItemComponentsTable({ return <>Range; } - const constraintValue = dt.itemDefinition.allowedValues?.text.__$$text; - const typeRef = - (dt.itemDefinition.typeRef?.__$$text as DmnBuiltInDataType) ?? DmnBuiltInDataType.Undefined; + const constraintValue = + dt.itemDefinition.typeConstraint?.text.__$$text ?? dt.itemDefinition.allowedValues?.text.__$$text; + + const typeHelper = constraintTypeHelper( + dt.itemDefinition, + allDataTypesById, + allTopLevelItemDefinitionUniqueNames + ); + if (constraintValue === undefined) { return <>None; } - if (isEnum(constraintValue, constraintTypeHelper(typeRef).check)) { + if (isEnum(constraintValue, typeHelper.check)) { return <>Enumeration; } - if (isRange(constraintValue, constraintTypeHelper(typeRef).check)) { + if (isRange(constraintValue, typeHelper.check)) { return <>Range; } return <>Expression; }; + const rootItemDefinition = recursivelyGetRootItemDefinition( + dt.itemDefinition, + allDataTypesById, + allTopLevelItemDefinitionUniqueNames + ); + + const isItemComponent = !!parent.itemDefinition?.itemComponent?.find( + (ic) => ic["@_id"] === rootItemDefinition["@_id"] + ); + return ( {shouldShowRow && ( @@ -357,7 +378,7 @@ export function ItemComponentsTable({ onClick={() => { addItemComponent(dt.itemDefinition["@_id"]!, "unshift", { "@_name": "New property", - typeRef: { __$$text: DmnBuiltInDataType.Undefined }, + typeRef: undefined, }); dmnEditorStoreApi.setState((state) => { state.dataTypesEditor.expandedItemComponentIds.push(dt.itemDefinition["@_id"]!); @@ -415,11 +436,11 @@ export function ItemComponentsTable({ })} onChange={(newDataType) => { editItemDefinition(dt.itemDefinition["@_id"]!, (itemDefinition, items) => { - itemDefinition.typeRef = { __$$text: newDataType }; if (itemDefinition.typeRef?.__$$text !== newDataType) { itemDefinition.typeConstraint = undefined; itemDefinition.allowedValues = undefined; } + itemDefinition.typeRef = newDataType ? { __$$text: newDataType } : undefined; }); }} /> @@ -439,7 +460,8 @@ export function ItemComponentsTable({ /> - {canHaveConstraints(dt.itemDefinition) ? ( + {canHaveConstraints(rootItemDefinition) || + (isStruct(rootItemDefinition) && !isItemComponent) ? ( - + )} ; nodeType?: NodeType; isEnabled?: boolean; }): NodeStyle { @@ -111,7 +112,7 @@ export function getNodeStyle({ } export function getNodeShapeFillColor(args: { - dmnStyle?: DMNDI15__DMNStyle | undefined; + dmnStyle?: Normalized | undefined; nodeType?: NodeType | undefined; isEnabled?: boolean | undefined; }) { @@ -134,7 +135,7 @@ export function getNodeShapeFillColor(args: { } export function getNodeShapeStrokeColor(args: { - dmnStyle?: DMNDI15__DMNStyle | undefined; + dmnStyle?: Normalized | undefined; isEnabled?: boolean | undefined; }) { const blue = args.dmnStyle?.["dmndi:StrokeColor"]?.["@_blue"]; @@ -148,7 +149,7 @@ export function getNodeShapeStrokeColor(args: { } export function getDmnFontStyle(args: { - dmnStyle?: DMNDI15__DMNStyle | undefined; + dmnStyle?: Normalized | undefined; isEnabled?: boolean | undefined; }): DmnFontStyle { const blue = args.dmnStyle?.["dmndi:FontColor"]?.["@_blue"]; diff --git a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx index fed4d403ab6..724e8a7a4a0 100644 --- a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx +++ b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx @@ -17,7 +17,7 @@ * under the License. */ -import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; +import { DmnBuiltInDataType, generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { DMN15__tBusinessKnowledgeModel, DMN15__tDecision, @@ -71,6 +71,7 @@ import { OutgoingStuffNodePanel } from "./OutgoingStuffNodePanel"; import { propsHaveSameValuesDeep } from "../memoization/memoization"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; import { NODE_LAYERS } from "../../store/computed/computeDiagramData"; +import { Normalized } from "../../normalization/normalize"; export type ElementFilter = E extends any ? E["__$$element"] extends Filter @@ -80,14 +81,14 @@ export type ElementFilter - | ElementFilter, "textAnnotation" | "group">; + | Unpacked["drgElement"]> + | ElementFilter["artifact"]>, "textAnnotation" | "group">; export type DmnDiagramNodeData = { dmnObjectNamespace: string | undefined; dmnObjectQName: XmlQName; dmnObject: T; - shape: DMNDI15__DMNShape & { index: number }; + shape: Normalized & { index: number }; index: number; hasHiddenRequirements: boolean; /** @@ -106,7 +107,7 @@ export const InputDataNode = React.memo( zIndex, type, id, - }: RF.NodeProps>) => { + }: RF.NodeProps & { __$$element: "inputData" }>>) => { const renderCount = useRef(0); renderCount.current++; @@ -149,8 +150,8 @@ export const InputDataNode = React.memo( const onTypeRefChange = useCallback( (newTypeRef) => { dmnEditorStoreApi.setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tInputData; - drgElement.variable ??= { "@_name": inputData["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": inputData["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }, @@ -167,8 +168,6 @@ export const InputDataNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - const { externalModelsByNamespace } = useExternalModels(); const isCollection = useDmnEditorStore((s) => { @@ -207,9 +206,9 @@ export const InputDataNode = React.memo( return ( <> {isAlternativeInputDataShape ? ( >) => { + }: RF.NodeProps & { __$$element: "decision" }>>) => { const renderCount = useRef(0); renderCount.current++; @@ -375,8 +374,8 @@ export const DecisionNode = React.memo( const onTypeRefChange = useCallback( (newTypeRef) => { dmnEditorStoreApi.setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tDecision; - drgElement.variable ??= { "@_name": decision["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": decision["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; if (drgElement.expression) { drgElement.expression["@_typeRef"] = newTypeRef; @@ -396,8 +395,6 @@ export const DecisionNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - const { externalModelsByNamespace } = useExternalModels(); const isCollection = useDmnEditorStore((s) => { @@ -413,7 +410,7 @@ export const DecisionNode = React.memo( return ( <> - + >) => { + }: RF.NodeProps< + DmnDiagramNodeData & { __$$element: "businessKnowledgeModel" }> + >) => { const renderCount = useRef(0); renderCount.current++; @@ -530,8 +529,10 @@ export const BkmNode = React.memo( const onTypeRefChange = useCallback( (newTypeRef) => { dmnEditorStoreApi.setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tBusinessKnowledgeModel; - drgElement.variable ??= { "@_name": bkm["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![ + index + ] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": bkm["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; if (drgElement.encapsulatedLogic) { drgElement.encapsulatedLogic["@_typeRef"] = newTypeRef; @@ -551,11 +552,9 @@ export const BkmNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - return ( <> - + >) => { + }: RF.NodeProps & { __$$element: "knowledgeSource" }>>) => { const renderCount = useRef(0); renderCount.current++; @@ -678,11 +677,9 @@ export const KnowledgeSourceNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - return ( <> - + >) => { + }: RF.NodeProps & { __$$element: "textAnnotation" }>>) => { const renderCount = useRef(0); renderCount.current++; @@ -796,11 +792,9 @@ export const TextAnnotationNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - return ( <> - + >) => { + }: RF.NodeProps & { __$$element: "decisionService" }>>) => { const renderCount = useRef(0); renderCount.current++; const ref = useRef(null); const isExternal = !!dmnObjectQName.prefix; - + const { externalModelsByNamespace } = useExternalModels(); const snapGrid = useDmnEditorStore((s) => s.diagram.snapGrid); const enableCustomNodeStyles = useDmnEditorStore((s) => s.diagram.overlays.enableCustomNodeStyles); const isHovered = useIsHovered(ref); @@ -930,8 +924,8 @@ export const DecisionServiceNode = React.memo( const onTypeRefChange = useCallback( (newTypeRef) => { dmnEditorStoreApi.setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tInputData; - drgElement.variable ??= { "@_name": decisionService["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": decisionService["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }, @@ -962,9 +956,13 @@ export const DecisionServiceNode = React.memo( dmnEditorStoreApi.setState((state) => { updateDecisionServiceDividerLine({ definitions: state.dmn.model.definitions, - drdIndex: state.diagram.drdIndex, - dmnShapesByHref: state.computed(state).indexedDrd().dmnShapesByHref, + drdIndex: state.computed(state).getDrdIndex(), + __readonly_dmnShapesByHref: state.computed(state).indexedDrd().dmnShapesByHref, drgElementIndex: index, + __readonly_dmnObjectNamespace: dmnObjectNamespace, + __readonly_externalDmnsIndex: state + .computed(state) + .getExternalModelTypesByNamespace(externalModelsByNamespace).dmns, shapeIndex: shape.index, localYPosition: e.y, snapGrid: state.diagram.snapGrid, @@ -981,7 +979,7 @@ export const DecisionServiceNode = React.memo( return () => { selection.on(".drag", null); }; - }, [decisionService, dmnEditorStoreApi, id, index, shape.index]); + }, [decisionService, dmnEditorStoreApi, dmnObjectNamespace, externalModelsByNamespace, id, index, shape.index]); const { fontCssProperties, shapeStyle } = useNodeStyle({ dmnStyle: shape["di:Style"], @@ -989,11 +987,9 @@ export const DecisionServiceNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - return ( <> - +

>) => { + }: RF.NodeProps & { __$$element: "group" }>>) => { const renderCount = useRef(0); renderCount.current++; @@ -1142,11 +1138,9 @@ export const GroupNode = React.memo( isEnabled: enableCustomNodeStyles, }); - const additionalClasses = `${className} ${dmnObjectQName.prefix ? "external" : ""}`; - return ( <> - +
; } & ( | { nodeType: Extract; isAlternativeInputDataShape: boolean } | { nodeType: Exclude } @@ -1470,12 +1464,12 @@ export function useDataTypeCreationCallbackForNodes(index: number, drgElementNam return useCallback( (newDataTypeName) => { dmnEditorStoreApi.setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tInputData; - drgElement.variable ??= { "@_name": drgElementName }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": drgElementName }; drgElement.variable["@_typeRef"] = newDataTypeName; const newItemDefinition = addTopLevelItemDefinition({ definitions: state.dmn.model.definitions, - partial: { "@_name": newDataTypeName, typeRef: { __$$text: DmnBuiltInDataType.Undefined } }, + partial: { "@_name": newDataTypeName, typeRef: undefined }, }); state.dataTypesEditor.activeItemDefinitionId = newItemDefinition["@_id"]; state.navigation.tab = DmnEditorTab.DATA_TYPES; diff --git a/packages/dmn-editor/src/draggable/Draggable.css b/packages/dmn-editor/src/draggable/Draggable.css index 732820ddbae..595fb549677 100644 --- a/packages/dmn-editor/src/draggable/Draggable.css +++ b/packages/dmn-editor/src/draggable/Draggable.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--draggable-row { display: flex; flex-direction: row; diff --git a/packages/dmn-editor/src/externalNodes/DmnObjectListItem.tsx b/packages/dmn-editor/src/externalNodes/DmnObjectListItem.tsx index afff1c14890..85423717aba 100644 --- a/packages/dmn-editor/src/externalNodes/DmnObjectListItem.tsx +++ b/packages/dmn-editor/src/externalNodes/DmnObjectListItem.tsx @@ -30,6 +30,7 @@ import { DmnBuiltInDataType, generateUuid } from "@kie-tools/boxed-expression-co import { useDmnEditorStore } from "../store/StoreContext"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; import { DMN15_SPEC } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; +import { Normalized } from "../normalization/normalize"; export function DmnObjectListItem({ dmnObject, @@ -37,7 +38,7 @@ export function DmnObjectListItem({ namespace, relativeToNamespace, }: { - dmnObject: Unpacked | undefined; + dmnObject: Unpacked["drgElement"]> | undefined; dmnObjectHref: string; namespace: string; relativeToNamespace: string; diff --git a/packages/dmn-editor/src/feel/buildFeelQName.ts b/packages/dmn-editor/src/feel/buildFeelQName.ts index dc97edd7b67..41b8a75c8d1 100644 --- a/packages/dmn-editor/src/feel/buildFeelQName.ts +++ b/packages/dmn-editor/src/feel/buildFeelQName.ts @@ -20,6 +20,7 @@ import { DMN15__tImport, DMN15__tNamedElement } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { XmlQName } from "@kie-tools/xml-parser-ts/dist/qNames"; import { XmlParserTsRootElementBaseType } from "@kie-tools/xml-parser-ts"; +import { Normalized } from "../normalization/normalize"; export type FeelQNameBuild = { full: string; @@ -35,7 +36,7 @@ export function buildFeelQNameFromXmlQName({ relativeToNamespace, model, }: { - namedElement: DMN15__tNamedElement; + namedElement: Normalized; namedElementQName: XmlQName; importsByNamespace: Map; model: XmlParserTsRootElementBaseType; @@ -64,7 +65,7 @@ export function buildFeelQNameFromNamespace({ importsByNamespace, relativeToNamespace, }: { - namedElement: DMN15__tNamedElement; + namedElement: Normalized; namespace: string; importsByNamespace: Map; relativeToNamespace: string; diff --git a/packages/dmn-editor/src/includedModels/IncludedModels.css b/packages/dmn-editor/src/includedModels/IncludedModels.css new file mode 100644 index 00000000000..6f6767ad97e --- /dev/null +++ b/packages/dmn-editor/src/includedModels/IncludedModels.css @@ -0,0 +1,3 @@ +.kie-dmn-editor--selected-model-to-include-error { + color: var(--pf-global--danger-color--100); +} diff --git a/packages/dmn-editor/src/includedModels/IncludedModels.tsx b/packages/dmn-editor/src/includedModels/IncludedModels.tsx index 2f06d2f6f19..3e2d5806ebe 100644 --- a/packages/dmn-editor/src/includedModels/IncludedModels.tsx +++ b/packages/dmn-editor/src/includedModels/IncludedModels.tsx @@ -50,11 +50,15 @@ import { allPmmlImportNamespaces, getPmmlNamespace } from "../pmml/pmml"; import { allDmnImportNamespaces } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; import { getNamespaceOfDmnImport } from "./importNamespaces"; import { Alert, AlertVariant } from "@patternfly/react-core/dist/js/components/Alert/Alert"; -import { Dropdown, DropdownItem, KebabToggle } from "@patternfly/react-core/dist/js/components/Dropdown"; +import { KebabToggle } from "@patternfly/react-core/dist/js/components/Dropdown"; import { TrashIcon } from "@patternfly/react-icons/dist/js/icons/trash-icon"; import { useInViewSelect } from "../responsiveness/useInViewSelect"; import { useCancelableEffect } from "@kie-tools-core/react-hooks/dist/useCancelableEffect"; import { State } from "../store/Store"; +import "./IncludedModels.css"; +import { Normalized } from "../normalization/normalize"; +import { Popover, PopoverPosition } from "@patternfly/react-core/dist/js/components/Popover"; +import { AlertActionCloseButton, AlertActionLink } from "@patternfly/react-core/dist/js/components/Alert"; export const EMPTY_IMPORT_NAME_NAMESPACE_IDENTIFIER = ""; @@ -79,6 +83,7 @@ export function IncludedModels() { const [importName, setImportName] = useState(""); const [selectedModel, setSelectedModel] = useState(undefined); + const [selectedModelError, setSelectedModelError] = useState(undefined); useCancelableEffect( useCallback( @@ -101,9 +106,13 @@ export function IncludedModels() { return; } + setSelectedModelError(undefined); setSelectedModel(externalModel); }) .catch((err) => { + setSelectedModelError( + `An error occurred when parsing the selected model '${selectedPathRelativeToThisDmn}'. Please double-check it is a non-empty valid model.` + ); console.error(err); return; }); @@ -121,6 +130,7 @@ export function IncludedModels() { setModelSelectOpen(false); setSelectedPathRelativeToThisDmn(undefined); setImportName(""); + setSelectedModelError(undefined); }, []); const add = useCallback(() => { @@ -161,6 +171,7 @@ export function IncludedModels() { }); setTimeout(() => { + setSelectedModelError(undefined); setSelectedModel(undefined); }, 5000); // Give it time for the `externalModelsByNamespace` object to be reassembled externally. @@ -239,11 +250,11 @@ export function IncludedModels() { <> setModalOpen(false)} + onClose={() => cancel()} title={"Include model"} variant={ModalVariant.large} actions={ - (modelPathsRelativeToThisDmnNotYetIncluded?.length ?? 0) > 0 + (modelPathsRelativeToThisDmnNotYetIncluded?.length ?? 0) > 0 && selectedModelError === undefined ? [ + ) + } + hasNoPadding={shouldRenderConfirmationMessage} + maxWidth={shouldRenderConfirmationMessage ? "300px" : "150px"} + minWidth={shouldRenderConfirmationMessage ? "300px" : "150px"} + isVisible={isRemovePopoverOpen} + showClose={false} + shouldClose={() => { + setRemovePopoverOpen(false); + setConfirmationPopoverOpen(false); }} + position={PopoverPosition.bottom} + shouldOpen={() => setRemovePopoverOpen(true)} > - {pathDisplayed} - - - - - ); -} - -function UnknownIncludedModelCard({ - _import, - index, - isReadonly, -}: { - _import: DMN15__tImport; - index: number; - isReadonly: boolean; -}) { - const dmnEditorStoreApi = useDmnEditorStoreApi(); - - const remove = useCallback( - (index: number) => { - dmnEditorStoreApi.setState((state) => { - deleteImport({ definitions: state.dmn.model.definitions, index }); - }); - }, - [dmnEditorStoreApi] - ); - - const { externalModelsByNamespace } = useExternalModels(); - - const rename = useCallback( - (newName) => { - dmnEditorStoreApi.setState((state) => { - renameImport({ - definitions: state.dmn.model.definitions, - index, - newName, - allTopLevelDataTypesByFeelName: state.computed(state).getDataTypes(externalModelsByNamespace) - .allTopLevelDataTypesByFeelName, - }); - }); - }, - [dmnEditorStoreApi, externalModelsByNamespace, index] - ); - - const extension = useMemo(() => { - if (allDmnImportNamespaces.has(_import["@_importType"])) { - return "dmn"; - } else if (allPmmlImportNamespaces.has(_import["@_importType"])) { - return "pmml"; - } else { - return "Unknwon"; - } - }, [_import]); - - const [isCardActionsOpen, setCardActionsOpen] = useState(false); - - return ( - - - - } - onSelect={() => setCardActionsOpen(false)} - isOpen={isCardActionsOpen} - menuAppendTo={document.body} - isPlain={true} - position={"right"} - dropdownItems={[ - - {!isReadonly && ( - } - onClick={() => { - if (isReadonly) { - return; - } - - remove(index); - }} - > - Remove - - )} - , - ]} - /> + + - - - + {externalModel ? ( + + {`${title}`} +

-

- Namespace: {_import["@_namespace"]} -

-

- URI: {_import["@_locationURI"] ?? None} -

-
-
+ + + + + ) : ( + // unknown + + + +
+

+ Namespace: {_import["@_namespace"]} +

+

+ URI: {_import["@_locationURI"] ?? None} +

+
+
+ )}
); } diff --git a/packages/dmn-editor/src/includedModels/importNamespaces.ts b/packages/dmn-editor/src/includedModels/importNamespaces.ts index 81fc8424207..e8345f98916 100644 --- a/packages/dmn-editor/src/includedModels/importNamespaces.ts +++ b/packages/dmn-editor/src/includedModels/importNamespaces.ts @@ -20,8 +20,9 @@ import { DMN15__tImport } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { allDmnImportNamespaces } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; import { allPmmlImportNamespaces, getPmmlNamespaceFromDmnImport } from "../pmml/pmml"; +import { Normalized } from "../normalization/normalize"; -export function getNamespaceOfDmnImport({ dmnImport }: { dmnImport: DMN15__tImport }) { +export function getNamespaceOfDmnImport({ dmnImport }: { dmnImport: Normalized }) { if (allDmnImportNamespaces.has(dmnImport["@_importType"])) { return dmnImport["@_namespace"]; } else if (allPmmlImportNamespaces.has(dmnImport["@_importType"])) { diff --git a/packages/dmn-editor/src/mutations/addConnectedNode.ts b/packages/dmn-editor/src/mutations/addConnectedNode.ts index a001d008f15..75868d7f355 100644 --- a/packages/dmn-editor/src/mutations/addConnectedNode.ts +++ b/packages/dmn-editor/src/mutations/addConnectedNode.ts @@ -37,6 +37,7 @@ import { addOrGetDrd } from "./addOrGetDrd"; import { getCentralizedDecisionServiceDividerLine } from "./updateDecisionServiceDividerLine"; import { repopulateInputDataAndDecisionsOnAllDecisionServices } from "./repopulateInputDataAndDecisionsOnDecisionService"; import { buildXmlHref } from "../xml/xmlHrefs"; +import { Normalized } from "../normalization/normalize"; export function addConnectedNode({ definitions, @@ -45,7 +46,7 @@ export function addConnectedNode({ newNode, edgeType, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; sourceNode: { type: NodeType; href: string; bounds: DC__Bounds; shapeId: string | undefined }; newNode: { type: NodeType; bounds: DC__Bounds }; @@ -62,7 +63,7 @@ export function addConnectedNode({ definitions.drgElement ??= []; const variableBase = { "@_id": generateUuid(), - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, }; definitions.drgElement?.push( switchExpression(newNode.type as Exclude, { @@ -179,40 +180,53 @@ export function getRequirementsFromEdge( sourceNode: { type: NodeType; href: string }, newEdgeId: string, edge: EdgeType -) { +): + | (Pick, "informationRequirement"> & + Pick, "knowledgeRequirement"> & + Pick, "authorityRequirement">) + | undefined { const ir: | undefined // - | Required> - | Required> = switchExpression(sourceNode.type, { - [NODE_TYPES.inputData]: { "@_id": newEdgeId, requiredInput: { "@_href": `${sourceNode.href}` } }, - [NODE_TYPES.decision]: { "@_id": newEdgeId, requiredDecision: { "@_href": `${sourceNode.href}` } }, - default: undefined, - }); + | Required, "requiredInput" | "@_id">> + | Required, "requiredDecision" | "@_id">> = switchExpression( + sourceNode.type, + { + [NODE_TYPES.inputData]: { "@_id": newEdgeId, requiredInput: { "@_href": `${sourceNode.href}` } }, + [NODE_TYPES.decision]: { "@_id": newEdgeId, requiredDecision: { "@_href": `${sourceNode.href}` } }, + default: undefined, + } + ); const kr: | undefined // - | Required> = switchExpression(sourceNode.type, { - [NODE_TYPES.bkm]: { "@_id": newEdgeId, requiredKnowledge: { "@_href": `${sourceNode.href}` } }, - [NODE_TYPES.decisionService]: { "@_id": newEdgeId, requiredKnowledge: { "@_href": `${sourceNode.href}` } }, - default: undefined, - }); + | Required, "requiredKnowledge" | "@_id">> = switchExpression( + sourceNode.type, + { + [NODE_TYPES.bkm]: { "@_id": newEdgeId, requiredKnowledge: { "@_href": `${sourceNode.href}` } }, + [NODE_TYPES.decisionService]: { "@_id": newEdgeId, requiredKnowledge: { "@_href": `${sourceNode.href}` } }, + default: undefined, + } + ); const ar: | undefined // - | Required> - | Required> - | Required> = switchExpression(sourceNode.type, { - [NODE_TYPES.inputData]: { "@_id": newEdgeId, requiredInput: { "@_href": `${sourceNode.href}` } }, - [NODE_TYPES.decision]: { "@_id": newEdgeId, requiredDecision: { "@_href": `${sourceNode.href}` } }, - [NODE_TYPES.knowledgeSource]: { "@_id": newEdgeId, requiredAuthority: { "@_href": `${sourceNode.href}` } }, - default: undefined, - }); + | Required, "requiredInput" | "@_id">> + | Required, "requiredDecision" | "@_id">> + | Required, "requiredAuthority" | "@_id">> = switchExpression( + sourceNode.type, + { + [NODE_TYPES.inputData]: { "@_id": newEdgeId, requiredInput: { "@_href": `${sourceNode.href}` } }, + [NODE_TYPES.decision]: { "@_id": newEdgeId, requiredDecision: { "@_href": `${sourceNode.href}` } }, + [NODE_TYPES.knowledgeSource]: { "@_id": newEdgeId, requiredAuthority: { "@_href": `${sourceNode.href}` } }, + default: undefined, + } + ); // We can use tDecision to type here, because it contains all requirement types. const requirements: - | (Pick & - Pick & - Pick) + | (Pick, "informationRequirement"> & + Pick, "knowledgeRequirement"> & + Pick, "authorityRequirement">) | undefined = switchExpression(edge, { [EDGE_TYPES.informationRequirement]: ir ? { informationRequirement: [ir] } : undefined, [EDGE_TYPES.knowledgeRequirement]: kr ? { knowledgeRequirement: [kr] } : undefined, diff --git a/packages/dmn-editor/src/mutations/addDecisionToDecisionService.ts b/packages/dmn-editor/src/mutations/addDecisionToDecisionService.ts index aa33665f767..6bd3055581f 100644 --- a/packages/dmn-editor/src/mutations/addDecisionToDecisionService.ts +++ b/packages/dmn-editor/src/mutations/addDecisionToDecisionService.ts @@ -24,6 +24,7 @@ import { repopulateInputDataAndDecisionsOnDecisionService } from "./repopulateIn import { SnapGrid } from "../store/Store"; import { MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import { Normalized } from "../normalization/normalize"; export function addDecisionToDecisionService({ definitions, @@ -32,7 +33,7 @@ export function addDecisionToDecisionService({ drdIndex, snapGrid, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; decisionId: string; decisionServiceId: string; drdIndex: number; @@ -55,11 +56,11 @@ export function addDecisionToDecisionService({ const diagram = addOrGetDrd({ definitions, drdIndex }); const decisionShape = diagram.diagramElements.find( (s) => s["@_dmnElementRef"] === decisionId && s.__$$element === "dmndi:DMNShape" - ) as DMNDI15__DMNShape; + ) as Normalized; const decisionServiceShape = diagram.diagramElements.find( (s) => s["@_dmnElementRef"] === decisionServiceId && s.__$$element === "dmndi:DMNShape" - ) as DMNDI15__DMNShape; + ) as Normalized; const section = getSectionForDecisionInsideDecisionService({ decisionShape, decisionServiceShape, snapGrid }); if (section === "encapsulated") { @@ -80,8 +81,8 @@ export function getSectionForDecisionInsideDecisionService({ decisionServiceShape, snapGrid, }: { - decisionShape: DMNDI15__DMNShape; - decisionServiceShape: DMNDI15__DMNShape; + decisionShape: Normalized; + decisionServiceShape: Normalized; snapGrid: SnapGrid; }): "output" | "encapsulated" { if (!decisionShape?.["dc:Bounds"] || !decisionServiceShape?.["dc:Bounds"]) { diff --git a/packages/dmn-editor/src/mutations/addEdge.ts b/packages/dmn-editor/src/mutations/addEdge.ts index ff98be9a09d..14e549433f9 100644 --- a/packages/dmn-editor/src/mutations/addEdge.ts +++ b/packages/dmn-editor/src/mutations/addEdge.ts @@ -27,6 +27,7 @@ import { DMN15__tInformationRequirement, DMN15__tKnowledgeRequirement, DMNDI15__DMNEdge, + DMNDI15__DMNShape, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { PositionalNodeHandleId } from "../diagram/connections/PositionalNodeHandles"; import { EdgeType, NodeType } from "../diagram/connections/graphStructure"; @@ -39,6 +40,7 @@ import { Unpacked } from "../tsExt/tsExt"; import { repopulateInputDataAndDecisionsOnAllDecisionServices } from "./repopulateInputDataAndDecisionsOnDecisionService"; import { DmnDiagramNodeData } from "../diagram/nodes/Nodes"; import { AutoPositionedEdgeMarker } from "../diagram/edges/AutoPositionedEdgeMarker"; +import { Normalized } from "../normalization/normalize"; export function addEdge({ definitions, @@ -48,7 +50,7 @@ export function addEdge({ edge, keepWaypoints, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; sourceNode: { type: NodeType; @@ -85,7 +87,7 @@ export function addEdge({ if (edge.type === EDGE_TYPES.association) { definitions.artifact ??= []; - const newAssociation: DMN15__tAssociation = { + const newAssociation: Normalized = { "@_id": newEdgeId, "@_associationDirection": "Both", sourceRef: { "@_href": `${sourceNode.href}` }, @@ -109,7 +111,7 @@ export function addEdge({ // Requirements else { const requirements = getRequirementsFromEdge(sourceNode, newEdgeId, edge.type); - const drgElement = definitions.drgElement![targetNode.index] as DMN15__tDecision; // We cast to tDecision here because it has all three types of requirement. + const drgElement = definitions.drgElement![targetNode.index] as Normalized; // We cast to tDecision here because it has all three types of requirement. if (requirements?.informationRequirement) { drgElement.informationRequirement ??= []; const removed = removeFirstMatchIfPresent(drgElement.informationRequirement, (ir) => @@ -156,10 +158,10 @@ export function addEdge({ const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); // Remove existing - const removedDmnEdge: DMNDI15__DMNEdge | undefined = removeFirstMatchIfPresent( + const removedDmnEdge = removeFirstMatchIfPresent( diagramElements, (e) => e.__$$element === "dmndi:DMNEdge" && e["@_dmnElementRef"] === existingEdgeId - ); + ) as Normalized | undefined; const newWaypoints = keepWaypoints ? [ @@ -191,18 +193,18 @@ export function addEdge({ return { newDmnEdge }; } -function doesInformationRequirementsPointTo(a: DMN15__tInformationRequirement, nodeId: string) { +function doesInformationRequirementsPointTo(a: Normalized, nodeId: string) { return ( a.requiredInput?.["@_href"] === `${nodeId}` || // a.requiredDecision?.["@_href"] === `${nodeId}` ); } -function doesKnowledgeRequirementsPointTo(a: DMN15__tKnowledgeRequirement, nodeId: string) { +function doesKnowledgeRequirementsPointTo(a: Normalized, nodeId: string) { return a.requiredKnowledge?.["@_href"] === `${nodeId}`; } -function doesAuthorityRequirementsPointTo(a: DMN15__tAuthorityRequirement, nodeId: string) { +function doesAuthorityRequirementsPointTo(a: Normalized, nodeId: string) { return ( a.requiredInput?.["@_href"] === `${nodeId}` || a.requiredDecision?.["@_href"] === `${nodeId}` || @@ -210,7 +212,7 @@ function doesAuthorityRequirementsPointTo(a: DMN15__tAuthorityRequirement, nodeI ); } -function areAssociationsEquivalent(a: DMN15__tAssociation, b: DMN15__tAssociation) { +function areAssociationsEquivalent(a: Normalized, b: Normalized) { return ( (a.sourceRef["@_href"] === b.sourceRef["@_href"] && a.targetRef["@_href"] === b.targetRef["@_href"]) || (a.sourceRef["@_href"] === b.targetRef["@_href"] && a.targetRef["@_href"] === b.sourceRef["@_href"]) diff --git a/packages/dmn-editor/src/mutations/addEdgeWaypoint.ts b/packages/dmn-editor/src/mutations/addEdgeWaypoint.ts index 87735cf144d..282865f0c5e 100644 --- a/packages/dmn-editor/src/mutations/addEdgeWaypoint.ts +++ b/packages/dmn-editor/src/mutations/addEdgeWaypoint.ts @@ -19,6 +19,7 @@ import { DC__Point, DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { addOrGetDrd } from "./addOrGetDrd"; +import { Normalized } from "../normalization/normalize"; export function addEdgeWaypoint({ definitions, @@ -27,7 +28,7 @@ export function addEdgeWaypoint({ beforeIndex, waypoint, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; edgeIndex: number; beforeIndex: number; diff --git a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts index 97e39f60a54..84817732264 100644 --- a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts +++ b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts @@ -17,72 +17,91 @@ * under the License. */ +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { DMN15__tDecisionService, DMN15__tDefinitions, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; -import { DECISION_SERVICE_COLLAPSED_DIMENSIONS } from "../diagram/nodes/DefaultSizes"; +import { ExternalModelsIndex } from "../DmnEditor"; +import { getAutoLayoutedInfo } from "../autolayout/autoLayoutInfo"; +import { DECISION_SERVICE_COLLAPSED_DIMENSIONS, MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; -import { Computed } from "../store/Store"; +import { Normalized } from "../normalization/normalize"; +import { Computed, SnapGrid, State } from "../store/Store"; import { computeContainingDecisionServiceHrefsByDecisionHrefs } from "../store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts"; +import { computeDiagramData } from "../store/computed/computeDiagramData"; +import { computeExternalModelsByType } from "../store/computed/computeExternalModelsByType"; import { computeIndexedDrd } from "../store/computed/computeIndexes"; import { xmlHrefToQName } from "../xml/xmlHrefToQName"; import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs"; +import { addOrGetDrd } from "./addOrGetDrd"; import { addShape } from "./addShape"; +import { applyAutoLayoutToDrd } from "./applyAutoLayoutToDrd"; import { repositionNode } from "./repositionNode"; +export enum StrategyForAddingDecisionServiceToDrd { + AUTO_GENERATE, + CONFLICT, + COPY_FROM_ANOTHER_DRD, +} + /** * When adding a Decision Service to a DRD, we need to bring all its encapsulated and output Decisions with it, * copying their layout from other DRDs, or formatting with autolayout. + * This method returns the strategy to be used when adding a Decision Service. + * The strategy should be used to call one of three methods: + * - AUTO_GENERATE: addAutoGeneratedDecisionServiceToDrd + * - CONFLICT: addConflictingDecisionServiceToDrd + * - COPY_FROM_ANOTHER_DRD: addExistingDecisionServiceToDrd */ -export function addExistingDecisionServiceToDrd({ - decisionServiceNamespace, - decisionService, - externalDmnsIndex, - thisDmnsNamespace, - thisDmnsDefinitions, - thisDmnsIndexedDrd, - drdIndex, - dropPoint, +export function getStrategyToAddExistingDecisionServiceToDrd({ + __readonly_definitions, + __readonly_decisionServiceNamespace, + __readonly_drgElement, + __readonly_externalDmnsIndex, + __readonly_namespace, + __readonly_indexedDrd, + __readonly_drdIndex, }: { - decisionServiceNamespace: string; - decisionService: DMN15__tDecisionService; - externalDmnsIndex: ReturnType["dmns"]; - thisDmnsNamespace: string; - thisDmnsDefinitions: DMN15__tDefinitions; - thisDmnsIndexedDrd: ReturnType; - drdIndex: number; - dropPoint: { x: number; y: number }; + __readonly_definitions: Normalized; + __readonly_decisionServiceNamespace: string; + __readonly_drgElement: Normalized; + __readonly_externalDmnsIndex: ReturnType["dmns"]; + __readonly_namespace: string; + __readonly_indexedDrd: ReturnType; + __readonly_drdIndex: number; }) { const decisionServiceDmnDefinitions = - !decisionServiceNamespace || decisionServiceNamespace === thisDmnsNamespace - ? thisDmnsDefinitions - : externalDmnsIndex.get(decisionServiceNamespace)?.model.definitions; + !__readonly_decisionServiceNamespace || __readonly_decisionServiceNamespace === __readonly_namespace + ? __readonly_definitions + : __readonly_externalDmnsIndex.get(__readonly_decisionServiceNamespace)?.model.definitions; if (!decisionServiceDmnDefinitions) { - throw new Error(`DMN MUTATION: Can't find definitions for model with namespace ${decisionServiceNamespace}`); + throw new Error( + `DMN MUTATION: Can't find definitions for model with namespace ${__readonly_decisionServiceNamespace}` + ); } const { decisionServiceNamespaceForHref, containedDecisionHrefsRelativeToThisDmn } = getDecisionServicePropertiesRelativeToThisDmn({ - thisDmnsNamespace, - decisionServiceNamespace, - decisionService, + thisDmnsNamespace: __readonly_namespace, + decisionServiceNamespace: __readonly_decisionServiceNamespace, + decisionService: __readonly_drgElement, }); const decisionServiceHrefRelativeToThisDmn = buildXmlHref({ namespace: decisionServiceNamespaceForHref, - id: decisionService["@_id"]!, + id: __readonly_drgElement["@_id"]!, }); const containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn = computeContainingDecisionServiceHrefsByDecisionHrefs({ - thisDmnsNamespace, - drgElementsNamespace: decisionServiceNamespace, + thisDmnsNamespace: __readonly_namespace, + drgElementsNamespace: __readonly_decisionServiceNamespace, drgElements: decisionServiceDmnDefinitions.drgElement, }); const doesThisDrdHaveConflictingDecisionService = containedDecisionHrefsRelativeToThisDmn.some((decisionHref) => (containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn.get(decisionHref) ?? []).some((d) => - thisDmnsIndexedDrd.dmnShapesByHref.has(d) + __readonly_indexedDrd.dmnShapesByHref.has(d) ) ); @@ -90,33 +109,21 @@ export function addExistingDecisionServiceToDrd({ // There's already, in this DRD, a Decision Service in expanded form that contains a Decision that is contained by the Decision Service we're adding. // As the DMN specification doesn't allow two copies of the same DRG element to be depicted in the same DRD, we can't add the Decision Service in expanded form. // To not disallow depicting the Decision Service in this DRD, though, we add it in collpased form. - addShape({ - definitions: thisDmnsDefinitions, - drdIndex, - nodeType: NODE_TYPES.decisionService, - shape: { - "@_dmnElementRef": xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions), - "@_isCollapsed": true, - "dc:Bounds": { - "@_x": dropPoint.x, - "@_y": dropPoint.y, - "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width, - "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height, - }, - }, - }); - return; + return { + strategyForAddingDecisionServiceToDrd: StrategyForAddingDecisionServiceToDrd.CONFLICT, + decisionServiceHrefRelativeToThisDmn, + containedDecisionHrefsRelativeToThisDmn, + }; } const drds = decisionServiceDmnDefinitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? []; - let indexedDrd: ReturnType | undefined; for (let i = 0; i < drds.length; i++) { - if (thisDmnsNamespace === decisionServiceNamespace && i === drdIndex) { + if (__readonly_namespace === __readonly_decisionServiceNamespace && i === __readonly_drdIndex) { continue; // Skip the current DRD! } - const _indexedDrd = computeIndexedDrd(thisDmnsNamespace, decisionServiceDmnDefinitions, i); + const _indexedDrd = computeIndexedDrd(__readonly_namespace, decisionServiceDmnDefinitions, i); const dsShape = _indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn); const hasCompleteExpandedDepictionOfDecisionService = dsShape && @@ -129,95 +136,383 @@ export function addExistingDecisionServiceToDrd({ } } - if (!indexedDrd) { - // There's no DRD which inclues a complete expanded depiction of the Decision Service. Let's proceed with auto-layout. - // TODO: Tiago + if (indexedDrd === undefined) { + return { + strategyForAddingDecisionServiceToDrd: StrategyForAddingDecisionServiceToDrd.AUTO_GENERATE, + decisionServiceHrefRelativeToThisDmn, + containedDecisionHrefsRelativeToThisDmn, + }; } else { - // Let's copy the expanded depiction of the Decision Service from `drd`. - // Adding or moving nodes that already exist in the current DRD to inside the Decision Service. - // The positions need all be relative to the Decision Service node, of course. - const dsShapeOnOtherDrd = indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn); - if ( - dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined || - dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined - ) { + return { + strategyForAddingDecisionServiceToDrd: StrategyForAddingDecisionServiceToDrd.COPY_FROM_ANOTHER_DRD, + indexedDrdContainingDecisionServiceDepiction: indexedDrd, + decisionServiceHrefRelativeToThisDmn, + containedDecisionHrefsRelativeToThisDmn, + }; + } +} + +export function addConflictingDecisionServiceToDrd({ + definitions, + __readonly_drdIndex, + __readonly_dropPoint, + __readonly_decisionServiceHrefRelativeToThisDmn, +}: { + definitions: Normalized; + __readonly_drdIndex: number; + __readonly_dropPoint: { x: number; y: number }; + __readonly_decisionServiceHrefRelativeToThisDmn: string; +}) { + addShape({ + definitions: definitions, + drdIndex: __readonly_drdIndex, + nodeType: NODE_TYPES.decisionService, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn, definitions), + "@_isCollapsed": true, + "dc:Bounds": { + "@_x": __readonly_dropPoint.x, + "@_y": __readonly_dropPoint.y, + "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width, + "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height, + }, + }, + }); +} + +export async function addAutoGeneratedDecisionServiceToDrd({ + state, + __readonly_decisionServiceNamespace, + __readonly_externalDmnsIndex, + __readonly_drdIndex, + __readonly_snapGrid, + __readonly_decisionServiceHrefRelativeToThisDmn, + __readonly_containedDecisionHrefsRelativeToThisDmn, + __readonly_dropPoint, + __readonly_isAlternativeInputDataShape, + __readonly_externalModelsByNamespace, +}: { + state: State; + __readonly_decisionServiceNamespace: string; + __readonly_externalDmnsIndex: ReturnType["dmns"]; + __readonly_drdIndex: number; + __readonly_snapGrid: SnapGrid; + __readonly_decisionServiceHrefRelativeToThisDmn: string; + __readonly_containedDecisionHrefsRelativeToThisDmn: string[]; + __readonly_dropPoint: { x: number; y: number }; + __readonly_isAlternativeInputDataShape: boolean; + __readonly_externalModelsByNamespace: ExternalModelsIndex | undefined; +}) { + const drds = state.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]; + // Create a Dummy DRD + // In case the model doesn't have a DRD, the Dummy DRD will be the new DRD. + // Otherwise, the Dummy DRD will be the next index. + const dummyDrdIndex = drds?.length === undefined ? 0 : drds.length; + addOrGetDrd({ + definitions: state.dmn.model.definitions, + drdIndex: dummyDrdIndex, + }); + + // Add the Decision Service + const minNodeSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({ + snapGrid: __readonly_snapGrid, + }); + addShape({ + definitions: state.dmn.model.definitions, + drdIndex: dummyDrdIndex, + nodeType: NODE_TYPES.decisionService, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn, state.dmn.model.definitions), + "dc:Bounds": { + "@_x": 0, // Auto-layout will be applied; + "@_y": 0, // Auto-layout will be applied; + ...minNodeSize, + }, + }, + }); + + // Add all Encapsulated and Output Decisions + for (const decisionHref of __readonly_containedDecisionHrefsRelativeToThisDmn) { + const decisionNs = parseXmlHref(decisionHref).namespace; + const decisionDmnDefinitions = + !decisionNs || decisionNs === state.dmn.model.definitions["@_namespace"] + ? state.dmn.model.definitions + : __readonly_externalDmnsIndex.get(decisionNs)?.model.definitions; + if (!decisionDmnDefinitions) { throw new Error( - `DMN MUTATION: Complete DMNShape for Decision Service with href ${decisionServiceHrefRelativeToThisDmn} should've existed on the indexed DRD.` + `DMN MUTATION: Can't find definitions for model with namespace ${__readonly_decisionServiceNamespace}` ); } - + const minNodeSize = MIN_NODE_SIZES[NODE_TYPES.decision]({ + snapGrid: __readonly_snapGrid, + }); addShape({ - definitions: thisDmnsDefinitions, - drdIndex, - nodeType: NODE_TYPES.decisionService, + definitions: state.dmn.model.definitions, + drdIndex: dummyDrdIndex, + nodeType: NODE_TYPES.decision, shape: { - "@_dmnElementRef": xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions), + "@_id": generateUuid(), + "@_dmnElementRef": xmlHrefToQName(decisionHref, state.dmn.model.definitions), "dc:Bounds": { - "@_x": dropPoint.x, - "@_y": dropPoint.y, - "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"], - "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"], + "@_x": 0, // Auto-layout will be applied; + "@_y": 0, // Auto-layout will be applied; + ...minNodeSize, }, }, }); + } + + // Compute the external model types by namespace after autogenerating the Decision Service + const externalModelTypesByNamespace = computeExternalModelsByType( + state.dmn.model.definitions.import, + __readonly_externalModelsByNamespace + ); + + // Compute the Dummy DRD indexed drd after autogenerating the Decision Service + const dummyIndexedDrd = computeIndexedDrd( + state.dmn.model.definitions["@_namespace"], + state.dmn.model.definitions, + dummyDrdIndex + ); - for (const decisionHref of containedDecisionHrefsRelativeToThisDmn) { - const decisionShapeOnOtherDrd = indexedDrd.dmnShapesByHref.get(decisionHref); - if ( - decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined || - decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined || - decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined || - decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined - ) { + // Compute the Dummy DRD diagram data after autogenerating the Decision Service + const { nodes, nodesById, edges, edgesById, drgEdges } = computeDiagramData( + state.diagram, + state.dmn.model.definitions, + externalModelTypesByNamespace, + dummyIndexedDrd, + __readonly_isAlternativeInputDataShape + ); + + // Get the auto-layout info + const { __readonly_autoLayoutedInfo, __readonly_parentNodesById } = await getAutoLayoutedInfo({ + __readonly_snapGrid, + __readonly_nodesById: nodesById, + __readonly_edgesById: edgesById, + __readonly_nodes: nodes, + __readonly_drgEdges: drgEdges, + __readonly_isAlternativeInputDataShape: __readonly_isAlternativeInputDataShape, + }); + + // Apply the auto-layouted info to the Dummy DRD + applyAutoLayoutToDrd({ + state, + __readonly_dmnShapesByHref: dummyIndexedDrd.dmnShapesByHref, + __readonly_edges: edges, + __readonly_edgesById: edgesById, + __readonly_nodesById: nodesById, + __readonly_autoLayoutedInfo, + __readonly_parentNodesById, + __readonly_drdIndex: dummyDrdIndex, + __readonly_dmnObjectNamespace: __readonly_decisionServiceNamespace, + __readonly_externalDmnsIndex: externalModelTypesByNamespace.dmns, + }); + + // Save DS shape before applying the autolayout + const { "@_x": dsShapeX, "@_y": dsShapeY } = dummyIndexedDrd.dmnShapesByHref.get( + __readonly_decisionServiceHrefRelativeToThisDmn + )!["dc:Bounds"]!; + // Reposition the auto generated Decision Service to the drop point + repositionNode({ + definitions: state.dmn.model.definitions, + drdIndex: dummyDrdIndex, + controlWaypointsByEdge: new Map(), + change: { + nodeType: NODE_TYPES.decisionService, + type: "absolute", + position: { x: __readonly_dropPoint.x, y: __readonly_dropPoint.y }, + shapeIndex: dummyIndexedDrd.dmnShapesByHref.get(__readonly_decisionServiceHrefRelativeToThisDmn)?.index ?? 0, + selectedEdges: [], + sourceEdgeIndexes: [], + targetEdgeIndexes: [], + }, + }); + + // Relatively reposition the auto generated Decisions to the drop point + for (const decisionHref of __readonly_containedDecisionHrefsRelativeToThisDmn) { + const currentDecisionShape = dummyIndexedDrd.dmnShapesByHref.get(decisionHref); + + const x = __readonly_dropPoint.x + currentDecisionShape!["dc:Bounds"]!["@_x"] - dsShapeX; + const y = __readonly_dropPoint.y + currentDecisionShape!["dc:Bounds"]!["@_y"] - dsShapeY; + + if (currentDecisionShape) { + repositionNode({ + definitions: state.dmn.model.definitions, + drdIndex: dummyDrdIndex, + controlWaypointsByEdge: new Map(), + change: { + nodeType: NODE_TYPES.decision, + type: "absolute", + position: { x, y }, + shapeIndex: currentDecisionShape.index, + selectedEdges: [], + sourceEdgeIndexes: [], + targetEdgeIndexes: [], + }, + }); + } + } + + // Copy the DS and Decisions to current DRD and remove Dummy DRD; + if (dummyDrdIndex !== __readonly_drdIndex) { + // The auto generated shape. + const { index, dmnElementRefQName, ...dsShape } = dummyIndexedDrd.dmnShapesByHref.get( + __readonly_decisionServiceHrefRelativeToThisDmn + )!; + drds![__readonly_drdIndex]["dmndi:DMNDiagramElement"] ??= []; + drds?.[__readonly_drdIndex]["dmndi:DMNDiagramElement"]?.push({ ...dsShape, __$$element: "dmndi:DMNShape" }); + for (const decisionHref of __readonly_containedDecisionHrefsRelativeToThisDmn) { + const { index, dmnElementRefQName, ...decisionShape } = dummyIndexedDrd.dmnShapesByHref.get(decisionHref)!; + drds?.[__readonly_drdIndex]["dmndi:DMNDiagramElement"]?.push({ + ...decisionShape, + __$$element: "dmndi:DMNShape", + }); + } + drds?.pop(); // Remove Dummy DRD; + } else { + // In this case Dummy DRD is the current DRD + } +} + +export function addExistingDecisionServiceToDrd({ + definitions, + __readonly_decisionServiceNamespace, + __readonly_externalDmnsIndex, + __readonly_namespace, + __readonly_indexedDrd, + __readonly_indexedDrdContainingDecisionServiceDepiction, + __readonly_drdIndex, + __readonly_dropPoint, + __readonly_decisionServiceHrefRelativeToThisDmn, + __readonly_containedDecisionHrefsRelativeToThisDmn, +}: { + definitions: Normalized; + __readonly_decisionServiceNamespace: string; + __readonly_externalDmnsIndex: ReturnType["dmns"]; + __readonly_namespace: string; + __readonly_indexedDrd: ReturnType; + __readonly_indexedDrdContainingDecisionServiceDepiction: ReturnType; + __readonly_drdIndex: number; + __readonly_dropPoint: { x: number; y: number }; + __readonly_decisionServiceHrefRelativeToThisDmn: string; + __readonly_containedDecisionHrefsRelativeToThisDmn: string[]; +}) { + // Let's copy the expanded depiction of the Decision Service from `drd`. + // Adding or moving nodes that already exist in the current DRD to inside the Decision Service. + // The positions need all be relative to the Decision Service node, of course. + const dsShapeOnOtherDrd = __readonly_indexedDrdContainingDecisionServiceDepiction.dmnShapesByHref.get( + __readonly_decisionServiceHrefRelativeToThisDmn + ); + if ( + dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined || + dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined + ) { + throw new Error( + `DMN MUTATION: Complete DMNShape for Decision Service with href ${__readonly_decisionServiceHrefRelativeToThisDmn} should've existed on the indexed DRD.` + ); + } + + const dsDividirLineOnOtherDrd = dsShapeOnOtherDrd["dmndi:DMNDecisionServiceDividerLine"]; + const decisionServiceDividerLineWaypoint = [ + { + "@_x": __readonly_dropPoint.x, + "@_y": + (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_y"] ?? 0) - + (dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] ?? 0) + + __readonly_dropPoint.y, + }, + { + "@_x": + (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[1]["@_x"] ?? 0) - + (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_x"] ?? 0) + + __readonly_dropPoint.x, + "@_y": + (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_y"] ?? 0) - + (dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] ?? 0) + + __readonly_dropPoint.y, + }, + ]; + addShape({ + definitions: definitions, + drdIndex: __readonly_drdIndex, + nodeType: NODE_TYPES.decisionService, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn, definitions), + "dc:Bounds": { + "@_x": __readonly_dropPoint.x, + "@_y": __readonly_dropPoint.y, + "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"], + "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"], + }, + }, + decisionServiceDividerLineWaypoint, + }); + + for (const decisionHref of __readonly_containedDecisionHrefsRelativeToThisDmn) { + const decisionShapeOnOtherDrd = + __readonly_indexedDrdContainingDecisionServiceDepiction.dmnShapesByHref.get(decisionHref); + if ( + decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined || + decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined || + decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined || + decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined + ) { + throw new Error( + `DMN MUTATION: Complete DMNShape for Decision with href ${decisionHref} should've existed on the indexed DRD.` + ); + } + + const x = + __readonly_dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] - dsShapeOnOtherDrd["dc:Bounds"]["@_x"]); + const y = + __readonly_dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] - dsShapeOnOtherDrd["dc:Bounds"]["@_y"]); + + const existingDecisionShape = __readonly_indexedDrd.dmnShapesByHref.get(decisionHref); + if (existingDecisionShape) { + repositionNode({ + definitions: definitions, + drdIndex: __readonly_drdIndex, + controlWaypointsByEdge: new Map(), + change: { + nodeType: NODE_TYPES.decision, + type: "absolute", + position: { x, y }, + shapeIndex: existingDecisionShape.index, + selectedEdges: [], + sourceEdgeIndexes: [], + targetEdgeIndexes: [], + }, + }); + } else { + const decisionNs = parseXmlHref(decisionHref).namespace; + const decisionDmnDefinitions = + !decisionNs || decisionNs === __readonly_namespace + ? definitions + : __readonly_externalDmnsIndex.get(decisionNs)?.model.definitions; + if (!decisionDmnDefinitions) { throw new Error( - `DMN MUTATION: Complete DMNShape for Decision with href ${decisionHref} should've existed on the indexed DRD.` + `DMN MUTATION: Can't find definitions for model with namespace ${__readonly_decisionServiceNamespace}` ); } - const x = dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] - dsShapeOnOtherDrd["dc:Bounds"]["@_x"]); - const y = dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] - dsShapeOnOtherDrd["dc:Bounds"]["@_y"]); - - const existingDecisionShape = thisDmnsIndexedDrd.dmnShapesByHref.get(decisionHref); - if (existingDecisionShape) { - repositionNode({ - definitions: thisDmnsDefinitions, - drdIndex, - controlWaypointsByEdge: new Map(), - change: { - nodeType: NODE_TYPES.decision, - type: "absolute", - position: { x, y }, - shapeIndex: existingDecisionShape.index, - selectedEdges: [], - sourceEdgeIndexes: [], - targetEdgeIndexes: [], - }, - }); - } else { - const decisionNs = parseXmlHref(decisionHref).namespace; - const decisionDmnDefinitions = - !decisionNs || decisionNs === thisDmnsNamespace - ? thisDmnsDefinitions - : externalDmnsIndex.get(decisionNs)?.model.definitions; - if (!decisionDmnDefinitions) { - throw new Error(`DMN MUTATION: Can't find definitions for model with namespace ${decisionServiceNamespace}`); - } - - addShape({ - definitions: thisDmnsDefinitions, - drdIndex, - nodeType: NODE_TYPES.decision, - shape: { - "@_dmnElementRef": xmlHrefToQName(decisionHref, thisDmnsDefinitions), - "dc:Bounds": { - "@_x": x, - "@_y": y, - "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"], - "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"], - }, + addShape({ + definitions: definitions, + drdIndex: __readonly_drdIndex, + nodeType: NODE_TYPES.decision, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": xmlHrefToQName(decisionHref, definitions), + "dc:Bounds": { + "@_x": x, + "@_y": y, + "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"], + "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"], }, - }); - } + }, + }); } } } @@ -229,7 +524,7 @@ export function getDecisionServicePropertiesRelativeToThisDmn({ }: { thisDmnsNamespace: string; decisionServiceNamespace: string; - decisionService: DMN15__tDecisionService; + decisionService: Normalized; }) { const decisionServiceNamespaceForHref = decisionServiceNamespace === thisDmnsNamespace ? "" : decisionServiceNamespace; diff --git a/packages/dmn-editor/src/mutations/addImport.ts b/packages/dmn-editor/src/mutations/addImport.ts index e0ebe5eca79..2e802dfafe1 100644 --- a/packages/dmn-editor/src/mutations/addImport.ts +++ b/packages/dmn-editor/src/mutations/addImport.ts @@ -19,12 +19,13 @@ import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { DMN15__tDefinitions, DMN15__tImport } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; export function addImport({ definitions, includedModel, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; includedModel: { name: string; namespace: string; @@ -40,7 +41,7 @@ export function addImport({ ? includedModel.normalizedPathRelativeToThisDmn // If the included model is located in a parent directory, we leave it that way because that is explicit enough already. : `./${includedModel.normalizedPathRelativeToThisDmn}`; // Always use this notation to make it explicit that we're using thisDmn's location as reference. - const newImport: DMN15__tImport = { + const newImport: Normalized = { "@_id": generateUuid(), "@_name": includedModel.name.trim(), "@_importType": includedModel.xmlns, diff --git a/packages/dmn-editor/src/mutations/addMissingImportNamespaces.ts b/packages/dmn-editor/src/mutations/addMissingImportNamespaces.ts index 198c07b0f98..253623ccb4c 100644 --- a/packages/dmn-editor/src/mutations/addMissingImportNamespaces.ts +++ b/packages/dmn-editor/src/mutations/addMissingImportNamespaces.ts @@ -18,8 +18,9 @@ */ import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; -export function addMissingImportNamespaces(definitions: DMN15__tDefinitions) { +export function addMissingImportNamespaces(definitions: Normalized) { if (definitions.import === undefined) { return; } diff --git a/packages/dmn-editor/src/mutations/addOrGetDrd.ts b/packages/dmn-editor/src/mutations/addOrGetDrd.ts index 2200bfaf35c..6cf19f7928c 100644 --- a/packages/dmn-editor/src/mutations/addOrGetDrd.ts +++ b/packages/dmn-editor/src/mutations/addOrGetDrd.ts @@ -19,13 +19,20 @@ import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; export function getDefaultDrdName({ drdIndex }: { drdIndex: number }) { return drdIndex === 0 ? "Default DRD" : "Unnamed DRD"; } -export function addOrGetDrd({ definitions, drdIndex }: { definitions: DMN15__tDefinitions; drdIndex: number }) { - const defaultName = getDefaultDrdName({ drdIndex }); +export function addOrGetDrd({ + definitions, + drdIndex, +}: { + definitions: Normalized; + drdIndex: number; +}) { + const drdName = getDefaultDrdName({ drdIndex: drdIndex }); // diagram definitions["dmndi:DMNDI"] ??= {}; @@ -34,7 +41,7 @@ export function addOrGetDrd({ definitions, drdIndex }: { definitions: DMN15__tDe const defaultDiagram = definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex]; defaultDiagram["@_id"] ??= generateUuid(); - defaultDiagram["@_name"] ??= defaultName; + defaultDiagram["@_name"] ??= drdName; defaultDiagram["@_useAlternativeInputDataShape"] ??= false; defaultDiagram["dmndi:DMNDiagramElement"] ??= []; diff --git a/packages/dmn-editor/src/mutations/addShape.ts b/packages/dmn-editor/src/mutations/addShape.ts index 92c8448fd18..fd716e34ae6 100644 --- a/packages/dmn-editor/src/mutations/addShape.ts +++ b/packages/dmn-editor/src/mutations/addShape.ts @@ -17,33 +17,43 @@ * under the License. */ -import { DMN15__tDefinitions, DMNDI15__DMNShape } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; -import { addOrGetDrd } from "./addOrGetDrd"; -import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; +import { + DC__Point, + DMN15__tDefinitions, + DMNDI15__DMNDecisionServiceDividerLine, + DMNDI15__DMNShape, +} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { NodeType } from "../diagram/connections/graphStructure"; import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import { Normalized } from "../normalization/normalize"; +import { addOrGetDrd } from "./addOrGetDrd"; import { getCentralizedDecisionServiceDividerLine } from "./updateDecisionServiceDividerLine"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function addShape({ definitions, drdIndex, nodeType, shape, + decisionServiceDividerLineWaypoint: decisionServiceDividerLineWaypoint, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; nodeType: NodeType; - shape: WithoutIdXmlAttributes; + shape: Normalized; + decisionServiceDividerLineWaypoint?: DC__Point[]; }) { const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); diagramElements.push({ __$$element: "dmndi:DMNShape", - "@_id": generateUuid(), ...(nodeType === NODE_TYPES.decisionService - ? { "dmndi:DMNDecisionServiceDividerLine": getCentralizedDecisionServiceDividerLine(shape["dc:Bounds"]!) } + ? { + "dmndi:DMNDecisionServiceDividerLine": + decisionServiceDividerLineWaypoint !== undefined + ? { "@_id": generateUuid(), "di:waypoint": [...decisionServiceDividerLineWaypoint] } + : getCentralizedDecisionServiceDividerLine(shape["dc:Bounds"]!), + } : {}), ...shape, }); } - -export type WithoutIdXmlAttributes = { [K in keyof T]: K extends "@_id" ? never : WithoutIdXmlAttributes }; diff --git a/packages/dmn-editor/src/mutations/addStandaloneNode.ts b/packages/dmn-editor/src/mutations/addStandaloneNode.ts index 607ab2d1950..2b348f07def 100644 --- a/packages/dmn-editor/src/mutations/addStandaloneNode.ts +++ b/packages/dmn-editor/src/mutations/addStandaloneNode.ts @@ -27,13 +27,14 @@ import { addOrGetDrd as getDefaultDiagram } from "./addOrGetDrd"; import { getCentralizedDecisionServiceDividerLine } from "./updateDecisionServiceDividerLine"; import { repopulateInputDataAndDecisionsOnAllDecisionServices } from "./repopulateInputDataAndDecisionsOnDecisionService"; import { buildXmlHref } from "../xml/xmlHrefs"; +import { Normalized } from "../normalization/normalize"; export function addStandaloneNode({ definitions, drdIndex, newNode, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; newNode: { type: NodeType; bounds: DC__Bounds }; }) { @@ -44,7 +45,7 @@ export function addStandaloneNode({ definitions.drgElement ??= []; const variableBase = { "@_id": generateUuid(), - "@_typeRef": DmnBuiltInDataType.Undefined, + "@_typeRef": undefined, }; definitions.drgElement?.push( switchExpression(newNode.type as Exclude, { @@ -120,13 +121,15 @@ export function addStandaloneNode({ const shapeId = generateUuid(); diagramElements?.push({ __$$element: "dmndi:DMNShape", - "@_id": shapeId, + "@_id": shapeId, // FIXME: Tiago --> This should break if removed. "@_dmnElementRef": newNodeId, "@_isCollapsed": false, "@_isListedInputData": false, "dc:Bounds": newNode.bounds, ...(newNode.type === NODE_TYPES.decisionService - ? { "dmndi:DMNDecisionServiceDividerLine": getCentralizedDecisionServiceDividerLine(newNode.bounds) } + ? { + "dmndi:DMNDecisionServiceDividerLine": getCentralizedDecisionServiceDividerLine(newNode.bounds), + } : {}), }); diff --git a/packages/dmn-editor/src/mutations/addTopLevelItemDefinition.ts b/packages/dmn-editor/src/mutations/addTopLevelItemDefinition.ts index 9111bde3a88..f1ab338af15 100644 --- a/packages/dmn-editor/src/mutations/addTopLevelItemDefinition.ts +++ b/packages/dmn-editor/src/mutations/addTopLevelItemDefinition.ts @@ -22,13 +22,14 @@ import { DMN15__tItemDefinition, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { getNewItemDefinition } from "../dataTypes/DataTypeSpec"; +import { Normalized } from "../normalization/normalize"; export function addTopLevelItemDefinition({ definitions, partial, }: { - definitions: DMN15__tDefinitions; - partial?: Partial; + definitions: Normalized; + partial?: Partial>; }) { const newItemDefinition = getNewItemDefinition(partial); definitions.itemDefinition ??= []; diff --git a/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts b/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts new file mode 100644 index 00000000000..0f3786a1b83 --- /dev/null +++ b/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts @@ -0,0 +1,225 @@ +/* + * 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. + */ + +import { DMNDI15__DMNShape } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { XmlQName } from "@kie-tools/xml-parser-ts/dist/qNames"; +import * as Elk from "elkjs/lib/elk.bundled.js"; +import * as RF from "reactflow"; +import { EdgeType, NodeType } from "../diagram/connections/graphStructure"; +import { PositionalNodeHandleId } from "../diagram/connections/PositionalNodeHandles"; +import { DmnDiagramEdgeData } from "../diagram/edges/Edges"; +import { DmnDiagramNodeData } from "../diagram/nodes/Nodes"; +import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import { addEdge } from "../mutations/addEdge"; +import { repositionNode } from "../mutations/repositionNode"; +import { resizeNode } from "../mutations/resizeNode"; +import { updateDecisionServiceDividerLine } from "../mutations/updateDecisionServiceDividerLine"; +import { Normalized } from "../normalization/normalize"; +import { State } from "../store/Store"; +import { AutolayoutParentNode, FAKE_MARKER, visitNodeAndNested } from "../autolayout/autoLayoutInfo"; +import { ExternalDmnsIndex } from "../DmnEditor"; + +export function applyAutoLayoutToDrd({ + state, + __readonly_autoLayoutedInfo, + __readonly_parentNodesById, + __readonly_nodesById, + __readonly_edgesById, + __readonly_edges, + __readonly_dmnShapesByHref, + __readonly_drdIndex, + __readonly_dmnObjectNamespace, + __readonly_externalDmnsIndex, +}: { + state: State; + __readonly_autoLayoutedInfo: { + isHorizontal: boolean; + nodes: Elk.ElkNode[] | undefined; + edges: Elk.ElkExtendedEdge[] | undefined; + }; + __readonly_parentNodesById: Map; + __readonly_nodesById: Map>; + __readonly_edgesById: Map>; + __readonly_edges: RF.Edge[]; + __readonly_dmnShapesByHref: Map< + string, + Normalized & { + index: number; + dmnElementRefQName: XmlQName; + } + >; + __readonly_drdIndex: number; + __readonly_dmnObjectNamespace: string | undefined; + __readonly_externalDmnsIndex: ExternalDmnsIndex; +}) { + // 7. Update all nodes positions skipping empty groups, which will be positioned manually after all nodes are done being repositioned. + const autolayoutedElkNodesById = new Map(); + + for (const topLevelElkNode of __readonly_autoLayoutedInfo.nodes ?? []) { + visitNodeAndNested(topLevelElkNode, { x: 100, y: 100 }, (elkNode, positionOffset) => { + if (elkNode.id.includes(FAKE_MARKER)) { + return; + } + + autolayoutedElkNodesById.set(elkNode.id, elkNode); + + const nodeId = elkNode.id; + const node = __readonly_nodesById.get(nodeId)!; + + repositionNode({ + definitions: state.dmn.model.definitions, + drdIndex: __readonly_drdIndex, + controlWaypointsByEdge: new Map(), + change: { + nodeType: node.type as NodeType, + type: "absolute", + position: { + x: elkNode.x! + positionOffset.x, + y: elkNode.y! + positionOffset.y, + }, + selectedEdges: [...__readonly_edgesById.keys()], + shapeIndex: node.data?.shape.index, + sourceEdgeIndexes: __readonly_edges.flatMap((e) => + e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] + ), + targetEdgeIndexes: __readonly_edges.flatMap((e) => + e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] + ), + }, + }); + }); + } + + // 8. Resize all nodes using the sizes calculated by ELK. + for (const topLevelElkNode of __readonly_autoLayoutedInfo.nodes ?? []) { + visitNodeAndNested(topLevelElkNode, { x: 0, y: 0 }, (elkNode) => { + if (elkNode.id.includes(FAKE_MARKER)) { + return; + } + + const nodeId = elkNode.id; + const node = __readonly_nodesById.get(nodeId)!; + + resizeNode({ + definitions: state.dmn.model.definitions, + drdIndex: __readonly_drdIndex, + __readonly_dmnShapesByHref: __readonly_dmnShapesByHref, + snapGrid: state.diagram.snapGrid, + __readonly_dmnObjectNamespace, + __readonly_externalDmnsIndex, + change: { + index: node.data.index, + isExternal: !!node.data.dmnObjectQName.prefix, + nodeType: node.type as NodeType, + dimension: { + "@_width": elkNode.width!, + "@_height": elkNode.height!, + }, + shapeIndex: node.data?.shape.index, + sourceEdgeIndexes: __readonly_edges.flatMap((e) => + e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] + ), + targetEdgeIndexes: __readonly_edges.flatMap((e) => + e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] : [] + ), + }, + }); + }); + } + + // 9. Updating Decision Service divider lines after all nodes are repositioned and resized. + for (const [parentNodeId] of __readonly_parentNodesById) { + const parentNode = __readonly_nodesById.get(parentNodeId); + if (parentNode?.type !== NODE_TYPES.decisionService) { + continue; + } + + const elkNode = autolayoutedElkNodesById.get(parentNodeId); + if (!elkNode) { + throw new Error(`Couldn't find Decision Service with id ${parentNode.id} at the autolayouted nodes map`); + } + + /** + * The second children of a Decision Service elkNode is a node representing the Encapsulated section. + * It's Y position will be exactly where the divider line should be. + */ + const dividerLinerLocalYPosition = elkNode.children?.[1]?.y; + if (!dividerLinerLocalYPosition) { + throw new Error( + `Couldn't find second child (which represents the Encapuslated Decision section) of Decision Service with id ${parentNode.id} at the autolayouted nodes map` + ); + } + + updateDecisionServiceDividerLine({ + definitions: state.dmn.model.definitions, + drdIndex: __readonly_drdIndex, + __readonly_dmnShapesByHref, + __readonly_dmnObjectNamespace, + __readonly_externalDmnsIndex, + drgElementIndex: parentNode.data.index, + shapeIndex: parentNode.data.shape.index, + snapGrid: state.diagram.snapGrid, + localYPosition: dividerLinerLocalYPosition, + }); + } + + // 10. Update the edges. Edges always go from top to bottom, removing waypoints. + for (const elkEdge of __readonly_autoLayoutedInfo.edges ?? []) { + if (elkEdge.id.includes(FAKE_MARKER)) { + continue; + } + + const edge = __readonly_edgesById.get(elkEdge.id)!; + + const sourceNode = __readonly_nodesById.get(elkEdge.sources[0])!; + const targetNode = __readonly_nodesById.get(elkEdge.targets[0])!; + + // If the target is an external node, we don't have to create the edge. + if (targetNode.data.dmnObjectQName.prefix) { + continue; + } + + addEdge({ + definitions: state.dmn.model.definitions, + drdIndex: __readonly_drdIndex, + edge: { + autoPositionedEdgeMarker: undefined, + type: edge.type as EdgeType, + targetHandle: PositionalNodeHandleId.Bottom, + sourceHandle: PositionalNodeHandleId.Top, + }, + sourceNode: { + type: sourceNode.type as NodeType, + href: sourceNode.id, + data: sourceNode.data, + bounds: sourceNode.data.shape["dc:Bounds"]!, + shapeId: sourceNode.data.shape["@_id"], + }, + targetNode: { + type: targetNode.type as NodeType, + href: targetNode.id, + data: targetNode.data, + bounds: targetNode.data.shape["dc:Bounds"]!, + index: targetNode.data.index, + shapeId: targetNode.data.shape["@_id"], + }, + keepWaypoints: false, + }); + } +} diff --git a/packages/dmn-editor/src/mutations/deleteDecisionFromDecisionService.ts b/packages/dmn-editor/src/mutations/deleteDecisionFromDecisionService.ts index a22b078e334..cb9e1273299 100644 --- a/packages/dmn-editor/src/mutations/deleteDecisionFromDecisionService.ts +++ b/packages/dmn-editor/src/mutations/deleteDecisionFromDecisionService.ts @@ -19,13 +19,14 @@ import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { repopulateInputDataAndDecisionsOnDecisionService } from "./repopulateInputDataAndDecisionsOnDecisionService"; +import { Normalized } from "../normalization/normalize"; export function deleteDecisionFromDecisionService({ definitions, decisionId, decisionServiceId, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; decisionId: string; decisionServiceId: string; }) { diff --git a/packages/dmn-editor/src/mutations/deleteEdge.ts b/packages/dmn-editor/src/mutations/deleteEdge.ts index 4399438ddd0..3cc43c8126c 100644 --- a/packages/dmn-editor/src/mutations/deleteEdge.ts +++ b/packages/dmn-editor/src/mutations/deleteEdge.ts @@ -26,6 +26,7 @@ import { import { addOrGetDrd } from "./addOrGetDrd"; import { DmnDiagramEdgeData } from "../diagram/edges/Edges"; import { repopulateInputDataAndDecisionsOnAllDecisionServices } from "./repopulateInputDataAndDecisionsOnDecisionService"; +import { Normalized } from "../normalization/normalize"; export enum EdgeDeletionMode { FROM_DRG_AND_ALL_DRDS, @@ -38,7 +39,7 @@ export function deleteEdge({ edge, mode, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; edge: { id: string; dmnObject: DmnDiagramEdgeData["dmnObject"] }; mode: EdgeDeletionMode; @@ -48,7 +49,7 @@ export function deleteEdge({ return { dmnEdge: undefined }; } - const dmnObjects: DMN15__tDefinitions["drgElement" | "artifact"] = + const dmnObjects: Normalized["drgElement" | "artifact"] = switchExpression(edge?.dmnObject.type, { association: definitions.artifact, group: definitions.artifact, @@ -64,9 +65,9 @@ export function deleteEdge({ const requirements = switchExpression(edge?.dmnObject.requirementType, { // Casting to DMN15__tDecision because if has all types of requirement, but not necessarily that's true. - informationRequirement: (dmnObjects[dmnObjectIndex] as DMN15__tDecision).informationRequirement, - knowledgeRequirement: (dmnObjects[dmnObjectIndex] as DMN15__tDecision).knowledgeRequirement, - authorityRequirement: (dmnObjects[dmnObjectIndex] as DMN15__tDecision).authorityRequirement, + informationRequirement: (dmnObjects[dmnObjectIndex] as Normalized).informationRequirement, + knowledgeRequirement: (dmnObjects[dmnObjectIndex] as Normalized).knowledgeRequirement, + authorityRequirement: (dmnObjects[dmnObjectIndex] as Normalized).authorityRequirement, association: dmnObjects, }) ?? []; @@ -78,7 +79,7 @@ export function deleteEdge({ } // Deleting the DMNEdge's - let deletedDmnEdgeOnCurrentDrd: DMNDI15__DMNEdge | undefined; + let deletedDmnEdgeOnCurrentDrd: Normalized | undefined; const drdCount = (definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? []).length; for (let i = 0; i < drdCount; i++) { diff --git a/packages/dmn-editor/src/mutations/deleteEdgeWaypoint.ts b/packages/dmn-editor/src/mutations/deleteEdgeWaypoint.ts index cf275118fe3..ca03b38b03f 100644 --- a/packages/dmn-editor/src/mutations/deleteEdgeWaypoint.ts +++ b/packages/dmn-editor/src/mutations/deleteEdgeWaypoint.ts @@ -19,6 +19,7 @@ import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { addOrGetDrd } from "./addOrGetDrd"; +import { Normalized } from "../normalization/normalize"; export function deleteEdgeWaypoint({ definitions, @@ -26,7 +27,7 @@ export function deleteEdgeWaypoint({ edgeIndex, waypointIndex, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; edgeIndex: number; waypointIndex: number; diff --git a/packages/dmn-editor/src/mutations/deleteImport.ts b/packages/dmn-editor/src/mutations/deleteImport.ts index b934cb61e04..34aa40bcc7a 100644 --- a/packages/dmn-editor/src/mutations/deleteImport.ts +++ b/packages/dmn-editor/src/mutations/deleteImport.ts @@ -19,15 +19,69 @@ import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { getXmlNamespaceDeclarationName } from "../xml/xmlNamespaceDeclarations"; +import { Normalized } from "../normalization/normalize"; +import { computeDiagramData } from "../store/computed/computeDiagramData"; +import { deleteNode, NodeDeletionMode } from "./deleteNode"; +import { nodeNatures } from "./NodeNature"; +import { NodeType } from "../diagram/connections/graphStructure"; +import { deleteEdge, EdgeDeletionMode } from "./deleteEdge"; +import { computeIndexedDrd } from "../store/computed/computeIndexes"; +import { Computed, defaultStaticState } from "../store/Store"; +import { TypeOrReturnType } from "../store/ComputedStateCache"; -export function deleteImport({ definitions, index }: { definitions: DMN15__tDefinitions; index: number }) { +export function deleteImport({ + definitions, + __readonly_index, + __readonly_externalModelTypesByNamespace, +}: { + definitions: Normalized; + __readonly_index: number; + __readonly_externalModelTypesByNamespace: TypeOrReturnType; +}) { definitions.import ??= []; - const [deleted] = definitions.import.splice(index, 1); + const [deletedImport] = definitions.import.splice(__readonly_index, 1); const namespaceName = getXmlNamespaceDeclarationName({ rootElement: definitions, - namespace: deleted["@_namespace"], + namespace: deletedImport["@_namespace"], }); + + // Delete from all DRDs + const defaultDiagram = defaultStaticState().diagram; + definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.forEach((_, i) => { + const indexedDrd = computeIndexedDrd(definitions["@_namespace"], definitions, i); + const { externalNodesByNamespace, drgEdges, edgesFromExternalNodesByNamespace } = computeDiagramData( + defaultDiagram, + definitions, + __readonly_externalModelTypesByNamespace, + indexedDrd, + false + ); + + externalNodesByNamespace.get(deletedImport["@_namespace"])?.forEach((node) => { + deleteNode({ + definitions, + __readonly_drgEdges: drgEdges, + __readonly_drdIndex: 0, + __readonly_nodeNature: nodeNatures[node.type! as NodeType], + __readonly_dmnObjectId: node.data.dmnObject?.["@_id"], + __readonly_dmnObjectQName: node.data.dmnObjectQName, + __readonly_dmnObjectNamespace: node.data.dmnObjectNamespace!, + __readonly_externalModelTypesByNamespace, + mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS, + }); + }); + + edgesFromExternalNodesByNamespace.get(deletedImport["@_namespace"])?.forEach((edge) => { + deleteEdge({ + definitions, + drdIndex: 0, + edge: { id: edge.id, dmnObject: edge.data!.dmnObject }, + mode: EdgeDeletionMode.FROM_DRG_AND_ALL_DRDS, + }); + }); + }); + if (namespaceName) { delete definitions[`@_xmlns:${namespaceName}`]; } diff --git a/packages/dmn-editor/src/mutations/deleteNode.ts b/packages/dmn-editor/src/mutations/deleteNode.ts index a3f07d7819a..5f4affe9fa4 100644 --- a/packages/dmn-editor/src/mutations/deleteNode.ts +++ b/packages/dmn-editor/src/mutations/deleteNode.ts @@ -30,6 +30,8 @@ import { EdgeDeletionMode, deleteEdge } from "./deleteEdge"; import { Computed } from "../store/Store"; import { computeContainingDecisionServiceHrefsByDecisionHrefs } from "../store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts"; import { xmlHrefToQName } from "../xml/xmlHrefToQName"; +import { Normalized } from "../normalization/normalize"; +import { NodeDmnObjects } from "../diagram/nodes/Nodes"; export enum NodeDeletionMode { FROM_DRG_AND_ALL_DRDS, @@ -38,36 +40,36 @@ export enum NodeDeletionMode { export function deleteNode({ definitions, - drgEdges, - drdIndex, - nodeNature, - dmnObjectId, - dmnObjectQName, - dmnObjectNamespace, - externalDmnsIndex, + __readonly_drgEdges, + __readonly_drdIndex, + __readonly_nodeNature, + __readonly_dmnObjectId, + __readonly_dmnObjectNamespace, + __readonly_dmnObjectQName, + __readonly_externalModelTypesByNamespace, mode, }: { - definitions: DMN15__tDefinitions; - drgEdges: DrgEdge[]; - drdIndex: number; - nodeNature: NodeNature; - externalDmnsIndex: ReturnType["dmns"]; - dmnObjectNamespace: string; - dmnObjectId: string | undefined; - dmnObjectQName: XmlQName; + definitions: Normalized; + __readonly_drgEdges: DrgEdge[]; + __readonly_drdIndex: number; + __readonly_nodeNature: NodeNature; + __readonly_externalModelTypesByNamespace: ReturnType; + __readonly_dmnObjectId: string | undefined; + __readonly_dmnObjectNamespace: string; + __readonly_dmnObjectQName: XmlQName; mode: NodeDeletionMode; }): { - deletedDmnObject: Unpacked | undefined; - deletedDmnShapeOnCurrentDrd: DMNDI15__DMNShape | undefined; + deletedDmnObject: Unpacked["drgElement" | "artifact"]> | undefined; + deletedDmnShapeOnCurrentDrd: Normalized | undefined; } { if ( mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY && !canRemoveNodeFromDrdOnly({ definitions, - drdIndex, - dmnObjectNamespace, - dmnObjectId, - externalDmnsIndex, + __readonly_drdIndex, + __readonly_dmnObjectNamespace, + __readonly_dmnObjectId, + __readonly_externalDmnsIndex: __readonly_externalModelTypesByNamespace.dmns, }) ) { console.warn("DMN MUTATION: Cannot hide a Decision that's contained by a Decision Service from a DRD."); @@ -77,14 +79,18 @@ export function deleteNode({ if (mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS) { // Delete Edges // A DRD doesn't necessarily renders all edges of the DRG, so we need to look for what DRG edges to delete when deleting a node from any DRD. - const nodeId = buildXmlHref({ namespace: dmnObjectNamespace, id: dmnObjectId! }); - for (let i = 0; i < drgEdges.length; i++) { - const drgEdge = drgEdges[i]; + const nodeId = buildXmlHref({ + namespace: + __readonly_dmnObjectNamespace === definitions["@_namespace"] ? undefined : __readonly_dmnObjectNamespace, + id: __readonly_dmnObjectId!, + }); + for (let i = 0; i < __readonly_drgEdges.length; i++) { + const drgEdge = __readonly_drgEdges[i]; // Only delete edges that end at or start from the node being deleted. if (drgEdge.sourceId === nodeId || drgEdge.targetId === nodeId) { deleteEdge({ definitions, - drdIndex, + drdIndex: __readonly_drdIndex, mode: EdgeDeletionMode.FROM_DRG_AND_ALL_DRDS, edge: { id: drgEdge.id, @@ -107,56 +113,61 @@ export function deleteNode({ } } - let dmnObject: Unpacked | undefined; + let deletedDmnObject: Unpacked["drgElement" | "artifact"]> | undefined; // External or unknown nodes don't have a dmnObject associated with it, just the shape.. - if (!dmnObjectQName.prefix) { + if (!__readonly_dmnObjectQName.prefix) { // Delete the dmnObject itself - if (nodeNature === NodeNature.ARTIFACT) { + if (__readonly_nodeNature === NodeNature.ARTIFACT) { if (mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS) { - const nodeIndex = (definitions.artifact ?? []).findIndex((a) => a["@_id"] === dmnObjectId); - dmnObject = definitions.artifact?.splice(nodeIndex, 1)?.[0]; + const nodeIndex = (definitions.artifact ?? []).findIndex((a) => a["@_id"] === __readonly_dmnObjectId); + deletedDmnObject = definitions.artifact?.splice(nodeIndex, 1)?.[0]; } else { throw new Error(`DMN MUTATION: Can't hide an artifact node.`); } - } else if (nodeNature === NodeNature.DRG_ELEMENT) { - const nodeIndex = (definitions.drgElement ?? []).findIndex((d) => d["@_id"] === dmnObjectId); - dmnObject = + } else if (__readonly_nodeNature === NodeNature.DRG_ELEMENT) { + const nodeIndex = (definitions.drgElement ?? []).findIndex((d) => d["@_id"] === __readonly_dmnObjectId); + deletedDmnObject = mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS ? definitions.drgElement?.splice(nodeIndex, 1)?.[0] : definitions.drgElement?.[nodeIndex]; - } else if (nodeNature === NodeNature.UNKNOWN) { + } else if (__readonly_nodeNature === NodeNature.UNKNOWN) { // Ignore. There's no dmnObject here. } else { - throw new Error(`DMN MUTATION: Unknown node nature '${nodeNature}'.`); + throw new Error(`DMN MUTATION: Unknown node nature '${__readonly_nodeNature}'.`); } - if (!dmnObject) { - throw new Error(`DMN MUTATION: Can't delete DMN object that doesn't exist: ID=${dmnObjectId}`); + if (!deletedDmnObject && __readonly_nodeNature !== NodeNature.UNKNOWN) { + /** + * We do not want to throw error in case of `nodeNature` equals to `NodeNature.UNKNOWN`. + * In such scenario it is expected `dmnObject` is undefined as we can not pair `dmnObject` with the `DMNShape`. + * However we are still able to delete at least the selected `DMNShape` from the diagram. + */ + throw new Error(`DMN MUTATION: Can't delete DMN object that doesn't exist: ID=${__readonly_dmnObjectId}`); } } - const shapeDmnElementRef = buildXmlQName(dmnObjectQName); + const shapeDmnElementRef = buildXmlQName(__readonly_dmnObjectQName); // Deleting the DMNShape's - let deletedDmnShapeOnCurrentDrd: DMNDI15__DMNShape | undefined; + let deletedDmnShapeOnCurrentDrd: Normalized | undefined; - const deletedIdsOnDmnObjectTree = dmnObject + const deletedIdsOnDmnObjectTree = deletedDmnObject ? getNewDmnIdRandomizer() - .ack({ json: [dmnObject], type: "DMN15__tDefinitions", attr: "drgElement" }) + .ack({ json: [deletedDmnObject], type: "DMN15__tDefinitions", attr: "drgElement" }) .getOriginalIds() : new Set(); const drdCount = (definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? []).length; for (let i = 0; i < drdCount; i++) { - if (mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY && i !== drdIndex) { + if (mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY && i !== __readonly_drdIndex) { continue; } const { diagramElements, widthsExtension } = addOrGetDrd({ definitions, drdIndex: i }); const dmnShapeIndex = (diagramElements ?? []).findIndex((d) => d["@_dmnElementRef"] === shapeDmnElementRef); if (dmnShapeIndex >= 0) { - if (i === drdIndex) { + if (i === __readonly_drdIndex) { deletedDmnShapeOnCurrentDrd = diagramElements[dmnShapeIndex]; } @@ -172,40 +183,40 @@ export function deleteNode({ repopulateInputDataAndDecisionsOnAllDecisionServices({ definitions }); return { - deletedDmnObject: mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS ? dmnObject : undefined, + deletedDmnObject: mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS ? deletedDmnObject : undefined, deletedDmnShapeOnCurrentDrd, }; } export function canRemoveNodeFromDrdOnly({ definitions, - drdIndex, - dmnObjectNamespace, - dmnObjectId, - externalDmnsIndex, + __readonly_drdIndex, + __readonly_dmnObjectNamespace, + __readonly_dmnObjectId, + __readonly_externalDmnsIndex, }: { - dmnObjectNamespace: string; - dmnObjectId: string | undefined; - definitions: DMN15__tDefinitions; - drdIndex: number; - externalDmnsIndex: ReturnType["dmns"]; + definitions: Normalized; + __readonly_dmnObjectNamespace: string; + __readonly_dmnObjectId: string | undefined; + __readonly_drdIndex: number; + __readonly_externalDmnsIndex: ReturnType["dmns"]; }) { - const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); + const { diagramElements } = addOrGetDrd({ definitions, drdIndex: __readonly_drdIndex }); const dmnObjectHref = buildXmlHref({ - namespace: dmnObjectNamespace === definitions["@_namespace"] ? "" : dmnObjectNamespace, - id: dmnObjectId!, + namespace: __readonly_dmnObjectNamespace === definitions["@_namespace"] ? undefined : __readonly_dmnObjectNamespace, + id: __readonly_dmnObjectId!, }); const drgElements = - definitions["@_namespace"] === dmnObjectNamespace + definitions["@_namespace"] === __readonly_dmnObjectNamespace ? definitions.drgElement ?? [] - : externalDmnsIndex.get(dmnObjectNamespace)?.model.definitions.drgElement ?? []; + : __readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace)?.model.definitions.drgElement ?? []; const containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn = computeContainingDecisionServiceHrefsByDecisionHrefs({ thisDmnsNamespace: definitions["@_namespace"], - drgElementsNamespace: dmnObjectNamespace, + drgElementsNamespace: __readonly_dmnObjectNamespace, drgElements, }); diff --git a/packages/dmn-editor/src/mutations/renameImport.ts b/packages/dmn-editor/src/mutations/renameImport.ts index 08dc479d1f2..5b2821f18fd 100644 --- a/packages/dmn-editor/src/mutations/renameImport.ts +++ b/packages/dmn-editor/src/mutations/renameImport.ts @@ -31,6 +31,7 @@ import { buildFeelQName, parseFeelQName } from "../feel/parseFeelQName"; import { DataTypeIndex } from "../dataTypes/DataTypes"; import { DMN15__tContext } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { DMN15_SPEC } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; +import { Normalized } from "../normalization/normalize"; export function renameImport({ definitions, @@ -38,7 +39,7 @@ export function renameImport({ allTopLevelDataTypesByFeelName, index, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; allTopLevelDataTypesByFeelName: DataTypeIndex; newName: string; index: number; @@ -85,13 +86,15 @@ export function renameImport({ if (element.__$$element === "decision" || element.__$$element === "businessKnowledgeModel") { traverseExpressionsInExpressionHolders(element, (expression, __$$element) => { if (__$$element === "functionDefinition") { - const e = expression as DMN15__tFunctionDefinition; + const e = expression as Normalized; if (e["@_kind"] === "PMML") { - const pmmlDocument = (e.expression as DMN15__tContext).contextEntry?.find( + const pmmlDocument = (e.expression as Normalized).contextEntry?.find( ({ variable }) => variable?.["@_name"] === DMN15_SPEC.BOXED.FUNCTION.PMML.documentFieldName ); - const pmmlDocumentLiteralExpression = pmmlDocument?.expression as DMN15__tLiteralExpression | undefined; + const pmmlDocumentLiteralExpression = pmmlDocument?.expression as + | Normalized + | undefined; if (pmmlDocumentLiteralExpression?.text?.__$$text === _import["@_name"]) { pmmlDocumentLiteralExpression.text = { __$$text: trimmedNewName }; } diff --git a/packages/dmn-editor/src/mutations/renameItemDefinition.ts b/packages/dmn-editor/src/mutations/renameItemDefinition.ts index e18ce3df4a6..8de71e9be32 100644 --- a/packages/dmn-editor/src/mutations/renameItemDefinition.ts +++ b/packages/dmn-editor/src/mutations/renameItemDefinition.ts @@ -24,6 +24,7 @@ import { traverseTypeRefedInExpressionHolders, } from "../dataTypes/DataTypeSpec"; import { DataTypeIndex } from "../dataTypes/DataTypes"; +import { Normalized } from "../normalization/normalize"; export function renameItemDefinition({ definitions, @@ -31,7 +32,7 @@ export function renameItemDefinition({ allDataTypesById, itemDefinitionId, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; newName: string; itemDefinitionId: string; allDataTypesById: DataTypeIndex; diff --git a/packages/dmn-editor/src/mutations/renameNode.ts b/packages/dmn-editor/src/mutations/renameNode.ts index 8ca0465da43..bc15f5246c6 100644 --- a/packages/dmn-editor/src/mutations/renameNode.ts +++ b/packages/dmn-editor/src/mutations/renameNode.ts @@ -22,13 +22,15 @@ import { DMN15__tGroup, DMN15__tTextAnnotation, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function renameDrgElement({ definitions, newName, index, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; newName: string; index: number; }) { @@ -39,7 +41,7 @@ export function renameDrgElement({ drgElement["@_name"] = trimmedNewName; if (drgElement.__$$element !== "knowledgeSource") { - drgElement.variable ??= { "@_name": trimmedNewName }; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": trimmedNewName }; drgElement.variable!["@_name"] = trimmedNewName; } @@ -59,11 +61,11 @@ export function renameGroupNode({ newName, index, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; newName: string; index: number; }) { - (definitions.artifact![index] as DMN15__tGroup)["@_name"] = newName; + (definitions.artifact![index] as Normalized)["@_name"] = newName; } export function updateTextAnnotation({ @@ -71,9 +73,9 @@ export function updateTextAnnotation({ newText, index, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; newText: string; index: number; }) { - (definitions.artifact![index] as DMN15__tTextAnnotation).text = { __$$text: newText }; + (definitions.artifact![index] as Normalized).text = { __$$text: newText }; } diff --git a/packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts b/packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts index c3fce874cd1..3b5bb781e1b 100644 --- a/packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts +++ b/packages/dmn-editor/src/mutations/repopulateInputDataAndDecisionsOnDecisionService.ts @@ -21,11 +21,12 @@ import { DMN15__tDecisionService, DMN15__tDefinitions, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; export function repopulateInputDataAndDecisionsOnAllDecisionServices({ definitions, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; }) { for (let i = 0; i < (definitions.drgElement ?? []).length; i++) { const drgElement = definitions.drgElement![i]; @@ -42,8 +43,8 @@ export function repopulateInputDataAndDecisionsOnDecisionService({ definitions, decisionService, }: { - definitions: DMN15__tDefinitions; - decisionService: DMN15__tDecisionService; + definitions: Normalized; + decisionService: Normalized; }) { // Save previous values to preserve order const inputDatas = new Set([...(decisionService.inputData ?? [])].map((e) => e["@_href"])); // Using Set for uniqueness diff --git a/packages/dmn-editor/src/mutations/repositionEdgeWaypoint.ts b/packages/dmn-editor/src/mutations/repositionEdgeWaypoint.ts index cceaa1377ac..c577b2f4926 100644 --- a/packages/dmn-editor/src/mutations/repositionEdgeWaypoint.ts +++ b/packages/dmn-editor/src/mutations/repositionEdgeWaypoint.ts @@ -19,6 +19,7 @@ import { DC__Point, DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { addOrGetDrd } from "./addOrGetDrd"; +import { Normalized } from "../normalization/normalize"; export function repositionEdgeWaypoint({ definitions, @@ -27,7 +28,7 @@ export function repositionEdgeWaypoint({ waypointIndex, waypoint, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; edgeIndex: number; waypointIndex: number; diff --git a/packages/dmn-editor/src/mutations/repositionNode.ts b/packages/dmn-editor/src/mutations/repositionNode.ts index bc66387cdc6..2e27f1497f8 100644 --- a/packages/dmn-editor/src/mutations/repositionNode.ts +++ b/packages/dmn-editor/src/mutations/repositionNode.ts @@ -27,6 +27,7 @@ import { NodeType } from "../diagram/connections/graphStructure"; import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; import { addOrGetDrd } from "./addOrGetDrd"; import { getCentralizedDecisionServiceDividerLine } from "./updateDecisionServiceDividerLine"; +import { Normalized } from "../normalization/normalize"; export function repositionNode({ definitions, @@ -39,7 +40,7 @@ export function repositionNode({ * This will make sure we only move edges once, even though they might be source/target edges for multiple nodes. */ controlWaypointsByEdge: Map>; - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; change: { nodeType: NodeType; @@ -60,7 +61,7 @@ export function repositionNode({ }) { const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); - const shape = diagramElements?.[change.shapeIndex] as DMNDI15__DMNShape | undefined; + const shape = diagramElements?.[change.shapeIndex] as Normalized | undefined; const shapeBounds = shape?.["dc:Bounds"]; if (!shapeBounds) { throw new Error("DMN MUTATION: Cannot reposition non-existent shape bounds"); @@ -84,7 +85,7 @@ export function repositionNode({ const offsetEdges = (args: { edgeIndexes: number[]; waypoint: "last" | "first" }) => { for (const edgeIndex of args.edgeIndexes) { - const edge = diagramElements[edgeIndex] as DMNDI15__DMNEdge | undefined; + const edge = diagramElements[edgeIndex] as Normalized | undefined; if (!edge || !edge["di:waypoint"]) { throw new Error("DMN MUTATION: Cannot reposition non-existent edge"); } diff --git a/packages/dmn-editor/src/mutations/resizeNode.ts b/packages/dmn-editor/src/mutations/resizeNode.ts index bd6e3cafe7d..ee991a1cc90 100644 --- a/packages/dmn-editor/src/mutations/resizeNode.ts +++ b/packages/dmn-editor/src/mutations/resizeNode.ts @@ -33,18 +33,24 @@ import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; import { SnapGrid } from "../store/Store"; import { addOrGetDrd } from "./addOrGetDrd"; import { DECISION_SERVICE_DIVIDER_LINE_PADDING } from "./updateDecisionServiceDividerLine"; +import { Normalized } from "../normalization/normalize"; +import { ExternalDmnsIndex } from "../DmnEditor"; export function resizeNode({ definitions, drdIndex, - dmnShapesByHref, + __readonly_dmnShapesByHref, + __readonly_dmnObjectNamespace, + __readonly_externalDmnsIndex, snapGrid, change, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; - dmnShapesByHref: Map; + __readonly_dmnShapesByHref: Map & { index: number }>; snapGrid: SnapGrid; + __readonly_dmnObjectNamespace: string | undefined; + __readonly_externalDmnsIndex: ExternalDmnsIndex; change: { nodeType: NodeType; isExternal: boolean; @@ -59,7 +65,7 @@ export function resizeNode({ const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); - const shape = diagramElements?.[change.shapeIndex] as DMNDI15__DMNShape | undefined; + const shape = diagramElements?.[change.shapeIndex] as Normalized | undefined; const shapeBounds = shape?.["dc:Bounds"]; if (!shapeBounds) { throw new Error("DMN MUTATION: Cannot resize non-existent shape bounds"); @@ -67,7 +73,15 @@ export function resizeNode({ const limit = { x: 0, y: 0 }; if (change.nodeType === NODE_TYPES.decisionService) { - const ds = definitions.drgElement![change.index] as DMN15__tDecisionService; + const externalDmn = __readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace ?? ""); + + const ds = + externalDmn === undefined + ? (definitions.drgElement![change.index] as Normalized) + : (externalDmn.model.definitions.drgElement![change.index] as Normalized); + if (!ds) { + throw new Error("DMN MUTATION: Cannot reposition divider line of non-existent Decision Service"); + } const dividerLineY = shape["dmndi:DMNDecisionServiceDividerLine"]?.["di:waypoint"]?.[0]?.["@_y"] ?? shapeBounds["@_y"]; @@ -76,7 +90,7 @@ export function resizeNode({ // We ignore handling the contents of the Decision Service when it is external if (!change.isExternal) { ds.encapsulatedDecision?.forEach((ed) => { - const edShape = dmnShapesByHref.get(ed["@_href"])!; + const edShape = __readonly_dmnShapesByHref.get(ed["@_href"])!; const dim = snapShapeDimensions(snapGrid, edShape, MIN_NODE_SIZES[NODE_TYPES.decision]({ snapGrid })); const pos = snapShapePosition(snapGrid, edShape); if (pos.x + dim.width > limit.x) { @@ -90,7 +104,7 @@ export function resizeNode({ // Output Decisions don't limit the resizing vertically, only horizontally. ds.outputDecision?.forEach((ed) => { - const edShape = dmnShapesByHref.get(ed["@_href"])!; + const edShape = __readonly_dmnShapesByHref.get(ed["@_href"])!; const dim = snapShapeDimensions(snapGrid, edShape, MIN_NODE_SIZES[NODE_TYPES.decision]({ snapGrid })); const pos = snapShapePosition(snapGrid, edShape); if (pos.x + dim.width > limit.x) { @@ -128,7 +142,7 @@ export function resizeNode({ edgeIndexesAlreadyUpdated.add(edgeIndex); - const edge = diagramElements[edgeIndex] as DMNDI15__DMNEdge | undefined; + const edge = diagramElements[edgeIndex] as Normalized | undefined; if (!edge || !edge["di:waypoint"]) { throw new Error("DMN MUTATION: Cannot reposition non-existent edge"); } diff --git a/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts b/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts index f3b7f56dd5c..d92ca3a67d1 100644 --- a/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts +++ b/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts @@ -29,21 +29,29 @@ import { snapShapeDimensions, snapShapePosition } from "../diagram/SnapGrid"; import { MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; import { SnapGrid } from "../store/Store"; import { NODE_TYPES } from "../diagram/nodes/NodeTypes"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; +import { addNamespaceToHref } from "../xml/xmlHrefs"; +import { ExternalDmnsIndex } from "../DmnEditor"; export const DECISION_SERVICE_DIVIDER_LINE_PADDING = 100; export function updateDecisionServiceDividerLine({ definitions, drdIndex, - dmnShapesByHref, + __readonly_dmnShapesByHref, + __readonly_dmnObjectNamespace, + __readonly_externalDmnsIndex, shapeIndex, localYPosition, drgElementIndex, snapGrid, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; - dmnShapesByHref: Map; + __readonly_dmnShapesByHref: Map & { index: number }>; + __readonly_dmnObjectNamespace: string | undefined; + __readonly_externalDmnsIndex: ExternalDmnsIndex; shapeIndex: number; localYPosition: number; drgElementIndex: number; @@ -51,13 +59,18 @@ export function updateDecisionServiceDividerLine({ }) { const { diagramElements } = addOrGetDrd({ definitions, drdIndex }); - const shape = diagramElements?.[shapeIndex] as DMNDI15__DMNShape | undefined; + const shape = diagramElements?.[shapeIndex] as Normalized | undefined; const shapeBounds = shape?.["dc:Bounds"]; if (!shapeBounds) { throw new Error("DMN MUTATION: Cannot reposition divider line of non-existent shape bounds"); } - const ds = definitions.drgElement![drgElementIndex] as DMN15__tDecisionService; + const externalDmn = __readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace ?? ""); + + const ds = + externalDmn === undefined + ? (definitions.drgElement![drgElementIndex] as Normalized) + : (externalDmn.model.definitions.drgElement![drgElementIndex] as Normalized); if (!ds) { throw new Error("DMN MUTATION: Cannot reposition divider line of non-existent Decision Service"); } @@ -69,14 +82,32 @@ export function updateDecisionServiceDividerLine({ const snappedDimensions = snapShapeDimensions(snapGrid, shape, decisionServiceMinSizes); const upperLimit = (ds.outputDecision ?? []).reduce((acc, od) => { + // For external Decision Services, the Output Decision will have the relative namespace. e.g. without namespace. + const href = + __readonly_dmnObjectNamespace !== undefined + ? addNamespaceToHref({ + href: od["@_href"], + namespace: + definitions["@_namespace"] === __readonly_dmnObjectNamespace ? undefined : __readonly_dmnObjectNamespace, + }) + : od["@_href"]; const v = - snapShapePosition(snapGrid, dmnShapesByHref.get(od["@_href"])!).y + - snapShapeDimensions(snapGrid, dmnShapesByHref.get(od["@_href"])!, decisionMinSizes).height; + snapShapePosition(snapGrid, __readonly_dmnShapesByHref.get(href)!).y + + snapShapeDimensions(snapGrid, __readonly_dmnShapesByHref.get(href)!, decisionMinSizes).height; return v > acc ? v : acc; }, snappedPosition.y + DECISION_SERVICE_DIVIDER_LINE_PADDING); const lowerLimit = (ds.encapsulatedDecision ?? []).reduce((acc, ed) => { - const v = snapShapePosition(snapGrid, dmnShapesByHref.get(ed["@_href"])!).y; + // For external Decision Services, the Encapsulated Decision will have the relative namespace. e.g. without namespace. + const href = + __readonly_dmnObjectNamespace !== undefined + ? addNamespaceToHref({ + href: ed["@_href"], + namespace: + definitions["@_namespace"] === __readonly_dmnObjectNamespace ? undefined : __readonly_dmnObjectNamespace, + }) + : ed["@_href"]; + const v = snapShapePosition(snapGrid, __readonly_dmnShapesByHref.get(href)!).y; return v < acc ? v : acc; }, snappedPosition.y + snappedDimensions.height - DECISION_SERVICE_DIVIDER_LINE_PADDING); @@ -87,8 +118,11 @@ export function updateDecisionServiceDividerLine({ shape["dmndi:DMNDecisionServiceDividerLine"]["di:waypoint"]![1]["@_y"] = newDividerLineYPosition; } -export function getCentralizedDecisionServiceDividerLine(bounds: DC__Bounds): DMNDI15__DMNDecisionServiceDividerLine { +export function getCentralizedDecisionServiceDividerLine( + bounds: DC__Bounds +): Normalized { return { + "@_id": generateUuid(), "di:waypoint": [ { "@_x": bounds["@_x"], "@_y": bounds["@_y"] + bounds["@_height"] / 2 }, { diff --git a/packages/dmn-editor/src/mutations/updateExpression.ts b/packages/dmn-editor/src/mutations/updateExpression.ts index 42e57febe92..f81bc318ed3 100644 --- a/packages/dmn-editor/src/mutations/updateExpression.ts +++ b/packages/dmn-editor/src/mutations/updateExpression.ts @@ -23,14 +23,15 @@ import { DMN15__tFunctionDefinition, } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { renameDrgElement } from "./renameNode"; +import { Normalized } from "../normalization/normalize"; export function updateExpression({ definitions, expression, drgElementIndex, }: { - definitions: DMN15__tDefinitions; - expression: BoxedExpression; + definitions: Normalized; + expression: Normalized; drgElementIndex: number; }): void { const drgElement = definitions.drgElement?.[drgElementIndex]; @@ -46,7 +47,7 @@ export function updateExpression({ if (drgElement?.__$$element === "decision") { drgElement.expression = expression; - drgElement.variable!["@_typeRef"] = expression?.["@_typeRef"] ?? drgElement.variable!["@_typeRef"]; + drgElement.variable!["@_typeRef"] = expression ? expression["@_typeRef"] : drgElement.variable!["@_typeRef"]; } else if (drgElement?.__$$element === "businessKnowledgeModel") { if (expression.__$$element !== "functionDefinition") { throw new Error("DMN MUTATION: Can't have an expression on a BKM that is not a Function."); @@ -58,7 +59,7 @@ export function updateExpression({ // We remove the __$$element here, because otherwise the "functionDefinition" element name will be used in the final XML. const { __$$element, ..._updateExpression } = expression; - drgElement.encapsulatedLogic = _updateExpression as DMN15__tFunctionDefinition; + drgElement.encapsulatedLogic = _updateExpression as Normalized; drgElement.variable!["@_typeRef"] = _updateExpression?.["@_typeRef"] ?? drgElement.variable!["@_typeRef"]; } else { throw new Error("DMN MUTATION: Can't update expression for drgElement that is not a Decision or a BKM."); diff --git a/packages/dmn-editor/src/mutations/updateExpressionWidths.ts b/packages/dmn-editor/src/mutations/updateExpressionWidths.ts index ff7f8451b45..7e6fb2d3dce 100644 --- a/packages/dmn-editor/src/mutations/updateExpressionWidths.ts +++ b/packages/dmn-editor/src/mutations/updateExpressionWidths.ts @@ -19,13 +19,14 @@ import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { addOrGetDrd } from "./addOrGetDrd"; +import { Normalized } from "../normalization/normalize"; export function updateExpressionWidths({ definitions, drdIndex, widthsById, }: { - definitions: DMN15__tDefinitions; + definitions: Normalized; drdIndex: number; widthsById: Map; }): void { diff --git a/packages/dmn-editor/src/normalization/autoGenerateDrd.ts b/packages/dmn-editor/src/normalization/autoGenerateDrd.ts new file mode 100644 index 00000000000..2053b0c7f9f --- /dev/null +++ b/packages/dmn-editor/src/normalization/autoGenerateDrd.ts @@ -0,0 +1,211 @@ +/* + * 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. + */ + +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; +import { ExternalDmnsIndex, ExternalModelsIndex, ExternalPmmlsIndex } from "../DmnEditor"; +import { computeDiagramData } from "../store/computed/computeDiagramData"; +import { State } from "../store/Store"; +import { MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; +import { getNodeTypeFromDmnObject } from "../diagram/maths/DmnMaths"; +import { DMN15__tDefinitions } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { DmnLatestModel } from "@kie-tools/dmn-marshaller"; +import { parseXmlHref } from "../xml/xmlHrefs"; +import { computeIndexedDrd } from "../store/computed/computeIndexes"; +import { getDefaultDrdName } from "../mutations/addOrGetDrd"; +import { addShape } from "../mutations/addShape"; +import { addEdge } from "../mutations/addEdge"; +import { EdgeType, NodeType } from "../diagram/connections/graphStructure"; +import { PositionalNodeHandleId } from "../diagram/connections/PositionalNodeHandles"; +import { Normalized } from "./normalize"; + +export async function autoGenerateDrd(args: { + model: State["dmn"]["model"]; + diagram: State["diagram"]; + externalModelsByNamespace: ExternalModelsIndex | undefined; + externalModelTypesByNamespace: { + dmns: ExternalDmnsIndex; + pmmls: ExternalPmmlsIndex; + }; +}) { + // Create DRD + args.model.definitions["dmndi:DMNDI"] = { + ...args.model.definitions["dmndi:DMNDI"], + "dmndi:DMNDiagram": [ + { + "@_id": generateUuid(), + "@_name": getDefaultDrdName({ drdIndex: 0 }), + "@_useAlternativeInputDataShape": false, + "dmndi:DMNDiagramElement": [], + "di:extension": { "kie:ComponentsWidthsExtension": { "kie:ComponentWidths": [{}] } }, + }, + ], + }; + + // 1. Add shapes from current DRG + args.model.definitions.drgElement?.forEach((drgElement) => { + const nodeType = getNodeTypeFromDmnObject(drgElement) ?? "node_unknown"; + const minNodeSize = MIN_NODE_SIZES[nodeType]({ + snapGrid: { + isEnabled: true, + x: 20, + y: 20, + }, + isAlternativeInputDataShape: false, + }); + + addShape({ + definitions: args.model.definitions, + drdIndex: 0, + nodeType: nodeType, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": drgElement["@_id"]!, + "dc:Bounds": { + "@_x": 0, + "@_y": 0, + ...minNodeSize, + }, + }, + }); + }); + + // 2. Add shapes from external models; + const definedNamespaces = new Map( + Object.keys(args.model.definitions) + .filter((keys: keyof Normalized) => String(keys).startsWith("@_xmlns:")) + .map((xmlnsKey: keyof Normalized) => [ + args.model.definitions[xmlnsKey], + xmlnsKey.split("@_xmlns:")[1], + ]) + ); + + const updateIndexedDrdWithNodes = computeIndexedDrd(args.model.definitions["@_namespace"], args.model.definitions, 0); + const { nodesById: updatedNodesByIdWithNodes, drgEdges: updatedDrgEdgesWithNodes } = computeDiagramData( + args.diagram, + args.model.definitions, + args.externalModelTypesByNamespace, + updateIndexedDrdWithNodes, + false + ); + + // Search on edges for any node that isn't on the nodesByIdMap; + // Only external nodes should be added to the externalNodesHref; + const externalNodesHref = updatedDrgEdgesWithNodes.reduce((acc, drgEdge) => { + if (!updatedNodesByIdWithNodes.has(drgEdge.sourceId)) { + acc.add(drgEdge.sourceId); + } + if (!updatedNodesByIdWithNodes.has(drgEdge.targetId)) { + acc.add(drgEdge.targetId); + } + return acc; + }, new Set()); + + // Add external shapes + externalNodesHref.forEach((href) => { + const { namespace, id } = parseXmlHref(href); + if (namespace) { + const externalModel = args.externalModelsByNamespace?.[namespace]; + if (externalModel && (externalModel.model as Normalized).definitions) { + const drgElements = (externalModel.model as Normalized).definitions.drgElement; + const drgElement = drgElements?.filter((drgElement) => drgElement["@_id"] === id); + + const nodeType = getNodeTypeFromDmnObject(drgElement![0]) ?? "node_unknown"; + const minNodeSize = MIN_NODE_SIZES[nodeType]({ + snapGrid: { + isEnabled: true, + x: 20, + y: 20, + }, + isAlternativeInputDataShape: false, + }); + + addShape({ + definitions: args.model.definitions, + drdIndex: 0, + nodeType: nodeType, + shape: { + "@_id": generateUuid(), + "@_dmnElementRef": `${definedNamespaces.get(namespace)}:${id}`, + "dc:Bounds": { + "@_x": 0, + "@_y": 0, + ...minNodeSize, + }, + }, + }); + } + } + }); + + // 3. Add edges + const updatedIndexedDrdWithExternalNodes = computeIndexedDrd( + args.model.definitions["@_namespace"], + args.model.definitions, + 0 + ); + const { + nodesById: updatedNodesByIdWithExternalNodes, + edgesById: updatedEdgesByIdWithExternalNodes, + drgEdges: updatedDrgEdgesWithExternalNodes, + } = computeDiagramData( + args.diagram, + args.model.definitions, + args.externalModelTypesByNamespace, + updatedIndexedDrdWithExternalNodes, + false + ); + + for (const drgEdge of updatedDrgEdgesWithExternalNodes) { + const edge = updatedEdgesByIdWithExternalNodes.get(drgEdge.id); + const sourceNode = updatedNodesByIdWithExternalNodes.get(drgEdge.sourceId); + const targetNode = updatedNodesByIdWithExternalNodes.get(drgEdge.targetId); + + // Avoid missing nodes. Possible cause is an external model which couldn't be found. + if (!edge || !sourceNode || !targetNode) { + continue; + } + + addEdge({ + definitions: args.model.definitions, + drdIndex: 0, + keepWaypoints: false, + edge: { + autoPositionedEdgeMarker: undefined, + type: edge.type as EdgeType, + targetHandle: PositionalNodeHandleId.Bottom, + sourceHandle: PositionalNodeHandleId.Top, + }, + sourceNode: { + type: sourceNode.type as NodeType, + href: sourceNode.id, + data: sourceNode.data, + bounds: sourceNode.data.shape["dc:Bounds"]!, + shapeId: sourceNode.data.shape["@_id"], + }, + targetNode: { + type: targetNode.type as NodeType, + href: targetNode.id, + data: targetNode.data, + bounds: targetNode.data.shape["dc:Bounds"]!, + index: targetNode.data.index, + shapeId: targetNode.data.shape["@_id"], + }, + }); + } +} diff --git a/packages/dmn-editor/src/normalization/normalize.ts b/packages/dmn-editor/src/normalization/normalize.ts index c3951676a50..92a6fb26aea 100644 --- a/packages/dmn-editor/src/normalization/normalize.ts +++ b/packages/dmn-editor/src/normalization/normalize.ts @@ -17,11 +17,22 @@ * under the License. */ +import { DmnLatestModel } from "@kie-tools/dmn-marshaller"; import { getNewDmnIdRandomizer } from "../idRandomizer/dmnIdRandomizer"; import { addMissingImportNamespaces } from "../mutations/addMissingImportNamespaces"; import { State } from "../store/Store"; -export function normalize(model: State["dmn"]["model"]) { +export type Normalized = WithRequiredDeep; + +type WithRequiredDeep = T extends undefined + ? T + : T extends Array + ? Array> + : { [P in keyof T]: WithRequiredDeep } & (K extends keyof T + ? { [P in K]-?: NonNullable> } + : T); + +export function normalize(model: DmnLatestModel): State["dmn"]["model"] { getNewDmnIdRandomizer() .ack({ json: model.definitions.drgElement, @@ -50,7 +61,9 @@ export function normalize(model: State["dmn"]["model"]) { }) .randomize({ skipAlreadyAttributedIds: true }); - addMissingImportNamespaces(model.definitions); + const normalizedModel = model as Normalized; + + addMissingImportNamespaces(normalizedModel.definitions); - return model; + return normalizedModel; } diff --git a/packages/dmn-editor/src/overlaysPanel/OverlaysPanel.tsx b/packages/dmn-editor/src/overlaysPanel/OverlaysPanel.tsx index 63901c773d7..f66be9aa90a 100644 --- a/packages/dmn-editor/src/overlaysPanel/OverlaysPanel.tsx +++ b/packages/dmn-editor/src/overlaysPanel/OverlaysPanel.tsx @@ -23,17 +23,37 @@ import { Form, FormGroup } from "@patternfly/react-core/dist/js/components/Form" import { Divider } from "@patternfly/react-core/dist/js/components/Divider"; import { Slider } from "@patternfly/react-core/dist/js/components/Slider"; import { useDmnEditorStore, useDmnEditorStoreApi } from "../store/StoreContext"; +import { useLayoutEffect, useRef } from "react"; const MIN_SNAP = 5; const MAX_SNAP = 50; const SNAP_STEP = 5; +const BOTTOM_MARGIN = 10; -export function OverlaysPanel() { +interface OverlaysPanelProps { + availableHeight?: number; +} + +export function OverlaysPanel({ availableHeight }: OverlaysPanelProps) { const diagram = useDmnEditorStore((s) => s.diagram); const dmnEditorStoreApi = useDmnEditorStoreApi(); + const overlayPanelContainer = useRef(null); + useLayoutEffect(() => { + if (overlayPanelContainer.current && availableHeight) { + const bounds = overlayPanelContainer.current.getBoundingClientRect(); + const currentHeight = bounds.height; + const yPos = bounds.y; + if (currentHeight + yPos >= availableHeight) { + overlayPanelContainer.current.style.height = availableHeight - BOTTOM_MARGIN + "px"; + overlayPanelContainer.current.style.overflowY = "scroll"; + } else { + overlayPanelContainer.current.style.overflowY = "visible"; + } + } + }); return ( - <> +
e.stopPropagation()} // Prevent ReactFlow KeyboardShortcuts from triggering when editing stuff on Overlays Panel > @@ -140,6 +160,6 @@ export function OverlaysPanel() { />
- +
); } diff --git a/packages/dmn-editor/src/pmml/pmml.ts b/packages/dmn-editor/src/pmml/pmml.ts index e07e4e3d977..5fed0e6df35 100644 --- a/packages/dmn-editor/src/pmml/pmml.ts +++ b/packages/dmn-editor/src/pmml/pmml.ts @@ -20,6 +20,7 @@ import { DMN15__tImport } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { buildXmlHref } from "../xml/xmlHrefs"; import * as __path from "path"; +import { Normalized } from "../normalization/normalize"; export const KIE_PMML_NAMESPACE = "https://kie.org/pmml"; @@ -56,7 +57,7 @@ export function getPmmlNamespace({ return buildXmlHref({ namespace: KIE_PMML_NAMESPACE, id: normalizedPosixPathRelativeToTheOpenFile }); } -export function getPmmlNamespaceFromDmnImport({ dmnImport }: { dmnImport: DMN15__tImport }) { +export function getPmmlNamespaceFromDmnImport({ dmnImport }: { dmnImport: Normalized }) { return dmnImport["@_locationURI"] ? getPmmlNamespace({ // We need to normalize the path here because they're always stored as explicit relative paths starting with `./` or `../` diff --git a/packages/dmn-editor/src/propertiesPanel/BkmProperties.tsx b/packages/dmn-editor/src/propertiesPanel/BkmProperties.tsx index a8c9d9aacb8..9a8466cfa18 100644 --- a/packages/dmn-editor/src/propertiesPanel/BkmProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BkmProperties.tsx @@ -30,13 +30,15 @@ import { InlineFeelNameInput } from "../feel/InlineFeelNameInput"; import { useDmnEditor } from "../DmnEditorContext"; import { useResolvedTypeRef } from "../dataTypes/useResolvedTypeRef"; import { useCallback } from "react"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function BkmProperties({ bkm, namespace, index, }: { - bkm: DMN15__tBusinessKnowledgeModel; + bkm: Normalized; namespace: string | undefined; index: number; }) { @@ -80,8 +82,10 @@ export function BkmProperties({ isDisabled={isReadonly} onChange={(newTypeRef) => { setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tBusinessKnowledgeModel; - drgElement.variable ??= { "@_name": bkm["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![ + index + ] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": bkm["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }} @@ -96,7 +100,9 @@ export function BkmProperties({ value={bkm.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tBusinessKnowledgeModel).description = { + ( + state.dmn.model.definitions.drgElement![index] as Normalized + ).description = { __$$text: newDescription, }; }); @@ -118,7 +124,9 @@ export function BkmProperties({ values={bkm.extensionElements?.["kie:attachment"]} onChange={(newExtensionElements) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tBusinessKnowledgeModel).extensionElements = { + ( + state.dmn.model.definitions.drgElement![index] as Normalized + ).extensionElements = { "kie:attachment": newExtensionElements, }; }); diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ContextInformationItemCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ContextInformationItemCell.tsx index db4821eef99..4bde233ac7d 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ContextInformationItemCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ContextInformationItemCell.tsx @@ -24,6 +24,7 @@ import { DMN15__tContext, DMN15__tInformationItem } from "@kie-tools/dmn-marshal import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { InformationItemCell } from "./InformationItemCell"; import { useDmnEditorStore } from "../../store/StoreContext"; +import { Normalized } from "../../normalization/normalize"; export function ContextInformationItemCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -44,8 +45,10 @@ export function ContextInformationItemCell(props: { [cellPath?.root, props.boxedExpressionIndex] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); - const rootExpressionUpdater = useBoxedExpressionUpdater(rootPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); + const rootExpressionUpdater = useBoxedExpressionUpdater>(rootPath ?? []); return ( <> @@ -68,7 +71,7 @@ export function ContextInformationItemCell(props: { }); rootExpressionUpdater((dmnObject) => { if (cellPath?.type === "context") { - const expression = (dmnObject as DMN15__tContext).contextEntry![cellPath.row ?? 0].expression; + const expression = (dmnObject as Normalized).contextEntry![cellPath.row ?? 0].expression; if (expression) { expression["@_typeRef"] = newTypeRef; } diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputHeaderCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputHeaderCell.tsx index 1256d589102..32d617bd96b 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputHeaderCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputHeaderCell.tsx @@ -24,7 +24,7 @@ import { ContentField, DescriptionField, ExpressionLanguageField, NameField, Typ import { FormGroup, FormSection } from "@patternfly/react-core/dist/js/components/Form"; import { DMN15__tInputClause } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { useDmnEditor } from "../../DmnEditorContext"; -import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; +import { DmnBuiltInDataType, generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { PropertiesPanelHeader } from "../PropertiesPanelHeader"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/ClipboardCopy"; @@ -32,6 +32,7 @@ import { ConstraintsFromTypeConstraintAttribute } from "../../dataTypes/Constrai import { useDmnEditorStore, useDmnEditorStoreApi } from "../../store/StoreContext"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; import { State } from "../../store/Store"; +import { Normalized } from "../../normalization/normalize"; export function DecisionTableInputHeaderCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -46,9 +47,9 @@ export function DecisionTableInputHeaderCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>(selectedObjectInfos?.expressionPath ?? []); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tInputClause, [selectedObjectInfos?.cell]); + const cell = useMemo(() => selectedObjectInfos?.cell as Normalized, [selectedObjectInfos?.cell]); const inputExpression = useMemo(() => cell.inputExpression, [cell.inputExpression]); const inputValues = useMemo(() => cell.inputValues, [cell.inputValues]); @@ -101,7 +102,7 @@ export function DecisionTableInputHeaderCell(props: { getAllUniqueNames={getAllUniqueNames} onChange={(newName) => updater((dmnObject) => { - dmnObject.inputExpression ??= {}; + dmnObject.inputExpression ??= { "@_id": generateUuid() }; dmnObject.inputExpression.text ??= { __$$text: "" }; dmnObject.inputExpression.text.__$$text = newName; }) @@ -110,12 +111,12 @@ export function DecisionTableInputHeaderCell(props: { updater((dmnObject) => { - dmnObject.inputExpression ??= {}; + dmnObject.inputExpression ??= { "@_id": generateUuid() }; dmnObject.inputExpression["@_typeRef"] = newTypeRef; - dmnObject.inputValues ??= { text: { __$$text: "" } }; + dmnObject.inputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.inputValues["@_typeRef"] = newTypeRef; }) } @@ -137,7 +138,7 @@ export function DecisionTableInputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newExpressionLanguage) => updater((dmnObject) => { - dmnObject.inputExpression ??= {}; + dmnObject.inputExpression ??= { "@_id": generateUuid() }; dmnObject.inputExpression["@_expressionLanguage"] = newExpressionLanguage; }) } @@ -148,7 +149,7 @@ export function DecisionTableInputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newDescription) => updater((dmnObject) => { - dmnObject.inputExpression ??= {}; + dmnObject.inputExpression ??= { "@_id": generateUuid() }; dmnObject.inputExpression.description ??= { __$$text: "" }; dmnObject.inputExpression.description.__$$text = newDescription; }) @@ -173,7 +174,7 @@ export function DecisionTableInputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newExpressionLanguage) => updater((dmnObject) => { - dmnObject.inputValues ??= { text: { __$$text: "" } }; + dmnObject.inputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.inputValues["@_expressionLanguage"] = newExpressionLanguage; }) } @@ -184,7 +185,7 @@ export function DecisionTableInputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newText) => updater((dmnObject) => { - dmnObject.inputValues ??= { text: { __$$text: "" } }; + dmnObject.inputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.inputValues.text ??= { __$$text: "" }; dmnObject.inputValues.text.__$$text = newText; }) @@ -196,7 +197,7 @@ export function DecisionTableInputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newDescription: string) => updater((dmnObject) => { - dmnObject.inputValues ??= { text: { __$$text: "" } }; + dmnObject.inputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.inputValues.description ??= { __$$text: "" }; dmnObject.inputValues.description.__$$text = newDescription; }) diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputRuleCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputRuleCell.tsx index 6fa40b414ea..c7660b75687 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputRuleCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableInputRuleCell.tsx @@ -30,6 +30,7 @@ import { ConstraintsFromTypeConstraintAttribute } from "../../dataTypes/Constrai import { useDmnEditor } from "../../DmnEditorContext"; import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../../normalization/normalize"; export function DecisionTableInputRule(props: { boxedExpressionIndex?: BoxedExpressionIndex; isReadonly: boolean }) { const dmnEditorStoreApi = useDmnEditorStoreApi(); @@ -56,16 +57,18 @@ export function DecisionTableInputRule(props: { boxedExpressionIndex?: BoxedExpr .getDataTypes(externalModelsByNamespace); const typeRef = allTopLevelItemDefinitionUniqueNames.get( - (root?.cell as DMN15__tDecisionTable)?.input?.[cellPath.column ?? 0].inputExpression["@_typeRef"] ?? "" + (root?.cell as Normalized)?.input?.[cellPath.column ?? 0].inputExpression[ + "@_typeRef" + ] ?? "" ) ?? DmnBuiltInDataType.Undefined; return { typeRef, itemDefinition: allDataTypesById.get(typeRef)?.itemDefinition }; } } }, [dmnEditorStoreApi, externalModelsByNamespace, props.boxedExpressionIndex, selectedObjectInfos?.expressionPath]); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>(selectedObjectInfos?.expressionPath ?? []); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tUnaryTests, [selectedObjectInfos?.cell]); + const cell = useMemo(() => selectedObjectInfos?.cell as Normalized, [selectedObjectInfos?.cell]); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputHeaderCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputHeaderCell.tsx index 68a51490169..026712b619e 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputHeaderCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputHeaderCell.tsx @@ -24,7 +24,7 @@ import { ContentField, DescriptionField, ExpressionLanguageField, NameField, Typ import { FormGroup, FormSection } from "@patternfly/react-core/dist/js/components/Form"; import { DMN15__tOutputClause } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { PropertiesPanelHeader } from "../PropertiesPanelHeader"; -import { BoxedDecisionTable, DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; +import { BoxedDecisionTable, DmnBuiltInDataType, generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; import { useDmnEditor } from "../../DmnEditorContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/ClipboardCopy"; @@ -32,6 +32,7 @@ import { ConstraintsFromTypeConstraintAttribute } from "../../dataTypes/Constrai import { useDmnEditorStore, useDmnEditorStoreApi } from "../../store/StoreContext"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; import { State } from "../../store/Store"; +import { Normalized } from "../../normalization/normalize"; export function DecisionTableOutputHeaderCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -47,9 +48,14 @@ export function DecisionTableOutputHeaderCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tOutputClause, [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized, + [selectedObjectInfos?.cell] + ); const defaultOutputEntry = useMemo(() => cell.defaultOutputEntry, [cell.defaultOutputEntry]); const outputValues = useMemo(() => cell.outputValues, [cell.outputValues]); @@ -66,7 +72,7 @@ export function DecisionTableOutputHeaderCell(props: { () => props.boxedExpressionIndex?.get( selectedObjectInfos?.expressionPath[selectedObjectInfos?.expressionPath.length - 1]?.root ?? "" - )?.cell as BoxedDecisionTable | undefined, + )?.cell as Normalized | undefined, [props.boxedExpressionIndex, selectedObjectInfos?.expressionPath] ); @@ -119,7 +125,7 @@ export function DecisionTableOutputHeaderCell(props: { alternativeFieldName={`${alternativeFieldName} Type`} isReadonly={true} dmnEditorRootElementRef={dmnEditorRootElementRef} - typeRef={root?.["@_typeRef"] ?? DmnBuiltInDataType.Undefined} + typeRef={root?.["@_typeRef"]} /> )} @@ -139,7 +145,7 @@ export function DecisionTableOutputHeaderCell(props: { alternativeFieldName={root?.output.length === 1 ? "Column Type" : undefined} isReadonly={props.isReadonly} dmnEditorRootElementRef={dmnEditorRootElementRef} - typeRef={cell?.["@_typeRef"] ?? DmnBuiltInDataType.Undefined} + typeRef={cell?.["@_typeRef"]} onChange={(newTypeRef) => updater((dmnObject) => { dmnObject["@_typeRef"] = newTypeRef; @@ -184,7 +190,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newExpressionLanguage) => updater((dmnObject) => { - dmnObject.defaultOutputEntry ??= {}; + dmnObject.defaultOutputEntry ??= { "@_id": generateUuid() }; dmnObject.defaultOutputEntry["@_expressionLanguage"] = newExpressionLanguage; }) } @@ -195,7 +201,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newText) => updater((dmnObject) => { - dmnObject.defaultOutputEntry ??= { text: { __$$text: "" } }; + dmnObject.defaultOutputEntry ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.defaultOutputEntry.text ??= { __$$text: "" }; dmnObject.defaultOutputEntry.text.__$$text = newText; }) @@ -207,7 +213,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newDescription) => updater((dmnObject) => { - dmnObject.defaultOutputEntry ??= { description: { __$$text: "" } }; + dmnObject.defaultOutputEntry ??= { "@_id": generateUuid(), description: { __$$text: "" } }; dmnObject.defaultOutputEntry.description ??= { __$$text: "" }; dmnObject.defaultOutputEntry.description.__$$text = newDescription; }) @@ -232,7 +238,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newExpressionLanguage) => updater((dmnObject) => { - dmnObject.outputValues ??= { text: { __$$text: "" } }; + dmnObject.outputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.outputValues["@_expressionLanguage"] = newExpressionLanguage; }) } @@ -243,7 +249,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newText) => updater((dmnObject) => { - dmnObject.outputValues ??= { text: { __$$text: "" } }; + dmnObject.outputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.outputValues.text.__$$text = newText; }) } @@ -254,7 +260,7 @@ export function DecisionTableOutputHeaderCell(props: { expressionPath={selectedObjectInfos?.expressionPath ?? []} onChange={(newDescription: string) => updater((dmnObject) => { - dmnObject.outputValues ??= { text: { __$$text: "" } }; + dmnObject.outputValues ??= { "@_id": generateUuid(), text: { __$$text: "" } }; dmnObject.outputValues.description ??= { __$$text: "" }; dmnObject.outputValues.description.__$$text = newDescription; }) diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputRuleCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputRuleCell.tsx index 069526dfde9..e062b8833bf 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputRuleCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableOutputRuleCell.tsx @@ -33,6 +33,7 @@ import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/a import { useDmnEditor } from "../../DmnEditorContext"; import { useDmnEditorStore, useDmnEditorStoreApi } from "../../store/StoreContext"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../../normalization/normalize"; export function DecisionTableOutputRuleCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -61,16 +62,21 @@ export function DecisionTableOutputRuleCell(props: { ) { const typeRef = allTopLevelItemDefinitionUniqueNames.get( - (root?.cell as DMN15__tDecisionTable)?.output?.[cellPath.column ?? 0]["@_typeRef"] ?? "" + (root?.cell as Normalized)?.output?.[cellPath.column ?? 0]["@_typeRef"] ?? "" ) ?? DmnBuiltInDataType.Undefined; return { typeRef, itemDefinition: allDataTypesById.get(typeRef)?.itemDefinition }; } } }, [dmnEditorStoreApi, externalModelsByNamespace, props.boxedExpressionIndex, selectedObjectInfos?.expressionPath]); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tLiteralExpression, [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized, + [selectedObjectInfos?.cell] + ); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableRootCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableRootCell.tsx index 906e6e7f0e8..a85fbd273ba 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableRootCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/DecisionTableRootCell.tsx @@ -26,10 +26,11 @@ import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/ClipboardCopy"; import { FormGroup } from "@patternfly/react-core/dist/js/components/Form"; import { useDmnEditorStore } from "../../store/StoreContext"; +import { Normalized } from "../../normalization/normalize"; type DecisionTableRoot = Pick< - DMN15__tDecisionTable, - "@_label" | "description" | "@_typeRef" | "@_outputLabel" | "@_aggregation" | "@_hitPolicy" + Normalized, + "@_label" | "description" | "@_typeRef" | "@_outputLabel" | "@_aggregation" | "@_hitPolicy" | "@_id" >; export function DecisionTableRootCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; isReadonly: boolean }) { diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ExpressionRootCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ExpressionRootCell.tsx index 5aeace5c2cc..bcf99fb1dae 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ExpressionRootCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/ExpressionRootCell.tsx @@ -27,7 +27,7 @@ import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/Clipboa import { FormGroup } from "@patternfly/react-core/dist/js/components/Form"; import { useDmnEditorStore } from "../../store/StoreContext"; -type ExpressionRoot = Pick; +type ExpressionRoot = Pick; export function ExpressionRootCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; isReadonly: boolean }) { const selectedObjectId = useDmnEditorStore((s) => s.boxedExpressionEditor.selectedObjectId); diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/Fields.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/Fields.tsx index 5ab9fc11355..46dc505c15a 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/Fields.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/Fields.tsx @@ -18,7 +18,7 @@ */ import * as React from "react"; -import { useEffect, useState, useRef } from "react"; +import { useEffect, useState } from "react"; import { FormGroup } from "@patternfly/react-core/dist/js/components/Form"; import { InlineFeelNameInput } from "../../feel/InlineFeelNameInput"; import { TextArea } from "@patternfly/react-core/dist/js/components/TextArea"; @@ -91,7 +91,7 @@ export function NameField(props: { export function TypeRefField(props: { alternativeFieldName?: string; - typeRef: string; + typeRef?: string; isReadonly: boolean; dmnEditorRootElementRef: React.RefObject; onChange?: (newTypeRef: string) => void; diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionParametersCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionParametersCell.tsx index f1c66238126..f5c3a2d3ecc 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionParametersCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionParametersCell.tsx @@ -21,7 +21,6 @@ import * as React from "react"; import { useCallback, useMemo, useState } from "react"; import { DescriptionField, NameField, TypeRefField } from "./Fields"; import { BoxedExpressionIndex } from "../../boxedExpressions/boxedExpressionIndex"; -import { DmnBuiltInDataType } from "@kie-tools/boxed-expression-component/dist/api"; import { useDmnEditor } from "../../DmnEditorContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/ClipboardCopy"; @@ -36,6 +35,8 @@ import { ConstraintsFromTypeConstraintAttribute } from "../../dataTypes/Constrai import { useDmnEditorStore, useDmnEditorStoreApi } from "../../store/StoreContext"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; import { State } from "../../store/Store"; +import { Normalized } from "../../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function FunctionDefinitionParameterCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -48,9 +49,14 @@ export function FunctionDefinitionParameterCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tInformationItem[], [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized[], + [selectedObjectInfos?.cell] + ); const [isParameterExpanded, setParameterExpaded] = useState([]); const getAllUniqueNames = useCallback((s: State) => new Map(), []); @@ -96,7 +102,7 @@ export function FunctionDefinitionParameterCell(props: { onChange={(newName: string) => { updater((dmnObject) => { dmnObject.formalParameter ??= []; - dmnObject.formalParameter[i] ??= { "@_name": "" }; + dmnObject.formalParameter[i] ??= { "@_id": generateUuid(), "@_name": "" }; dmnObject.formalParameter[i]["@_name"] = newName; }); }} @@ -107,7 +113,7 @@ export function FunctionDefinitionParameterCell(props: { onTypeRefChange={(newTypeRef) => updater((dmnObject) => { dmnObject.formalParameter ??= []; - dmnObject.formalParameter[i] ??= { "@_name": "" }; + dmnObject.formalParameter[i] ??= { "@_id": generateUuid(), "@_name": "" }; dmnObject.formalParameter[i]["@_typeRef"] = newTypeRef; }) } @@ -119,7 +125,11 @@ export function FunctionDefinitionParameterCell(props: { onChange={(newDescription: string) => { updater((dmnObject) => { dmnObject.formalParameter ??= []; - dmnObject.formalParameter[i] ??= { "@_name": "", description: { __$$text: "" } }; + dmnObject.formalParameter[i] ??= { + "@_id": generateUuid(), + "@_name": "", + description: { __$$text: "" }, + }; dmnObject.formalParameter[i].description ??= { __$$text: "" }; dmnObject.formalParameter[i].description!.__$$text = newDescription; }); @@ -134,7 +144,7 @@ export function FunctionDefinitionParameterCell(props: { } function FunctionDefinitionParameterTypeRef(props: { - parameter: DMN15__tInformationItem; + parameter: Normalized; isReadonly: boolean; onTypeRefChange: (newTypeRef: string) => void; }) { @@ -156,7 +166,7 @@ function FunctionDefinitionParameterTypeRef(props: { {itemDefinition && ( diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionRootCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionRootCell.tsx index b86bf3f2f69..9c8f50e509b 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionRootCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/FunctionDefinitionRootCell.tsx @@ -26,8 +26,12 @@ import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/Clipboa import { FormGroup } from "@patternfly/react-core/dist/js/components/Form"; import { DMN15__tFunctionDefinition } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; import { useDmnEditorStore } from "../../store/StoreContext"; +import { Normalized } from "../../normalization/normalize"; -type FunctionDefinitionRoot = Pick; +type FunctionDefinitionRoot = Pick< + Normalized, + "@_kind" | "@_typeRef" | "description" | "@_id" +>; export function FunctionDefinitionRootCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InformationItemCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InformationItemCell.tsx index 8cc221a2232..3df915f947a 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InformationItemCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InformationItemCell.tsx @@ -30,6 +30,7 @@ import { useDmnEditor } from "../../DmnEditorContext"; import { Constraints, ConstraintsFromTypeConstraintAttribute } from "../../dataTypes/Constraints"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; import { State } from "../../store/Store"; +import { Normalized } from "../../normalization/normalize"; export function InformationItemCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -47,7 +48,10 @@ export function InformationItemCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tInformationItem, [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized, + [selectedObjectInfos?.cell] + ); const itemDefinition = useMemo(() => { const { allDataTypesById, allTopLevelItemDefinitionUniqueNames } = dmnEditorStoreApi @@ -76,7 +80,7 @@ export function InformationItemCell(props: { /> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationFunctionCallCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationFunctionCallCell.tsx index 736e4f4ba4d..f9001c60852 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationFunctionCallCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationFunctionCallCell.tsx @@ -24,6 +24,7 @@ import { BoxedExpressionIndex } from "../../boxedExpressions/boxedExpressionInde import { useDmnEditorStore } from "../../store/StoreContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { DMN15__tLiteralExpression } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../../normalization/normalize"; export function InvocationFunctionCallCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -35,9 +36,14 @@ export function InvocationFunctionCallCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tLiteralExpression, [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized, + [selectedObjectInfos?.cell] + ); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationInformationItemCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationInformationItemCell.tsx index 66aeacb05a5..7d9b62c6771 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationInformationItemCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/InvocationInformationItemCell.tsx @@ -27,6 +27,7 @@ import { import { useDmnEditorStore } from "../../store/StoreContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { InformationItemCell } from "./InformationItemCell"; +import { Normalized } from "../../normalization/normalize"; export function InvocationInformationItemCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -47,8 +48,10 @@ export function InvocationInformationItemCell(props: { [cellPath?.root, props.boxedExpressionIndex] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); - const rootExpressionUpdater = useBoxedExpressionUpdater(rootPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); + const rootExpressionUpdater = useBoxedExpressionUpdater>(rootPath ?? []); return ( <> @@ -71,7 +74,7 @@ export function InvocationInformationItemCell(props: { }); rootExpressionUpdater((dmnObject) => { if (cellPath?.type === "invocation") { - const expression = (dmnObject as DMN15__tInvocation).binding![cellPath.row ?? 0].expression; + const expression = (dmnObject as Normalized).binding![cellPath.row ?? 0].expression; if (expression) { expression["@_typeRef"] = newTypeRef; } diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/IteratorVariableCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/IteratorVariableCell.tsx index 2860e49cce5..0b621cc218e 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/IteratorVariableCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/IteratorVariableCell.tsx @@ -24,6 +24,7 @@ import { BoxedExpressionIndex } from "../../boxedExpressions/boxedExpressionInde import { useDmnEditorStore } from "../../store/StoreContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { DMN15__tIterator } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types"; +import { Normalized } from "../../normalization/normalize"; export function IteratorVariableCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; isReadonly: boolean }) { const selectedObjectId = useDmnEditorStore((s) => s.boxedExpressionEditor.selectedObjectId); @@ -32,9 +33,9 @@ export function IteratorVariableCell(props: { boxedExpressionIndex?: BoxedExpres [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>(selectedObjectInfos?.expressionPath ?? []); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tIterator, [selectedObjectInfos?.cell]); + const cell = useMemo(() => selectedObjectInfos?.cell as Normalized, [selectedObjectInfos?.cell]); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/LiteralExpressionContentCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/LiteralExpressionContentCell.tsx index 4dd5ba09a72..1ced038ecc9 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/LiteralExpressionContentCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/LiteralExpressionContentCell.tsx @@ -26,6 +26,7 @@ import { useDmnEditorStore } from "../../store/StoreContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { ClipboardCopy } from "@patternfly/react-core/dist/js/components/ClipboardCopy"; import { FormGroup } from "@patternfly/react-core/dist/js/components/Form"; +import { Normalized } from "../../normalization/normalize"; export function LiteralExpressionContentCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -37,9 +38,14 @@ export function LiteralExpressionContentCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); - const cell = useMemo(() => selectedObjectInfos?.cell as DMN15__tLiteralExpression, [selectedObjectInfos?.cell]); + const cell = useMemo( + () => selectedObjectInfos?.cell as Normalized, + [selectedObjectInfos?.cell] + ); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/RelationInformationItemCell.tsx b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/RelationInformationItemCell.tsx index de835fb77d9..3eecf1da257 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/RelationInformationItemCell.tsx +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/RelationInformationItemCell.tsx @@ -24,6 +24,7 @@ import { DMN15__tInformationItem } from "@kie-tools/dmn-marshaller/dist/schemas/ import { useDmnEditorStore } from "../../store/StoreContext"; import { useBoxedExpressionUpdater } from "./useBoxedExpressionUpdater"; import { InformationItemCell } from "./InformationItemCell"; +import { Normalized } from "../../normalization/normalize"; export function RelationInformationItemCell(props: { boxedExpressionIndex?: BoxedExpressionIndex; @@ -35,7 +36,9 @@ export function RelationInformationItemCell(props: { [props.boxedExpressionIndex, selectedObjectId] ); - const updater = useBoxedExpressionUpdater(selectedObjectInfos?.expressionPath ?? []); + const updater = useBoxedExpressionUpdater>( + selectedObjectInfos?.expressionPath ?? [] + ); return ( <> diff --git a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/useBoxedExpressionUpdater.ts b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/useBoxedExpressionUpdater.ts index bd12efde335..1b4ab772cba 100644 --- a/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/useBoxedExpressionUpdater.ts +++ b/packages/dmn-editor/src/propertiesPanel/BoxedExpressionPropertiesPanelComponents/useBoxedExpressionUpdater.ts @@ -27,6 +27,7 @@ import { useDmnEditorStore, useDmnEditorStoreApi } from "../../store/StoreContex import { buildXmlHref } from "../../xml/xmlHrefs"; import { AllExpressionsWithoutTypes } from "../../dataTypes/DataTypeSpec"; import { useExternalModels } from "../../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../../normalization/normalize"; export function useBoxedExpressionUpdater( expressionPath: ExpressionPath[] | undefined @@ -49,15 +50,19 @@ export function useBoxedExpressionUpdater( if (state.dmn.model.definitions.drgElement?.[node?.data.index ?? 0]?.__$$element === "businessKnowledgeModel") { const dmnObject = getDmnObjectByPath( expressionPath ?? [], - (state.dmn.model.definitions.drgElement?.[node?.data.index ?? 0] as DMN15__tBusinessKnowledgeModel) - ?.encapsulatedLogic + ( + state.dmn.model.definitions.drgElement?.[ + node?.data.index ?? 0 + ] as Normalized + )?.encapsulatedLogic ); dmnObject && consumer(dmnObject as T); } if (state.dmn.model.definitions.drgElement?.[node?.data.index ?? 0]?.__$$element === "decision") { const dmnObject = getDmnObjectByPath( expressionPath ?? [], - (state.dmn.model.definitions.drgElement?.[node?.data.index ?? 0] as DMN15__tDecision)?.expression + (state.dmn.model.definitions.drgElement?.[node?.data.index ?? 0] as Normalized) + ?.expression ); dmnObject && consumer(dmnObject as T); } diff --git a/packages/dmn-editor/src/propertiesPanel/DecisionProperties.tsx b/packages/dmn-editor/src/propertiesPanel/DecisionProperties.tsx index a51e9d40ed9..7a4fda0e62b 100644 --- a/packages/dmn-editor/src/propertiesPanel/DecisionProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/DecisionProperties.tsx @@ -30,13 +30,15 @@ import { InlineFeelNameInput } from "../feel/InlineFeelNameInput"; import { useDmnEditor } from "../DmnEditorContext"; import { useResolvedTypeRef } from "../dataTypes/useResolvedTypeRef"; import { useCallback } from "react"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function DecisionProperties({ decision, namespace, index, }: { - decision: DMN15__tDecision; + decision: Normalized; namespace: string | undefined; index: number; }) { @@ -80,8 +82,8 @@ export function DecisionProperties({ isDisabled={isReadonly} onChange={(newTypeRef) => { setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tDecision; - drgElement.variable ??= { "@_name": decision["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": decision["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }} @@ -96,7 +98,7 @@ export function DecisionProperties({ value={decision.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecision).description = { + (state.dmn.model.definitions.drgElement![index] as Normalized).description = { __$$text: newDescription, }; }); @@ -121,7 +123,9 @@ export function DecisionProperties({ value={decision.question?.__$$text} onChange={(newQuestion) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecision).question = { __$$text: newQuestion }; + (state.dmn.model.definitions.drgElement![index] as Normalized).question = { + __$$text: newQuestion, + }; }); }} placeholder={"Enter a question..."} @@ -138,7 +142,7 @@ export function DecisionProperties({ value={decision.allowedAnswers?.__$$text} onChange={(newAllowedAnswers) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecision).allowedAnswers = { + (state.dmn.model.definitions.drgElement![index] as Normalized).allowedAnswers = { __$$text: newAllowedAnswers, }; }); @@ -154,7 +158,7 @@ export function DecisionProperties({ values={decision.extensionElements?.["kie:attachment"]} onChange={(newExtensionElements) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecision).extensionElements = { + (state.dmn.model.definitions.drgElement![index] as Normalized).extensionElements = { "kie:attachment": newExtensionElements, }; }); diff --git a/packages/dmn-editor/src/propertiesPanel/DecisionServiceProperties.tsx b/packages/dmn-editor/src/propertiesPanel/DecisionServiceProperties.tsx index 803bf9cf870..76adad60526 100644 --- a/packages/dmn-editor/src/propertiesPanel/DecisionServiceProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/DecisionServiceProperties.tsx @@ -42,10 +42,13 @@ import { useExternalModels } from "../includedModels/DmnEditorDependenciesContex import { DragAndDrop, Draggable } from "../draggable/Draggable"; import { buildFeelQNameFromNamespace } from "../feel/buildFeelQName"; import { Alert, AlertVariant } from "@patternfly/react-core/dist/js/components/Alert/Alert"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export type AllKnownDrgElementsByHref = Map< string, - ({ __$$element: "decision" } & DMN15__tDecision) | ({ __$$element: "inputData" } & DMN15__tInputData) + | ({ __$$element: "decision" } & Normalized) + | ({ __$$element: "inputData" } & Normalized) >; export function DecisionServiceProperties({ @@ -53,7 +56,7 @@ export function DecisionServiceProperties({ namespace, index, }: { - decisionService: DMN15__tDecisionService; + decisionService: Normalized; namespace: string | undefined; index: number; }) { @@ -125,8 +128,8 @@ export function DecisionServiceProperties({ isDisabled={isReadonly} onChange={(newTypeRef) => { setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tDecisionService; - drgElement.variable ??= { "@_name": decisionService["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": decisionService["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }} @@ -141,7 +144,7 @@ export function DecisionServiceProperties({ value={decisionService.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecisionService).description = { + (state.dmn.model.definitions.drgElement![index] as Normalized).description = { __$$text: newDescription, }; }); @@ -181,7 +184,7 @@ export function DecisionServiceProperties({ allDrgElementsByHref={allDrgElementsByHref} onChange={(newInputDecisions) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecisionService).inputDecision = + (state.dmn.model.definitions.drgElement![index] as Normalized).inputDecision = newInputDecisions; }); }} @@ -195,7 +198,8 @@ export function DecisionServiceProperties({ allDrgElementsByHref={allDrgElementsByHref} onChange={(newInputData) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecisionService).inputData = newInputData; + (state.dmn.model.definitions.drgElement![index] as Normalized).inputData = + newInputData; }); }} isDisabled={isReadonly} @@ -213,9 +217,10 @@ export function DecisionServiceProperties({ values={decisionService.extensionElements?.["kie:attachment"]} onChange={(newExtensionElements) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tDecisionService).extensionElements = { - "kie:attachment": newExtensionElements, - }; + (state.dmn.model.definitions.drgElement![index] as Normalized).extensionElements = + { + "kie:attachment": newExtensionElements, + }; }); }} /> @@ -229,7 +234,7 @@ export function DecisionServiceElementList({ allDrgElementsByHref, }: { decisionServiceNamespace: string | undefined; - elements: DMN15__tDecisionService["outputDecision"]; + elements: Normalized["outputDecision"]; allDrgElementsByHref: AllKnownDrgElementsByHref; }) { const thisDmnsNamespace = useDmnEditorStore((s) => s.dmn.model.definitions["@_namespace"]); @@ -282,9 +287,9 @@ export function DraggableDecisionServiceElementList({ isDisabled, }: { decisionServiceNamespace: string | undefined; - elements: DMN15__tDecisionService["outputDecision"]; + elements: Normalized["outputDecision"]; allDrgElementsByHref: AllKnownDrgElementsByHref; - onChange: (hrefs: DMN15__tDMNElementReference[] | undefined) => void; + onChange: (hrefs: Normalized[] | undefined) => void; isDisabled: boolean; }) { const thisDmnsNamespace = useDmnEditorStore((s) => s.dmn.model.definitions["@_namespace"]); @@ -310,7 +315,7 @@ export function DraggableDecisionServiceElementList({ }, []); const draggableItem = useCallback( - (element: DMN15__tDMNElementReference, index: number) => { + (element: Normalized, index: number) => { const localHref = parseXmlHref(element["@_href"]); // If the localHref has a namespace, then that's the one to use, as it can be that an external node is pointing to another external node in their perspective @@ -374,7 +379,7 @@ function DecisionServiceEquivalentFunction({ allDrgElementsByHref, decisionServiceNamespace, }: { - decisionService: DMN15__tDecisionService; + decisionService: Normalized; allDrgElementsByHref: AllKnownDrgElementsByHref; decisionServiceNamespace: string | undefined; }) { @@ -413,7 +418,10 @@ function DecisionServiceEquivalentFunction({ ); const buildFunctionArgList = useCallback( - (inputDecisions?: DMN15__tDMNElementReference[], inputData?: DMN15__tDMNElementReference[]) => { + ( + inputDecisions?: Normalized[], + inputData?: Normalized[] + ) => { const inputDecisionNodeNames = inputDecisions?.map((ide) => getNodeNameByHref(ide["@_href"])); const inputDataNodeNames = inputData?.map((ida) => getNodeNameByHref(ida["@_href"])); diff --git a/packages/dmn-editor/src/propertiesPanel/DiagramPropertiesPanel.css b/packages/dmn-editor/src/propertiesPanel/DiagramPropertiesPanel.css index 5413247ee38..aef65c9582b 100644 --- a/packages/dmn-editor/src/propertiesPanel/DiagramPropertiesPanel.css +++ b/packages/dmn-editor/src/propertiesPanel/DiagramPropertiesPanel.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .pf-c-form__field-group-body { grid-column-start: 1; padding-left: 20px; diff --git a/packages/dmn-editor/src/propertiesPanel/DocumentationLinksFormGroup.css b/packages/dmn-editor/src/propertiesPanel/DocumentationLinksFormGroup.css index 3d7f7c9eed7..df69f04614c 100644 --- a/packages/dmn-editor/src/propertiesPanel/DocumentationLinksFormGroup.css +++ b/packages/dmn-editor/src/propertiesPanel/DocumentationLinksFormGroup.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--documentation-link--empty-state { padding: 10px; background: #eee; diff --git a/packages/dmn-editor/src/propertiesPanel/FontOptions.css b/packages/dmn-editor/src/propertiesPanel/FontOptions.css index 82c35402547..191fbd1024f 100644 --- a/packages/dmn-editor/src/propertiesPanel/FontOptions.css +++ b/packages/dmn-editor/src/propertiesPanel/FontOptions.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--font-options-toggle-group { display: flex; flex-direction: row; diff --git a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx index 13a9f47f8ed..e63da7c1ffd 100644 --- a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx +++ b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx @@ -35,6 +35,8 @@ import { useDmnEditorStore, useDmnEditorStoreApi } from "../store/StoreContext"; import { ColorPicker } from "./ColorPicker"; import { PropertiesPanelHeader } from "./PropertiesPanelHeader"; import "./FontOptions.css"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; +import { Normalized } from "../normalization/normalize"; // https://www.w3schools.com/cssref/css_websafe_fonts.php // Array of [name, family] @@ -77,18 +79,21 @@ export function FontOptions({ startExpanded, nodeIds }: { startExpanded: boolean const isFontStrikeThrough = useMemo(() => shapeStyles[0]?.["@_fontStrikeThrough"] ?? false, [shapeStyles]); const fontSize = useMemo(() => shapeStyles[0]?.["@_fontSize"] ?? DEFAULT_FONT_SIZE, [shapeStyles]); const fontColor = useMemo(() => { - const b = (shapeStyles[0]?.["dmndi:FontColor"]?.["@_blue"] ?? DEFAULT_FONT_COLOR["@_red"]).toString(16); + const b = (shapeStyles[0]?.["dmndi:FontColor"]?.["@_blue"] ?? DEFAULT_FONT_COLOR["@_blue"]).toString(16); const g = (shapeStyles[0]?.["dmndi:FontColor"]?.["@_green"] ?? DEFAULT_FONT_COLOR["@_green"]).toString(16); - const r = (shapeStyles[0]?.["dmndi:FontColor"]?.["@_red"] ?? DEFAULT_FONT_COLOR["@_blue"]).toString(16); + const r = (shapeStyles[0]?.["dmndi:FontColor"]?.["@_red"] ?? DEFAULT_FONT_COLOR["@_red"]).toString(16); return `#${r.length === 1 ? "0" + r : r}${g.length === 1 ? "0" + g : g}${b.length === 1 ? "0" + b : b}`; }, [shapeStyles]); const [isStyleSectionExpanded, setStyleSectionExpanded] = useState(startExpanded); const setShapeStyles = useCallback( - (callback: (shape: DMNDI15__DMNShape[], state: State) => void) => { + (callback: (shape: Normalized[], state: State) => void) => { dmnEditorStoreApi.setState((s) => { - const { diagramElements } = addOrGetDrd({ definitions: s.dmn.model.definitions, drdIndex: s.diagram.drdIndex }); + const { diagramElements } = addOrGetDrd({ + definitions: s.dmn.model.definitions, + drdIndex: s.computed(s).getDrdIndex(), + }); const shapes = nodeIds.map((nodeId) => { const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId); @@ -100,7 +105,7 @@ export function FontOptions({ startExpanded, nodeIds }: { startExpanded: boolean }); for (const shape of shapes) { - shape["di:Style"] ??= { __$$element: "dmndi:DMNStyle" }; + shape["di:Style"] ??= { "@_id": generateUuid(), __$$element: "dmndi:DMNStyle" }; } callback(shapes, s); @@ -233,7 +238,7 @@ export function FontOptions({ startExpanded, nodeIds }: { startExpanded: boolean setShapeStyles((shapes, state) => { shapes.forEach((shape) => { state.diagram.isEditingStyle = false; - shape["di:Style"]!["dmndi:FontColor"] ??= DEFAULT_FONT_COLOR; + shape["di:Style"]!["dmndi:FontColor"] ??= { ...DEFAULT_FONT_COLOR }; shape["di:Style"]!["dmndi:FontColor"]["@_red"] = parseInt(temporaryFontColor.slice(0, 2), 16); shape["di:Style"]!["dmndi:FontColor"]["@_green"] = parseInt(temporaryFontColor.slice(2, 4), 16); shape["di:Style"]!["dmndi:FontColor"]["@_blue"] = parseInt(temporaryFontColor.slice(4, 6), 16); @@ -256,10 +261,7 @@ export function FontOptions({ startExpanded, nodeIds }: { startExpanded: boolean shape["di:Style"]!["@_fontStrikeThrough"] = undefined; shape["di:Style"]!["@_fontSize"] = undefined; shape["di:Style"]!["@_fontFamily"] = undefined; - shape["di:Style"]!["dmndi:FontColor"] ??= DEFAULT_FONT_COLOR; - shape["di:Style"]!["dmndi:FontColor"]["@_red"] = DEFAULT_FONT_COLOR["@_red"]; - shape["di:Style"]!["dmndi:FontColor"]["@_green"] = DEFAULT_FONT_COLOR["@_green"]; - shape["di:Style"]!["dmndi:FontColor"]["@_blue"] = DEFAULT_FONT_COLOR["@_blue"]; + shape["di:Style"]!["dmndi:FontColor"] = { ...DEFAULT_FONT_COLOR }; }); }); }, [setShapeStyles]); diff --git a/packages/dmn-editor/src/propertiesPanel/GroupProperties.tsx b/packages/dmn-editor/src/propertiesPanel/GroupProperties.tsx index 3e4f20bded7..5b6625120f6 100644 --- a/packages/dmn-editor/src/propertiesPanel/GroupProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/GroupProperties.tsx @@ -25,8 +25,9 @@ import { TextArea } from "@patternfly/react-core/dist/js/components/TextArea"; import { TextInput } from "@patternfly/react-core/dist/js/components/TextInput"; import { useDmnEditorStoreApi } from "../store/StoreContext"; import { renameGroupNode } from "../mutations/renameNode"; +import { Normalized } from "../normalization/normalize"; -export function GroupProperties({ group, index }: { group: DMN15__tGroup; index: number }) { +export function GroupProperties({ group, index }: { group: Normalized; index: number }) { const { setState } = useDmnEditorStoreApi(); return ( @@ -58,7 +59,7 @@ export function GroupProperties({ group, index }: { group: DMN15__tGroup; index: value={group.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.artifact![index] as DMN15__tGroup).description = { + (state.dmn.model.definitions.artifact![index] as Normalized).description = { __$$text: newDescription, }; }); diff --git a/packages/dmn-editor/src/propertiesPanel/InputDataProperties.tsx b/packages/dmn-editor/src/propertiesPanel/InputDataProperties.tsx index eddba2809a6..76d36a1e611 100644 --- a/packages/dmn-editor/src/propertiesPanel/InputDataProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/InputDataProperties.tsx @@ -30,13 +30,15 @@ import { InlineFeelNameInput } from "../feel/InlineFeelNameInput"; import { useDmnEditor } from "../DmnEditorContext"; import { useResolvedTypeRef } from "../dataTypes/useResolvedTypeRef"; import { useCallback } from "react"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; export function InputDataProperties({ inputData, namespace, index, }: { - inputData: DMN15__tInputData; + inputData: Normalized; namespace: string | undefined; index: number; }) { @@ -79,8 +81,8 @@ export function InputDataProperties({ isDisabled={isReadonly} onChange={(newTypeRef) => { setState((state) => { - const drgElement = state.dmn.model.definitions.drgElement![index] as DMN15__tInputData; - drgElement.variable ??= { "@_name": inputData["@_name"] }; + const drgElement = state.dmn.model.definitions.drgElement![index] as Normalized; + drgElement.variable ??= { "@_id": generateUuid(), "@_name": inputData["@_name"] }; drgElement.variable["@_typeRef"] = newTypeRef; }); }} @@ -94,7 +96,7 @@ export function InputDataProperties({ value={inputData.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tInputData).description = { + (state.dmn.model.definitions.drgElement![index] as Normalized).description = { __$$text: newDescription, }; }); @@ -116,7 +118,7 @@ export function InputDataProperties({ values={inputData.extensionElements?.["kie:attachment"]} onChange={(newExtensionElements) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tInputData).extensionElements = { + (state.dmn.model.definitions.drgElement![index] as Normalized).extensionElements = { "kie:attachment": newExtensionElements, }; }); diff --git a/packages/dmn-editor/src/propertiesPanel/KnowledgeSourceProperties.tsx b/packages/dmn-editor/src/propertiesPanel/KnowledgeSourceProperties.tsx index 36af3c30a17..4c43b83be96 100644 --- a/packages/dmn-editor/src/propertiesPanel/KnowledgeSourceProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/KnowledgeSourceProperties.tsx @@ -28,13 +28,14 @@ import { useDmnEditorStore, useDmnEditorStoreApi } from "../store/StoreContext"; import { renameDrgElement } from "../mutations/renameNode"; import { InlineFeelNameInput } from "../feel/InlineFeelNameInput"; import { useCallback } from "react"; +import { Normalized } from "../normalization/normalize"; export function KnowledgeSourceProperties({ knowledgeSource, namespace, index, }: { - knowledgeSource: DMN15__tKnowledgeSource; + knowledgeSource: Normalized; namespace: string | undefined; index: number; }) { @@ -75,7 +76,7 @@ export function KnowledgeSourceProperties({ value={knowledgeSource.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tKnowledgeSource).description = { + (state.dmn.model.definitions.drgElement![index] as Normalized).description = { __$$text: newDescription, }; }); @@ -100,7 +101,9 @@ export function KnowledgeSourceProperties({ value={knowledgeSource.type?.__$$text} onChange={(newType) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tKnowledgeSource).type = { __$$text: newType }; + (state.dmn.model.definitions.drgElement![index] as Normalized).type = { + __$$text: newType, + }; }); }} placeholder={"Enter source type..."} @@ -115,7 +118,7 @@ export function KnowledgeSourceProperties({ value={knowledgeSource["@_locationURI"]} onChange={(newLocationUri) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tKnowledgeSource)["@_locationURI"] = + (state.dmn.model.definitions.drgElement![index] as Normalized)["@_locationURI"] = newLocationUri; }); }} @@ -128,9 +131,10 @@ export function KnowledgeSourceProperties({ values={knowledgeSource.extensionElements?.["kie:attachment"]} onChange={(newExtensionElements) => { setState((state) => { - (state.dmn.model.definitions.drgElement![index] as DMN15__tKnowledgeSource).extensionElements = { - "kie:attachment": newExtensionElements, - }; + (state.dmn.model.definitions.drgElement![index] as Normalized).extensionElements = + { + "kie:attachment": newExtensionElements, + }; }); }} /> diff --git a/packages/dmn-editor/src/propertiesPanel/PropertiesPanelHeader.css b/packages/dmn-editor/src/propertiesPanel/PropertiesPanelHeader.css index ec390d6bcad..f8d7545a242 100644 --- a/packages/dmn-editor/src/propertiesPanel/PropertiesPanelHeader.css +++ b/packages/dmn-editor/src/propertiesPanel/PropertiesPanelHeader.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--properties-panel-header { padding-bottom: 10px; } diff --git a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.css b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.css index fdf2523253e..c3204e91758 100644 --- a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.css +++ b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--shape-options-toggle-button > button { height: 48px; align-items: center; diff --git a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx index 6353afeedcc..e144015f510 100644 --- a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx +++ b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx @@ -39,6 +39,8 @@ import { MIN_NODE_SIZES } from "../diagram/nodes/DefaultSizes"; import { NodeType } from "../diagram/connections/graphStructure"; import { Button, ButtonVariant } from "@patternfly/react-core/dist/js/components/Button"; import { DC__Dimension } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_2/ts-gen/types"; +import { Normalized } from "../normalization/normalize"; +import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api"; const DEFAULT_FILL_COLOR = { "@_blue": 255, "@_green": 255, "@_red": 255 }; const DEFAULT_STROKE_COLOR = { "@_blue": 0, "@_green": 0, "@_red": 0 }; @@ -91,16 +93,16 @@ export function ShapeOptions({ }, [boundHeight, nodeIds]); const fillColor = useMemo(() => { - const b = (shapeStyles[0]?.["dmndi:FillColor"]?.["@_blue"] ?? DEFAULT_FILL_COLOR["@_red"]).toString(16); + const b = (shapeStyles[0]?.["dmndi:FillColor"]?.["@_blue"] ?? DEFAULT_FILL_COLOR["@_blue"]).toString(16); const g = (shapeStyles[0]?.["dmndi:FillColor"]?.["@_green"] ?? DEFAULT_FILL_COLOR["@_green"]).toString(16); - const r = (shapeStyles[0]?.["dmndi:FillColor"]?.["@_red"] ?? DEFAULT_FILL_COLOR["@_blue"]).toString(16); + const r = (shapeStyles[0]?.["dmndi:FillColor"]?.["@_red"] ?? DEFAULT_FILL_COLOR["@_red"]).toString(16); return `#${r.length === 1 ? "0" + r : r}${g.length === 1 ? "0" + g : g}${b.length === 1 ? "0" + b : b}`; }, [shapeStyles]); const strokeColor = useMemo(() => { - const b = (shapeStyles[0]?.["dmndi:StrokeColor"]?.["@_blue"] ?? DEFAULT_STROKE_COLOR["@_red"]).toString(16); + const b = (shapeStyles[0]?.["dmndi:StrokeColor"]?.["@_blue"] ?? DEFAULT_STROKE_COLOR["@_blue"]).toString(16); const g = (shapeStyles[0]?.["dmndi:StrokeColor"]?.["@_green"] ?? DEFAULT_STROKE_COLOR["@_green"]).toString(16); - const r = (shapeStyles[0]?.["dmndi:StrokeColor"]?.["@_red"] ?? DEFAULT_STROKE_COLOR["@_blue"]).toString(16); + const r = (shapeStyles[0]?.["dmndi:StrokeColor"]?.["@_red"] ?? DEFAULT_STROKE_COLOR["@_red"]).toString(16); return `#${r.length === 1 ? "0" + r : r}${g.length === 1 ? "0" + g : g}${b.length === 1 ? "0" + b : b}`; }, [shapeStyles]); @@ -109,7 +111,10 @@ export function ShapeOptions({ const setBounds = useCallback( (callback: (bounds: DC__Bounds, state: State) => void, nodeId: string) => { dmnEditorStoreApi.setState((s) => { - const { diagramElements } = addOrGetDrd({ definitions: s.dmn.model.definitions, drdIndex: s.diagram.drdIndex }); + const { diagramElements } = addOrGetDrd({ + definitions: s.dmn.model.definitions, + drdIndex: s.computed(s).getDrdIndex(), + }); const index = s.computed(s).indexedDrd()?.dmnShapesByHref?.get(nodeId)?.index ?? -1; if (index < 0) { @@ -199,12 +204,15 @@ export function ShapeOptions({ const setShapeStyles = useCallback( ( callback: ( - shapesWithMinNodeSize: { shape: DMNDI15__DMNShape; minNodeSize: DC__Dimension }[], + shapesWithMinNodeSize: { shape: Normalized; minNodeSize: DC__Dimension }[], state: State ) => void ) => { dmnEditorStoreApi.setState((s) => { - const { diagramElements } = addOrGetDrd({ definitions: s.dmn.model.definitions, drdIndex: s.diagram.drdIndex }); + const { diagramElements } = addOrGetDrd({ + definitions: s.dmn.model.definitions, + drdIndex: s.computed(s).getDrdIndex(), + }); const shapesWithMinNodeSize = nodeIds.map((nodeId) => { const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId); @@ -228,7 +236,7 @@ export function ShapeOptions({ throw new Error(`DMN Element with index ${i++} is not a DMNShape.`); } - shape["di:Style"] ??= { __$$element: "dmndi:DMNStyle" }; + shape["di:Style"] ??= { "@_id": generateUuid(), __$$element: "dmndi:DMNStyle" }; } callback(shapesWithMinNodeSize, s); @@ -312,6 +320,7 @@ export function ShapeOptions({ shapeWithNodes.forEach(({ shape, minNodeSize }) => { shape["di:Style"] ??= { __$$element: "dmndi:DMNStyle", + "@_id": generateUuid(), "dmndi:FillColor": { ...DEFAULT_FILL_COLOR }, "dmndi:StrokeColor": { ...DEFAULT_STROKE_COLOR }, }; @@ -320,7 +329,7 @@ export function ShapeOptions({ shape["dc:Bounds"] ??= { "@_width": minNodeSize["@_width"], - "@_height": minNodeSize["@_width"], + "@_height": minNodeSize["@_height"], "@_x": 0, "@_y": 0, }; diff --git a/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.css b/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.css index eacb0ce9da8..f861ec9e08d 100644 --- a/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.css +++ b/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.css @@ -1,3 +1,22 @@ +/* + * 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. + */ + .kie-dmn-editor--single-node-properties-title-colapsed { margin-bottom: 40px; } diff --git a/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.tsx b/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.tsx index 5c36c900ba6..2be84cd41d7 100644 --- a/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/SingleNodeProperties.tsx @@ -50,6 +50,7 @@ import { PropertiesPanelHeader } from "./PropertiesPanelHeader"; import { UnknownProperties } from "./UnknownProperties"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; import "./SingleNodeProperties.css"; +import { Normalized } from "../normalization/normalize"; export function SingleNodeProperties({ nodeId }: { nodeId: string }) { const dmnEditorStoreApi = useDmnEditorStoreApi(); @@ -133,7 +134,7 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.inputData: return ( } namespace={node.data.dmnObjectNamespace} index={node.data.index} /> @@ -141,7 +142,7 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.decision: return ( } namespace={node.data.dmnObjectNamespace} index={node.data.index} /> @@ -149,7 +150,7 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.bkm: return ( } namespace={node.data.dmnObjectNamespace} index={node.data.index} /> @@ -157,7 +158,7 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.decisionService: return ( } namespace={node.data.dmnObjectNamespace} index={node.data.index} /> @@ -165,7 +166,7 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.knowledgeSource: return ( } namespace={node.data.dmnObjectNamespace} index={node.data.index} /> @@ -173,12 +174,17 @@ export function SingleNodeProperties({ nodeId }: { nodeId: string }) { case NODE_TYPES.textAnnotation: return ( } index={node.data.index} /> ); case NODE_TYPES.group: - return ; + return ( + } + index={node.data.index} + /> + ); case NODE_TYPES.unknown: return ; default: diff --git a/packages/dmn-editor/src/propertiesPanel/TextAnnotationProperties.tsx b/packages/dmn-editor/src/propertiesPanel/TextAnnotationProperties.tsx index 574ab6c6733..a232f87e8e7 100644 --- a/packages/dmn-editor/src/propertiesPanel/TextAnnotationProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/TextAnnotationProperties.tsx @@ -25,12 +25,13 @@ import { TextArea } from "@patternfly/react-core/dist/js/components/TextArea"; import { TextInput } from "@patternfly/react-core/dist/js/components/TextInput"; import { useDmnEditorStoreApi } from "../store/StoreContext"; import { updateTextAnnotation } from "../mutations/renameNode"; +import { Normalized } from "../normalization/normalize"; export function TextAnnotationProperties({ textAnnotation, index, }: { - textAnnotation: DMN15__tTextAnnotation; + textAnnotation: Normalized; index: number; }) { const { setState } = useDmnEditorStoreApi(); @@ -46,7 +47,8 @@ export function TextAnnotationProperties({ placeholder={"Enter a text format..."} onChange={(newTextFormat) => { setState((state) => { - (state.dmn.model.definitions.artifact![index] as DMN15__tTextAnnotation)["@_textFormat"] = newTextFormat; + (state.dmn.model.definitions.artifact![index] as Normalized)["@_textFormat"] = + newTextFormat; }); }} /> @@ -81,7 +83,7 @@ export function TextAnnotationProperties({ value={textAnnotation.description?.__$$text} onChange={(newDescription) => { setState((state) => { - (state.dmn.model.definitions.artifact![index] as DMN15__tTextAnnotation).description = { + (state.dmn.model.definitions.artifact![index] as Normalized).description = { __$$text: newDescription, }; }); diff --git a/packages/dmn-editor/src/propertiesPanel/UnknownProperties.tsx b/packages/dmn-editor/src/propertiesPanel/UnknownProperties.tsx index a94148e4c3b..83dfedb4ab0 100644 --- a/packages/dmn-editor/src/propertiesPanel/UnknownProperties.tsx +++ b/packages/dmn-editor/src/propertiesPanel/UnknownProperties.tsx @@ -28,8 +28,9 @@ import { Button, ButtonVariant } from "@patternfly/react-core/dist/js/components import { useDmnEditor } from "../DmnEditorContext"; import { Divider } from "@patternfly/react-core/dist/js/components/Divider"; import { useExternalModels } from "../includedModels/DmnEditorDependenciesContext"; +import { Normalized } from "../normalization/normalize"; -export function UnknownProperties(props: { shape: DMNDI15__DMNShape; dmnElementRefQName: XmlQName }) { +export function UnknownProperties(props: { shape: Normalized; dmnElementRefQName: XmlQName }) { const thisDmn = useDmnEditorStore((s) => s.dmn); const { externalModelsByNamespace } = useExternalModels(); const externalDmnsByNamespace = useDmnEditorStore( @@ -53,7 +54,7 @@ export function UnknownProperties(props: { shape: DMNDI15__DMNShape; dmnElementR const externalDrgElementsById = (externalDmn.model.definitions.drgElement ?? []).reduce( (acc, e, index) => acc.set(e["@_id"]!, { element: e, index }), - new Map }>() + new Map["drgElement"]> }>() ); const externalDrgElement = externalDrgElementsById.get(props.dmnElementRefQName.localPart); diff --git a/packages/dmn-editor/src/store/Store.ts b/packages/dmn-editor/src/store/Store.ts index 108f9fa5385..21f04b3bf74 100644 --- a/packages/dmn-editor/src/store/Store.ts +++ b/packages/dmn-editor/src/store/Store.ts @@ -25,7 +25,7 @@ import { create } from "zustand"; import { immer } from "zustand/middleware/immer"; import { ExternalModelsIndex } from "../DmnEditor"; import { DmnDiagramNodeData } from "../diagram/nodes/Nodes"; -import { normalize } from "../normalization/normalize"; +import { Normalized, normalize } from "../normalization/normalize"; import { ComputedStateCache } from "./ComputedStateCache"; import { computeAllFeelVariableUniqueNames } from "./computed/computeAllFeelVariableUniqueNames"; import { computeDataTypes } from "./computed/computeDataTypes"; @@ -69,7 +69,7 @@ export type DropTargetNode = undefined | RF.Node; export interface State { dispatch: (s: State) => Dispatch; computed: (s: State) => Computed; - dmn: { model: DmnLatestModel }; + dmn: { model: Normalized }; focus: { consumableId: string | undefined; }; @@ -88,7 +88,10 @@ export interface State { tab: DmnEditorTab; }; diagram: { - drdIndex: number; + autoLayout: { + canAutoGenerateDrd: boolean; + }; + __unsafeDrdIndex: number; edgeIdBeingUpdated: string | undefined; dropTargetNode: DropTargetNode; ongoingConnection: RF.OnConnectStartParams | undefined; @@ -136,6 +139,12 @@ export type Computed = { e: ExternalModelsIndex | undefined ) => ReturnType; + /** + * Get a valid DRD index. + * `__unsafeDrdIndex` can point to a DRD that doens't exist. + */ + getDrdIndex(): number; + getDataTypes(e: ExternalModelsIndex | undefined): ReturnType; getAllFeelVariableUniqueNames(): ReturnType; @@ -181,7 +190,10 @@ export const defaultStaticState = (): Omit) { +export function createDmnEditorStore(model: DmnLatestModel, computedCache: ComputedStateCache) { + const { diagram, ...defaultState } = defaultStaticState(); return create( immer(() => ({ dmn: { model: normalize(model), }, - ...defaultStaticState(), + ...defaultState, + diagram: { + ...diagram, + // A model without DRD and with DRG element can be auto generated + autoLayout: { + canAutoGenerateDrd: + model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] === undefined && + model.definitions.drgElement !== undefined, + }, + }, dispatch(s: State) { return { dmn: { @@ -334,7 +356,7 @@ export function createDmnEditorStore(model: State["dmn"]["model"], computedCache return computedCache.cached("indexedDrd", computeIndexedDrd, [ s.dmn.model.definitions["@_namespace"], s.dmn.model.definitions, - s.diagram.drdIndex, + s.computed(s).getDrdIndex(), ]); }, @@ -348,7 +370,7 @@ export function createDmnEditorStore(model: State["dmn"]["model"], computedCache computedCache.cached( "isAlternativeInputDataShape", (drdIndex, dmnDiagram) => dmnDiagram?.[drdIndex]?.["@_useAlternativeInputDataShape"] ?? false, - [s.diagram.drdIndex, s.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]] as const + [s.computed(s).getDrdIndex(), s.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]] as const ), isDropTargetNodeValidForSelection: (externalModelsByNamespace: ExternalModelsIndex | undefined) => @@ -357,6 +379,16 @@ export function createDmnEditorStore(model: State["dmn"]["model"], computedCache s.computed(s).getDiagramData(externalModelsByNamespace), ]), + getDrdIndex: () => + computedCache.cached( + "getDrdIndex", + (__unsafeDrdIndex, dmnDiagram) => + dmnDiagram?.length && __unsafeDrdIndex > dmnDiagram.length - 1 + ? dmnDiagram.length - 1 + : __unsafeDrdIndex, + [s.diagram.__unsafeDrdIndex, s.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]] as const + ), + getDataTypes: (externalModelsByNamespace: ExternalModelsIndex | undefined) => computedCache.cached("getDataTypes", computeDataTypes, [ s.dmn.model.definitions["@_namespace"], diff --git a/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts b/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts index 1c579853d9f..6365fa7fb88 100644 --- a/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts +++ b/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts @@ -31,7 +31,7 @@ export function computeContainingDecisionServiceHrefsByDecisionHrefs({ drgElements: State["dmn"]["model"]["definitions"]["drgElement"]; }) { drgElements ??= []; - const decisionServiecHrefsByDecisionHrefs = new Map(); + const decisionServiceHrefsByDecisionHrefs = new Map(); for (const drgElement of drgElements) { const drgElementHref = buildXmlHref({ @@ -41,9 +41,9 @@ export function computeContainingDecisionServiceHrefsByDecisionHrefs({ // Decision if (drgElement.__$$element === "decision") { - decisionServiecHrefsByDecisionHrefs.set( + decisionServiceHrefsByDecisionHrefs.set( drgElementHref, - decisionServiecHrefsByDecisionHrefs.get(drgElementHref) ?? [] + decisionServiceHrefsByDecisionHrefs.get(drgElementHref) ?? [] ); } // DS @@ -55,8 +55,8 @@ export function computeContainingDecisionServiceHrefsByDecisionHrefs({ }); for (const containedDecisionHref of containedDecisionHrefsRelativeToThisDmn) { - decisionServiecHrefsByDecisionHrefs.set(containedDecisionHref, [ - ...(decisionServiecHrefsByDecisionHrefs.get(containedDecisionHref) ?? []), + decisionServiceHrefsByDecisionHrefs.set(containedDecisionHref, [ + ...(decisionServiceHrefsByDecisionHrefs.get(containedDecisionHref) ?? []), drgElementHref, ]); } @@ -65,5 +65,5 @@ export function computeContainingDecisionServiceHrefsByDecisionHrefs({ } } - return decisionServiecHrefsByDecisionHrefs; + return decisionServiceHrefsByDecisionHrefs; } diff --git a/packages/dmn-editor/src/store/computed/computeDataTypes.ts b/packages/dmn-editor/src/store/computed/computeDataTypes.ts index 1851d584e7b..baa3c388179 100644 --- a/packages/dmn-editor/src/store/computed/computeDataTypes.ts +++ b/packages/dmn-editor/src/store/computed/computeDataTypes.ts @@ -24,6 +24,7 @@ import { DataType, DataTypeIndex } from "../../dataTypes/DataTypes"; import { buildFeelQNameFromNamespace } from "../../feel/buildFeelQName"; import { TypeOrReturnType } from "../ComputedStateCache"; import { Computed, State } from "../Store"; +import { Normalized } from "../../normalization/normalize"; export function computeDataTypes( namespace: State["dmn"]["model"]["definitions"]["@_namespace"], @@ -79,7 +80,7 @@ export function computeDataTypes( } export function buildDataTypesTree( - items: DMN15__tItemDefinition[], + items: Normalized[], importsByNamespace: Map, allDataTypesById: DataTypeIndex, allTopLevelDataTypesByFeelName: DataTypeIndex, diff --git a/packages/dmn-editor/src/store/computed/computeDiagramData.ts b/packages/dmn-editor/src/store/computed/computeDiagramData.ts index a5cd4e36b93..69552fa0412 100644 --- a/packages/dmn-editor/src/store/computed/computeDiagramData.ts +++ b/packages/dmn-editor/src/store/computed/computeDiagramData.ts @@ -36,6 +36,8 @@ import { buildXmlHref, parseXmlHref } from "../../xml/xmlHrefs"; import { TypeOrReturnType } from "../ComputedStateCache"; import { Computed, State } from "../Store"; import { getDecisionServicePropertiesRelativeToThisDmn } from "../../mutations/addExistingDecisionServiceToDrd"; +import { Normalized } from "../../normalization/normalize"; +import { KIE_UNKNOWN_NAMESPACE } from "../../kie/kie"; export const NODE_LAYERS = { GROUP_NODE: 0, @@ -50,6 +52,7 @@ type AckEdge = (args: { type: EdgeType; source: string; target: string; + sourceNamespace: string | undefined; }) => RF.Edge; type AckNode = ( @@ -77,6 +80,8 @@ export function computeDiagramData( const nodesById = new Map>(); const edgesById = new Map>(); const parentIdsById = new Map(); + const externalNodesByNamespace = new Map>>(); + const edgesFromExternalNodesByNamespace = new Map>>(); const { selectedNodes, draggingNodes, resizingNodes, selectedEdges } = { selectedNodes: new Set(diagram._selectedNodes), @@ -91,7 +96,7 @@ export function computeDiagramData( const drgEdges: DrgEdge[] = []; const drgAdjacencyList: DrgAdjacencyList = new Map(); - const ackEdge: AckEdge = ({ id, type, dmnObject, source, target }) => { + const ackEdge: AckEdge = ({ id, type, dmnObject, source, target, sourceNamespace }) => { const data = { dmnObject, dmnEdge: id ? indexedDrd.dmnEdgesByDmnElementRef.get(id) : undefined, @@ -108,6 +113,13 @@ export function computeDiagramData( selected: selectedEdges.has(id), }; + if (sourceNamespace && sourceNamespace !== KIE_UNKNOWN_NAMESPACE) { + edgesFromExternalNodesByNamespace.set(sourceNamespace, [ + ...(edgesFromExternalNodesByNamespace.get(sourceNamespace) ?? []), + edge, + ]); + } + edgesById.set(edge.id, edge); if (edge.selected) { selectedEdgesById.set(edge.id, edge); @@ -148,6 +160,7 @@ export function computeDiagramData( type: EDGE_TYPES.association, source: dmnObject.sourceRef?.["@_href"], target: dmnObject.targetRef?.["@_href"], + sourceNamespace: undefined, // association are always from the current namespace }); }); @@ -223,6 +236,13 @@ export function computeDiagramData( } } + if (dmnObjectNamespace && dmnObjectNamespace !== KIE_UNKNOWN_NAMESPACE) { + externalNodesByNamespace.set(dmnObjectNamespace, [ + ...(externalNodesByNamespace.get(dmnObjectNamespace) ?? []), + newNode, + ]); + } + nodesById.set(newNode.id, newNode); if (newNode.selected) { selectedNodesById.set(newNode.id, newNode); @@ -260,11 +280,11 @@ export function computeDiagramData( namespace, (externalDmn.model.definitions.drgElement ?? []).reduce( (acc, e, index) => acc.set(e["@_id"]!, { element: e, index }), - new Map }>() + new Map["drgElement"]> }>() ) ); }, - new Map }>>() + new Map["drgElement"]> }>>() ); const externalNodes = [...indexedDrd.dmnShapesByHref.entries()].flatMap(([href, shape]) => { @@ -359,6 +379,8 @@ export function computeDiagramData( nodes: sortedNodes, edges: sortedEdges, edgesById, + externalNodesByNamespace, + edgesFromExternalNodesByNamespace, nodesById, selectedNodeTypes, selectedNodesById, @@ -370,7 +392,7 @@ export function computeDiagramData( function ackRequirementEdges( thisDmnsNamespace: string, drgElementsNamespace: string, - drgElements: DMN15__tDefinitions["drgElement"], + drgElements: Normalized["drgElement"], ackEdge: AckEdge ) { const namespace = drgElementsNamespace === thisDmnsNamespace ? "" : drgElementsNamespace; @@ -392,6 +414,7 @@ function ackRequirementEdges( type: EDGE_TYPES.informationRequirement, source: buildXmlHref({ namespace: irHref.namespace ?? namespace, id: irHref.id }), target: buildXmlHref({ namespace, id: dmnObject["@_id"]! }), + sourceNamespace: irHref.namespace ?? namespace, }); }); } @@ -411,6 +434,7 @@ function ackRequirementEdges( type: EDGE_TYPES.knowledgeRequirement, source: buildXmlHref({ namespace: krHref.namespace ?? namespace, id: krHref.id }), target: buildXmlHref({ namespace, id: dmnObject["@_id"]! }), + sourceNamespace: krHref.namespace ?? namespace, }); }); } @@ -434,6 +458,7 @@ function ackRequirementEdges( type: EDGE_TYPES.authorityRequirement, source: buildXmlHref({ namespace: arHref.namespace ?? namespace, id: arHref.id }), target: buildXmlHref({ namespace, id: dmnObject["@_id"]! }), + sourceNamespace: arHref.namespace ?? namespace, }); }); } diff --git a/packages/dmn-editor/src/store/computed/computeIndexes.ts b/packages/dmn-editor/src/store/computed/computeIndexes.ts index 3d1d4d68c5e..de381e762ce 100644 --- a/packages/dmn-editor/src/store/computed/computeIndexes.ts +++ b/packages/dmn-editor/src/store/computed/computeIndexes.ts @@ -21,15 +21,19 @@ import { DMNDI15__DMNEdge, DMNDI15__DMNShape } from "@kie-tools/dmn-marshaller/d import { XmlQName, parseXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames"; import { KIE_DMN_UNKNOWN_NAMESPACE } from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/Dmn15Spec"; import { buildXmlHref } from "../../xml/xmlHrefs"; -import { State } from "../Store"; +import { Computed, State } from "../Store"; +import { Normalized } from "../../normalization/normalize"; export function computeIndexedDrd( thisDmnsNamespace: string, definitions: State["dmn"]["model"]["definitions"], - drdIndex: State["diagram"]["drdIndex"] + drdIndex: ReturnType ) { - const dmnEdgesByDmnElementRef = new Map(); - const dmnShapesByHref = new Map(); + const dmnEdgesByDmnElementRef = new Map & { index: number }>(); + const dmnShapesByHref = new Map< + string, + Normalized & { index: number; dmnElementRefQName: XmlQName } + >(); const hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects = new Set(); const diagramElements = diff --git a/packages/dmn-editor/src/store/computed/initial.ts b/packages/dmn-editor/src/store/computed/initial.ts index 89c8d98bead..7efe970d57e 100644 --- a/packages/dmn-editor/src/store/computed/initial.ts +++ b/packages/dmn-editor/src/store/computed/initial.ts @@ -57,4 +57,8 @@ export const INITIAL_COMPUTED_CACHE: Cache = { value: undefined, dependencies: [], }, + getDrdIndex: { + value: undefined, + dependencies: [], + }, }; diff --git a/packages/dmn-editor/src/xml/xmlHrefs.ts b/packages/dmn-editor/src/xml/xmlHrefs.ts index 2419b86a930..4fa05444828 100644 --- a/packages/dmn-editor/src/xml/xmlHrefs.ts +++ b/packages/dmn-editor/src/xml/xmlHrefs.ts @@ -39,3 +39,24 @@ export function parseXmlHref(href: string): XmlHref { return { namespace: split[0] ? split[0] : undefined, id: split[1] }; } + +/** + * This function adds a `namespace` to an HREF. This operation will only succed if + * the provided HREF doesn't have a `namespace`. + * + * In case the provided `namespace` is `undefined`, this function will return the HREF. + * + * In case the provided HREF already have an `namespace`, this function will return the HREF. + */ +export function addNamespaceToHref({ href, namespace }: { href: string; namespace: string | undefined }) { + if (namespace === undefined) { + return href; + } + + const { namespace: hrefNamespace, id } = parseXmlHref(href); + if (hrefNamespace !== undefined) { + return href; + } + + return buildXmlHref({ namespace, id }); +} diff --git a/packages/dmn-editor/stories/dev/DevWebApp.stories.tsx b/packages/dmn-editor/stories/dev/DevWebApp.stories.tsx index ce39e136bb0..262231b2523 100644 --- a/packages/dmn-editor/stories/dev/DevWebApp.stories.tsx +++ b/packages/dmn-editor/stories/dev/DevWebApp.stories.tsx @@ -24,12 +24,14 @@ import "@patternfly/react-core/dist/styles/base.css"; import { Flex, FlexItem } from "@patternfly/react-core/dist/js/layouts/Flex"; import { Page, PageSection } from "@patternfly/react-core/dist/js/components/Page"; import { DmnLatestModel, getMarshaller, DmnMarshaller } from "@kie-tools/dmn-marshaller"; +import { Normalized, normalize } from "@kie-tools/dmn-editor/dist/normalization/normalize"; import { availableModelsByPath, modelsByNamespace } from "./availableModelsToInclude"; import { generateEmptyDmn15 } from "../misc/empty/Empty.stories"; import { loanPreQualificationDmn } from "../useCases/loanPreQualification/LoanPreQualification.stories"; import { DmnEditorWrapper } from "../dmnEditorStoriesWrapper"; import { DmnEditorProps, + DmnEditorRef, ExternalModelsIndex, OnDmnModelChange, OnRequestExternalModelByPath, @@ -39,12 +41,40 @@ import { const initialModel = generateEmptyDmn15(); +const emptyDrd = ` + + + + + + + + + + + + + + + + + + + + + +`; + function DevWebApp(args: DmnEditorProps) { - const [state, setState] = useState<{ marshaller: DmnMarshaller; stack: DmnLatestModel[]; pointer: number }>(() => { + const [state, setState] = useState<{ + marshaller: DmnMarshaller; + stack: Normalized[]; + pointer: number; + }>(() => { const initialDmnMarshaller = getMarshaller(initialModel, { upgradeTo: "latest" }); return { marshaller: initialDmnMarshaller, - stack: [initialDmnMarshaller.parser.parse()], + stack: [normalize(initialDmnMarshaller.parser.parse())], pointer: 0, }; }); @@ -61,7 +91,7 @@ function DevWebApp(args: DmnEditorProps) { const reader = new FileReader(); reader.addEventListener("load", ({ target }) => { const marshaller = getMarshaller(target?.result as string, { upgradeTo: "latest" }); - setState({ marshaller, stack: [marshaller.parser.parse()], pointer: 0 }); + setState({ marshaller, stack: [normalize(marshaller.parser.parse())], pointer: 0 }); }); reader.readAsText(item.getAsFile() as any); } @@ -77,7 +107,7 @@ function DevWebApp(args: DmnEditorProps) { const marshaller = getMarshaller(generateEmptyDmn15(), { upgradeTo: "latest" }); setState({ marshaller, - stack: [marshaller.parser.parse()], + stack: [normalize(marshaller.parser.parse())], pointer: 0, }); }, []); @@ -119,7 +149,7 @@ function DevWebApp(args: DmnEditorProps) { const onSelectModel = useCallback( (newModel) => { - onModelChange(getMarshaller(newModel, { upgradeTo: "latest" }).parser.parse()); + onModelChange(normalize(getMarshaller(newModel, { upgradeTo: "latest" }).parser.parse())); }, [onModelChange] ); @@ -163,6 +193,8 @@ function DevWebApp(args: DmnEditorProps) {     +     +     |