From fc319f90e3d9c27a46bb78c3bc161729ee6a52fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 16 Sep 2019 17:50:05 +0200 Subject: [PATCH] Append uninstall links --- .../sonia/scm/plugin/InstalledPlugin.java | 12 ++++- .../scm/api/v2/resources/PluginDtoMapper.java | 3 +- .../scm/plugin/DefaultPluginManager.java | 24 +++++++++- .../api/v2/resources/PluginDtoMapperTest.java | 11 +++++ .../scm/plugin/DefaultPluginManagerTest.java | 48 ++++++++++++++++++- 5 files changed, 91 insertions(+), 7 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java index 219af9763c..5d009a96e4 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java +++ b/scm-core/src/main/java/sonia/scm/plugin/InstalledPlugin.java @@ -135,7 +135,14 @@ public final class InstalledPlugin implements Plugin this.markedForUninstall = markedForUninstall; } - //~--- fields --------------------------------------------------------------- + public boolean isUninstallable() { + return uninstallable; + } + + public void setUninstallable(boolean uninstallable) { + this.uninstallable = uninstallable; + } +//~--- fields --------------------------------------------------------------- /** plugin class loader */ private final ClassLoader classLoader; @@ -151,5 +158,6 @@ public final class InstalledPlugin implements Plugin private final boolean core; - private boolean markedForUninstall; + private boolean markedForUninstall = false; + private boolean uninstallable = false; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index e3e1c026ac..160ec22354 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -74,10 +74,9 @@ public abstract class PluginDtoMapper { ) { links.single(link("update", resourceLinks.availablePlugin().install(information.getName()))); } - if (!plugin.isCore() + if (plugin.isUninstallable() && (!availablePlugin.isPresent() || !availablePlugin.get().isPending()) && PluginPermissions.manage().isPermitted() - // TODO check if plugin is no dependency of another plugin ) { links.single(link("uninstall", resourceLinks.installedPlugin().uninstall(information.getName()))); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index dd85764bc2..708c9004bf 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -82,15 +82,16 @@ public class DefaultPluginManager implements PluginManager { this.center = center; this.installer = installer; - this.computeRequiredPlugins(); + this.computeInstallationDependencies(); } @VisibleForTesting - synchronized void computeRequiredPlugins() { + synchronized void computeInstallationDependencies() { loader.getInstalledPlugins() .stream() .map(InstalledPlugin::getDescriptor) .forEach(dependencyTracker::addInstalled); + updateMayUninstallFlag(); } @Override @@ -166,6 +167,7 @@ public class DefaultPluginManager implements PluginManager { for (AvailablePlugin plugin : plugins) { try { PendingPluginInstallation pending = installer.install(plugin); + dependencyTracker.addInstalled(plugin.getDescriptor()); pendingInstallations.add(pending); } catch (PluginInstallException ex) { cancelPending(pendingInstallations); @@ -178,6 +180,7 @@ public class DefaultPluginManager implements PluginManager { restart("plugin installation"); } else { pendingQueue.addAll(pendingInstallations); + updateMayUninstallFlag(); } } } @@ -197,6 +200,23 @@ public class DefaultPluginManager implements PluginManager { } catch (IOException e) { throw new PluginException("could not mark plugin " + name + " in path " + installed.getDirectory() + " for uninstall", e); } + + if (restartAfterInstallation) { + restart("plugin installation"); + } else { + updateMayUninstallFlag(); + } + } + + private void updateMayUninstallFlag() { + loader.getInstalledPlugins() + .forEach(p -> p.setUninstallable(isUninstallable(p))); + } + + private boolean isUninstallable(InstalledPlugin p) { + return !p.isCore() + && !p.isMarkedForUninstall() + && dependencyTracker.mayUninstall(p.getDescriptor().getInformation().getName()); } @Override diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java index 5bd7c1199e..0da2331bd3 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java @@ -127,4 +127,15 @@ class PluginDtoMapperTest { PluginDto dto = mapper.mapAvailable(plugin); assertThat(dto.getDependencies()).containsOnly("one", "two"); } + + @Test + void shouldAppendUninstallLink() { + when(subject.isPermitted("plugin:manage")).thenReturn(true); + InstalledPlugin plugin = createInstalled(createPluginInformation()); + when(plugin.isUninstallable()).thenReturn(true); + + PluginDto dto = mapper.mapInstalled(plugin, emptyList()); + assertThat(dto.getLinks().getLinkBy("uninstall").get().getHref()) + .isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin/uninstall"); + } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java index 7e16e0fa94..00a8e26c5e 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java @@ -24,6 +24,7 @@ import java.nio.file.Path; import java.util.List; import java.util.Optional; +import static java.util.Arrays.asList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; @@ -328,7 +329,7 @@ class DefaultPluginManagerTest { when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin)); - manager.computeRequiredPlugins(); + manager.computeInstallationDependencies(); assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false)); } @@ -369,6 +370,51 @@ class DefaultPluginManagerTest { assertThat(temp.resolve("uninstall")).doesNotExist(); } + + @Test + void shouldMarkUninstallablePlugins() { + InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin"); + InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin"); + when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin)); + + manager.computeInstallationDependencies(); + + verify(reviewPlugin).setUninstallable(true); + verify(mailPlugin).setUninstallable(false); + } + + @Test + void shouldUpdateMayUninstallFlagAfterDependencyIsUninstalled() { + InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin"); + InstalledPlugin reviewPlugin = createInstalled("scm-review-plugin"); + when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + when(loader.getInstalledPlugins()).thenReturn(asList(mailPlugin, reviewPlugin)); + + manager.computeInstallationDependencies(); + + manager.uninstall("scm-review-plugin", false); + + verify(mailPlugin).setUninstallable(true); + } + + @Test + void shouldUpdateMayUninstallFlagAfterDependencyIsInstalled() { + InstalledPlugin mailPlugin = createInstalled("scm-mail-plugin"); + AvailablePlugin reviewPlugin = createAvailable("scm-review-plugin"); + when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); + + when(loader.getInstalledPlugins()).thenReturn(singletonList(mailPlugin)); + when(center.getAvailable()).thenReturn(singleton(reviewPlugin)); + + manager.computeInstallationDependencies(); + + manager.install("scm-review-plugin", false); + + verify(mailPlugin).setUninstallable(false); + } } @Nested