Implement fix all button

This commit is contained in:
Armin Schrenk
2022-11-25 17:17:55 +01:00
parent 74e3c28441
commit 4dc1d59305
5 changed files with 95 additions and 42 deletions
@@ -8,8 +8,12 @@ import org.cryptomator.ui.common.FxController;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.BooleanExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
@@ -35,30 +39,42 @@ public class CheckDetailController implements FxController {
private final Binding<Number> countOfCritSeverity;
private final Binding<Boolean> warnOrCritsExist;
private final ResultListCellFactory resultListCellFactory;
private final ResultFixApplier resultFixApplier;
private final BooleanProperty fixAllInfoResultsExecuted;
private final BooleanBinding fixAllInfoResultsPossible;
public ListView<Result> resultsListView;
private Subscription resultSubscription;
@Inject
public CheckDetailController(ObjectProperty<Check> selectedTask, ResultListCellFactory resultListCellFactory) {
public CheckDetailController(ObjectProperty<Check> selectedTask, ResultListCellFactory resultListCellFactory, ResultFixApplier resultFixApplier) {
this.resultListCellFactory = resultListCellFactory;
this.resultFixApplier = resultFixApplier;
this.results = EasyBind.wrapList(FXCollections.observableArrayList());
this.check = selectedTask;
this.checkState = selectedTask.flatMap(Check::stateProperty);
this.checkName = selectedTask.map(Check::getName).orElse("");
this.checkRunning = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.RUNNING::equals).orElse(false));
this.checkScheduled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SCHEDULED::equals).orElse(false));
this.checkSkipped =BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false));
this.checkSkipped = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SKIPPED::equals).orElse(false));
this.checkSucceeded = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.SUCCEEDED::equals).orElse(false));
this.checkFailed = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.ERROR::equals).orElse(false));
this.checkCancelled = BooleanExpression.booleanExpression(checkState.map(Check.CheckState.CANCELLED::equals).orElse(false));
this.checkFinished = checkSucceeded.or(checkFailed).or(checkCancelled);
this.countOfWarnSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.WARN));
this.countOfCritSeverity = results.reduce(countSeverity(DiagnosticResult.Severity.CRITICAL));
this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0) );
this.warnOrCritsExist = EasyBind.combine(checkSucceeded, countOfWarnSeverity, countOfCritSeverity, (suceeded, warns, crits) -> suceeded && (warns.longValue() > 0 || crits.longValue() > 0));
this.fixAllInfoResultsExecuted = new SimpleBooleanProperty(false);
this.fixAllInfoResultsPossible = Bindings.createBooleanBinding(() -> results.stream().anyMatch(this::isFixableInfoResult), results) //
.and(fixAllInfoResultsExecuted.not());
selectedTask.addListener(this::selectedTaskChanged);
}
private boolean isFixableInfoResult(Result r) {
return r.diagnosis().getSeverity() == DiagnosticResult.Severity.INFO && r.getState() == Result.FixState.FIXABLE;
}
private void selectedTaskChanged(ObservableValue<? extends Check> observable, Check oldValue, Check newValue) {
if (resultSubscription != null) {
resultSubscription.unsubscribe();
@@ -78,6 +94,13 @@ public class CheckDetailController implements FxController {
resultsListView.setCellFactory(resultListCellFactory);
}
@FXML
public void fixAllInfoResults() {
fixAllInfoResultsExecuted.setValue(true);
results.stream().filter(this::isFixableInfoResult).forEach(resultFixApplier::fix);
}
/* Getter/Setter */
public String getCheckName() {
@@ -175,4 +198,13 @@ public class CheckDetailController implements FxController {
public Check getCheck() {
return check.get();
}
public ObservableValue<Boolean> fixAllInfoResultsPossibleProperty() {
return fixAllInfoResultsPossible;
}
public boolean getFixAllInfoResultsPossible() {
return fixAllInfoResultsPossible.getValue();
}
}
@@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicReference;
@HealthCheckScoped
class ResultFixApplier {
private static final Logger LOG = LoggerFactory.getLogger(ResultFixApplier.class);
private final Path vaultPath;
private final SecureRandom csprng;
private final Masterkey masterkey;
@@ -40,19 +42,26 @@ class ResultFixApplier {
public CompletionStage<Void> fix(Result result) {
Preconditions.checkArgument(result.getState() == Result.FixState.FIXABLE);
result.setState(Result.FixState.FIXING);
return CompletableFuture.runAsync(() -> fix(result.diagnosis()), sequentialExecutor)
return CompletableFuture.runAsync(() -> result.setState(Result.FixState.FIXING), Platform::runLater) //
.thenRunAsync(() -> fix(result.diagnosis()), sequentialExecutor) //
.whenCompleteAsync((unused, throwable) -> {
var fixed = throwable == null ? Result.FixState.FIXED : Result.FixState.FIX_FAILED;
result.setState(fixed);
final Result.FixState s;
if (throwable == null) {
LOG.debug("Fix for {} applied successful.", result.diagnosis().getClass().getName());
s = Result.FixState.FIXED;
} else {
LOG.error("Failed to apply fix for {}", result.diagnosis().getClass().getName(), throwable);
s = Result.FixState.FIX_FAILED;
}
result.setState(s);
}, Platform::runLater);
}
public void fix(DiagnosticResult diagnosis) {
private void fix(DiagnosticResult diagnosis) {
try (var masterkeyClone = masterkey.copy(); //
var cryptor = CryptorProvider.forScheme(vaultConfig.getCipherCombo()).provide(masterkeyClone, csprng)) {
diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor)
.orElseThrow(() -> new IllegalStateException("No fix for diagnosis "+diagnosis.getClass().getName() +" implemented."))
diagnosis.getFix(vaultPath, vaultConfig, masterkeyClone, cryptor) //
.orElseThrow(() -> new IllegalStateException("No fix for diagnosis " + diagnosis.getClass().getName() + " implemented.")) //
.apply();
} catch (Exception e) {
throw new FixFailedException(e);
@@ -60,6 +69,7 @@ class ResultFixApplier {
}
public static class FixFailedException extends CompletionException {
private FixFailedException(Throwable cause) {
super(cause);
}
@@ -12,7 +12,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
@@ -49,8 +48,8 @@ public class ResultListCellController implements FxController {
private final BooleanBinding fixFailed;
private final BooleanBinding fixRunningOrDone;
private final List<Subscription> subscriptions;
private final Tooltip fixSuccess;
private final Tooltip fixFail;
private final Tooltip fixSuccessTip;
private final Tooltip fixFailTip;
private AutoAnimator fixRunningRotator;
@@ -73,10 +72,10 @@ public class ResultListCellController implements FxController {
this.fixFailed = Bindings.createBooleanBinding(this::isFixFailed, fixState);
this.fixRunningOrDone = fixing.or(fixed).or(fixFailed);
this.subscriptions = new ArrayList<>();
this.fixSuccess = new Tooltip(resourceBundle.getString("health.fix.successTip"));
this.fixFail = new Tooltip(resourceBundle.getString("health.fix.failTip"));
fixSuccess.setShowDelay(Duration.millis(100));
fixFail.setShowDelay(Duration.millis(100));
this.fixSuccessTip = new Tooltip(resourceBundle.getString("health.fix.successTip"));
this.fixFailTip = new Tooltip(resourceBundle.getString("health.fix.failTip"));
fixSuccessTip.setShowDelay(Duration.millis(100));
fixFailTip.setShowDelay(Duration.millis(100));
}
@FXML
@@ -93,22 +92,20 @@ public class ResultListCellController implements FxController {
.onCondition(fixing) //
.afterStop(() -> fixView.setRotate(0)) //
.build();
fixState.addListener(((observable, oldValue, newValue) -> {
if (newValue == Result.FixState.FIXED) {
Tooltip.install(fixView, fixSuccessTip);
} else if (newValue == Result.FixState.FIX_FAILED) {
Tooltip.install(fixView, fixFailTip);
}
}));
}
@FXML
public void fix() {
Result r = result.get();
if (r != null) {
fixApplier.fix(r).whenCompleteAsync(this::fixFinished, Platform::runLater);
}
}
private void fixFinished(Void unused, Throwable exception) {
if (exception != null) {
LOG.error("Failed to apply fix", exception);
Tooltip.install(fixView, fixFail);
} else {
Tooltip.install(fixView, fixSuccess);
fixApplier.fix(r);
}
}
@@ -5,25 +5,38 @@
<?import javafx.scene.layout.VBox?>
<?import org.cryptomator.ui.health.CheckStateIconView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
<?import javafx.scene.layout.Region?>
<VBox xmlns:fx="http://javafx.com/fxml"
xmlns="http://javafx.com/javafx"
fx:controller="org.cryptomator.ui.health.CheckDetailController"
spacing="6">
<Label fx:id="detailHeader" styleClass="label-extra-large" text="${controller.checkName}" contentDisplay="LEFT">
<graphic>
<HBox alignment="CENTER" minWidth="25" maxWidth="25">
<CheckStateIconView fx:id="checkStateIconView" check="${controller.check}" glyphSize="20"/>
</HBox>
</graphic>
</Label>
spacing="12">
<HBox alignment="CENTER" >
<VBox spacing="12">
<Label fx:id="detailHeader" styleClass="label-extra-large" text="${controller.checkName}" contentDisplay="RIGHT">
<graphic>
<HBox alignment="CENTER" minWidth="25" maxWidth="25">
<CheckStateIconView check="${controller.check}" glyphSize="20"/>
</HBox>
</graphic>
</Label>
<Label text="%health.check.detail.checkRunning" visible="${controller.checkRunning}" managed="${controller.checkRunning}"/>
<Label text="%health.check.detail.checkScheduled" visible="${controller.checkScheduled}" managed="${controller.checkScheduled}"/>
<Label text="%health.check.detail.checkSkipped" visible="${controller.checkSkipped}" managed="${controller.checkSkipped}"/>
<Label text="%health.check.detail.checkCancelled" visible="${controller.checkCancelled}" managed="${controller.checkCancelled}"/>
<Label text="%health.check.detail.checkFailed" visible="${controller.checkFailed}" managed="${controller.checkFailed}"/>
<Label text="%health.check.detail.checkFinished" visible="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}"/>
<Label text="%health.check.detail.checkFinishedAndFound" visible="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}"/>
</VBox>
<Region HBox.hgrow="ALWAYS"/>
<Button text="%health.check.detail.fixAllSpecificBtn" contentDisplay="RIGHT" graphicTextGap="3" visible="${controller.checkFinished}" managed="${controller.checkFinished}" disable="${! controller.fixAllInfoResultsPossible}" onAction="#fixAllInfoResults">
<graphic>
<FontAwesome5IconView glyph="INFO_CIRCLE" glyphSize="12" styleClass="glyph-icon-muted"/>
</graphic>
</Button>
<Label text="%health.check.detail.checkRunning" visible="${controller.checkRunning}" managed="${controller.checkRunning}"/>
<Label text="%health.check.detail.checkScheduled" visible="${controller.checkScheduled}" managed="${controller.checkScheduled}"/>
<Label text="%health.check.detail.checkSkipped" visible="${controller.checkSkipped}" managed="${controller.checkSkipped}"/>
<Label text="%health.check.detail.checkCancelled" visible="${controller.checkCancelled}" managed="${controller.checkCancelled}"/>
<Label text="%health.check.detail.checkFailed" visible="${controller.checkFailed}" managed="${controller.checkFailed}"/>
<Label text="%health.check.detail.checkFinished" visible="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; !controller.warnOrCritsExist}"/>
<Label text="%health.check.detail.checkFinishedAndFound" visible="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}" managed="${controller.checkSucceeded &amp;&amp; controller.warnOrCritsExist}"/>
</HBox>
<ListView fx:id="resultsListView" VBox.vgrow="ALWAYS" visible="${!controller.checkSkipped}" fixedCellSize="25"/>
</VBox>
@@ -219,6 +219,7 @@ health.check.detail.checkFinished=The check finished successfully.
health.check.detail.checkFinishedAndFound=The check finished running. Please review the results.
health.check.detail.checkFailed=The check exited due to an error.
health.check.detail.checkCancelled=The check was cancelled.
health.check.detail.fixAllSpecificBtn=Fix all of type
health.check.exportBtn=Export Report
## Fix Application
health.fix.fixBtn=Fix