From c92683d5ea09d97efa01ef2f93f366865470034d Mon Sep 17 00:00:00 2001 From: Michal Petrov Date: Thu, 23 Feb 2023 19:05:57 +0100 Subject: [PATCH] HAL-1824: add confirmation check for deployment replacing --- .../deployment/AbstractDeploymentColumn.java | 19 +++++- .../client/deployment/DeploymentTasks.java | 63 +++++++++++++++++++ app/src/web/style/wizard.less | 6 ++ .../org/jboss/hal/ballroom/wizard/Wizard.java | 31 +++++++++ .../java/org/jboss/hal/resources/CSS.java | 1 + .../org/jboss/hal/resources/Messages.java | 2 + .../jboss/hal/resources/Messages.properties | 1 + 7 files changed, 121 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/jboss/hal/client/deployment/AbstractDeploymentColumn.java b/app/src/main/java/org/jboss/hal/client/deployment/AbstractDeploymentColumn.java index 3535292c86..bd621ef24e 100644 --- a/app/src/main/java/org/jboss/hal/client/deployment/AbstractDeploymentColumn.java +++ b/app/src/main/java/org/jboss/hal/client/deployment/AbstractDeploymentColumn.java @@ -473,17 +473,32 @@ protected void uploadDeployment() { .addStep(UPLOAD, new UploadDeploymentStep(resources)) .addStep(NAMES, new NamesStep(environment, metadata, resources)) - .onBack((context, currentState) -> currentState == NAMES ? UPLOAD : null) + .onBack((context, currentState) -> currentState == NAMES ? (context.name != null ? NAMES : UPLOAD) : null) .onNext((context, currentState) -> currentState == UPLOAD ? NAMES : null) .stayOpenAfterFinish() .onFinish((wzd, wzdContext) -> { String name = wzdContext.name; wzd.showProgress(columnProps.deploymentProgressTitle, columnProps.getDeploymentProgressText.apply(name)); + + Task confirmReplacement = context -> new Promise<>((resolve, reject) -> { + int result = context.peek(); + + if (result == 404) { + resolve.onInvoke(context); + return; + } + + wzd.showWarning(columnProps.replaceDeploymentTitle, + resources.messages().deploymentReplaceConfirmation(name), resources.constants().replace(), + __ -> resolve.onInvoke(context), false); + }); + List> tasks = new ArrayList<>(); tasks.add(new DeploymentTasks.CheckDeployment(dispatcher, name)); + tasks.add(confirmReplacement); tasks.add(new DeploymentTasks.UploadOrReplace(environment, dispatcher, name, wzdContext.runtimeName, - wzdContext.file, wzdContext.enabled)); + wzdContext.file, wzdContext.enabled)); if (columnProps.columnType == ColumnType.SERVER_GROUP) { tasks.add(getServerGroupDeploymentTask(name, wzdContext.runtimeName)); } diff --git a/app/src/main/java/org/jboss/hal/client/deployment/DeploymentTasks.java b/app/src/main/java/org/jboss/hal/client/deployment/DeploymentTasks.java index 85f2bd99d2..eb808b3640 100644 --- a/app/src/main/java/org/jboss/hal/client/deployment/DeploymentTasks.java +++ b/app/src/main/java/org/jboss/hal/client/deployment/DeploymentTasks.java @@ -24,6 +24,7 @@ import javax.inject.Provider; +import org.jboss.hal.ballroom.dialog.Dialog; import org.jboss.hal.config.Environment; import org.jboss.hal.core.deployment.Content; import org.jboss.hal.core.deployment.Deployment; @@ -47,6 +48,7 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.Lists; +import com.google.gwt.safehtml.shared.SafeHtml; import com.google.web.bindery.event.shared.EventBus; import elemental2.dom.File; @@ -57,6 +59,7 @@ import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; +import static org.jboss.elemento.Elements.p; import static org.jboss.hal.dmr.ModelDescriptionConstants.ADD; import static org.jboss.hal.dmr.ModelDescriptionConstants.ADDRESS; import static org.jboss.hal.dmr.ModelDescriptionConstants.CHILD_TYPE; @@ -100,6 +103,8 @@ static void upload(FinderColumn column, Environment environment, Dispatch String filename = files.item(i).name; builder.append(filename).append(" "); tasks.add(new CheckDeployment(dispatcher, filename)); + tasks.add(new ConfirmReplacement(resources.constants().replaceDeployment(), resources.constants().replace(), + resources.messages().deploymentReplaceConfirmation(filename))); tasks.add(new UploadOrReplace(environment, dispatcher, filename, filename, files.item(i), !hasServerGroup)); if (hasServerGroup) { tasks.add(new AddServerGroupDeployment(environment, dispatcher, filename, filename, serverGroup)); @@ -362,9 +367,56 @@ public Promise apply(final FlowContext context) { } } + /** + * Check if {@code 404} is on the stack, if not asks the user for confirmation, if user confirms {@code 200} is kept on the + * stack otherwise {@code 403} is pushed on the stack. + */ + static final class ConfirmReplacement implements Task { + + private final String title; + private final String okButton; + private final SafeHtml message; + + ConfirmReplacement(String title, String okButton, SafeHtml message) { + this.title = title; + this.okButton = okButton; + this.message = message; + } + + @Override + public Promise apply(FlowContext context) { + Integer result = context.peek(); + + if (result == 404) { + return Promise.resolve(context); + } + + return new Promise<>((resolve, reject) -> { + + Dialog confirmDialog = new Dialog.Builder(title) + .primary(okButton, () -> { + resolve.onInvoke(context); + return true; + }) + .secondary(() -> { + context.pop(); + context.push(403); + resolve.onInvoke(context); + return true; + }) + .size(Dialog.Size.MEDIUM) + .add(p().innerHtml(message).element()) + .build(); + + confirmDialog.show(); + }); + } + } + /** * Creates a new deployment or replaces an existing deployment. The function looks for a status code in the context. If no * status context or {@code 404} is found, a new deployment is created, if {@code 200} is found the deployment is replaced. + * If {@code 403} is found the task does nothing. *

* The function puts an {@link UploadStatistics} under the key {@link DeploymentTasks#UPLOAD_STATISTICS} into the context. */ @@ -389,6 +441,7 @@ static final class UploadOrReplace implements Task { @Override public Promise apply(final FlowContext context) { + boolean skip = false; boolean replace; Operation.Builder builder; @@ -397,6 +450,16 @@ public Promise apply(final FlowContext context) { } else { Integer status = context.peek(); replace = status == 200; + skip = status == 403; + } + + if (skip) { + UploadStatistics statistics = context.get(UPLOAD_STATISTICS); + if (statistics == null) { + statistics = new UploadStatistics(environment); + context.set(UPLOAD_STATISTICS, statistics); + } + return Promise.resolve(context); } if (replace) { diff --git a/app/src/web/style/wizard.less b/app/src/web/style/wizard.less index 619bdd0d16..6e8dae2186 100644 --- a/app/src/web/style/wizard.less +++ b/app/src/web/style/wizard.less @@ -27,6 +27,12 @@ line-height: (@font-size-base * 5.6); } +.wizard-pf-warning-icon { + color: @color-pf-orange-300; + font-size: (@font-size-base * 5.6); + line-height: (@font-size-base * 5.6); +} + .wizard-hal-error-text { text-align: left; white-space: pre; diff --git a/ballroom/src/main/java/org/jboss/hal/ballroom/wizard/Wizard.java b/ballroom/src/main/java/org/jboss/hal/ballroom/wizard/Wizard.java index 5029696678..abfab9ec61 100644 --- a/ballroom/src/main/java/org/jboss/hal/ballroom/wizard/Wizard.java +++ b/ballroom/src/main/java/org/jboss/hal/ballroom/wizard/Wizard.java @@ -279,6 +279,37 @@ public void showSuccess(String title, SafeHtml text, String successButton, Succe finishCanClose = lastStep; } + public void showWarning(String title, SafeHtml text, String okButton, SuccessAction okAction, boolean lastStep) { + blankSlate.classList.remove(wizardPfProcess); + blankSlate.classList.add(wizardPfComplete); + Elements.removeChildrenFrom(blankSlate); + + blankSlate.appendChild(div().css(wizardPfWarningIcon) + .add(span().css(glyphicon("exclamation-sign"))).element()); + blankSlate.appendChild(h(3).css(blankSlatePfMainAction).textContent(title).element()); + blankSlate.appendChild(p().css(blankSlatePfSecondaryAction).innerHtml(text).element()); + + if (okButton != null && okAction != null) { + blankSlate.appendChild(button().css(btn, btnLg, btnPrimary) + .textContent(okButton) + .on(click, event -> okAction.execute(context)) + .element()); + } + + stepElements.values().forEach(element -> Elements.setVisible(element, false)); + Elements.setVisible(blankSlate, true); + + cancelButton.disabled = lastStep; + backButton.disabled = lastStep; + nextButton.disabled = false; + if (lastStep) { + nextText.textContent = CONSTANTS.close(); + Elements.setVisible(nextIcon, false); + } + nextButton.onclick = null; + finishCanClose = lastStep; + } + public void showError(String title, SafeHtml text) { showError(title, text, null, true); } diff --git a/resources/src/main/java/org/jboss/hal/resources/CSS.java b/resources/src/main/java/org/jboss/hal/resources/CSS.java index 01a3561e16..ee0e167f7f 100644 --- a/resources/src/main/java/org/jboss/hal/resources/CSS.java +++ b/resources/src/main/java/org/jboss/hal/resources/CSS.java @@ -477,6 +477,7 @@ public interface CSS { String wizardPfSteps = "wizard-pf-steps"; String wizardPfStepsIndicator = "wizard-pf-steps-indicator"; String wizardPfSuccessIcon = "wizard-pf-success-icon"; + String wizardPfWarningIcon = "wizard-pf-warning-icon"; String wrap = "wrap"; String DASH = "-"; diff --git a/resources/src/main/java/org/jboss/hal/resources/Messages.java b/resources/src/main/java/org/jboss/hal/resources/Messages.java index 0dd18420b2..2e8c350b3f 100644 --- a/resources/src/main/java/org/jboss/hal/resources/Messages.java +++ b/resources/src/main/java/org/jboss/hal/resources/Messages.java @@ -204,6 +204,8 @@ public interface Messages extends com.google.gwt.i18n.client.Messages { SafeHtml deploymentReadError(String deployment); + SafeHtml deploymentReplaceConfirmation(String name); + SafeHtml deploymentReplaced(@PluralCount int count); SafeHtml deploymentStopped(String name); diff --git a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties index f990e5a7f6..8211dc1273 100644 --- a/resources/src/main/resources/org/jboss/hal/resources/Messages.properties +++ b/resources/src/main/resources/org/jboss/hal/resources/Messages.properties @@ -140,6 +140,7 @@ deploymentOpFailed={0} deployments couldn't be processed. deploymentOpFailed[\=1]=The deployment couldn't be processed. deploymentPreview=This is an archived deployment. Use the button below to download a copy of this deployment. In order to add or modify files in the deployment you need to explode it. deploymentReadError=Unable to read information for deployment {0}. +deploymentReplaceConfirmation=Deployment {0} already exists. Do you want to replace it? deploymentReplaced={0} deployments have been replaced. deploymentReplaced[\=1]=The deployment has been replaced. deploymentStandaloneColumnFilterDescription=Filter by: name or deployment status