Append uninstall links

This commit is contained in:
René Pfeuffer
2019-09-16 17:50:05 +02:00
parent 0243edf585
commit fc319f90e3
5 changed files with 91 additions and 7 deletions

View File

@@ -135,7 +135,14 @@ public final class InstalledPlugin implements Plugin
this.markedForUninstall = markedForUninstall; this.markedForUninstall = markedForUninstall;
} }
//~--- fields --------------------------------------------------------------- public boolean isUninstallable() {
return uninstallable;
}
public void setUninstallable(boolean uninstallable) {
this.uninstallable = uninstallable;
}
//~--- fields ---------------------------------------------------------------
/** plugin class loader */ /** plugin class loader */
private final ClassLoader classLoader; private final ClassLoader classLoader;
@@ -151,5 +158,6 @@ public final class InstalledPlugin implements Plugin
private final boolean core; private final boolean core;
private boolean markedForUninstall; private boolean markedForUninstall = false;
private boolean uninstallable = false;
} }

View File

@@ -74,10 +74,9 @@ public abstract class PluginDtoMapper {
) { ) {
links.single(link("update", resourceLinks.availablePlugin().install(information.getName()))); links.single(link("update", resourceLinks.availablePlugin().install(information.getName())));
} }
if (!plugin.isCore() if (plugin.isUninstallable()
&& (!availablePlugin.isPresent() || !availablePlugin.get().isPending()) && (!availablePlugin.isPresent() || !availablePlugin.get().isPending())
&& PluginPermissions.manage().isPermitted() && PluginPermissions.manage().isPermitted()
// TODO check if plugin is no dependency of another plugin
) { ) {
links.single(link("uninstall", resourceLinks.installedPlugin().uninstall(information.getName()))); links.single(link("uninstall", resourceLinks.installedPlugin().uninstall(information.getName())));
} }

View File

@@ -82,15 +82,16 @@ public class DefaultPluginManager implements PluginManager {
this.center = center; this.center = center;
this.installer = installer; this.installer = installer;
this.computeRequiredPlugins(); this.computeInstallationDependencies();
} }
@VisibleForTesting @VisibleForTesting
synchronized void computeRequiredPlugins() { synchronized void computeInstallationDependencies() {
loader.getInstalledPlugins() loader.getInstalledPlugins()
.stream() .stream()
.map(InstalledPlugin::getDescriptor) .map(InstalledPlugin::getDescriptor)
.forEach(dependencyTracker::addInstalled); .forEach(dependencyTracker::addInstalled);
updateMayUninstallFlag();
} }
@Override @Override
@@ -166,6 +167,7 @@ public class DefaultPluginManager implements PluginManager {
for (AvailablePlugin plugin : plugins) { for (AvailablePlugin plugin : plugins) {
try { try {
PendingPluginInstallation pending = installer.install(plugin); PendingPluginInstallation pending = installer.install(plugin);
dependencyTracker.addInstalled(plugin.getDescriptor());
pendingInstallations.add(pending); pendingInstallations.add(pending);
} catch (PluginInstallException ex) { } catch (PluginInstallException ex) {
cancelPending(pendingInstallations); cancelPending(pendingInstallations);
@@ -178,6 +180,7 @@ public class DefaultPluginManager implements PluginManager {
restart("plugin installation"); restart("plugin installation");
} else { } else {
pendingQueue.addAll(pendingInstallations); pendingQueue.addAll(pendingInstallations);
updateMayUninstallFlag();
} }
} }
} }
@@ -197,6 +200,23 @@ public class DefaultPluginManager implements PluginManager {
} catch (IOException e) { } catch (IOException e) {
throw new PluginException("could not mark plugin " + name + " in path " + installed.getDirectory() + " for uninstall", 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 @Override

View File

@@ -127,4 +127,15 @@ class PluginDtoMapperTest {
PluginDto dto = mapper.mapAvailable(plugin); PluginDto dto = mapper.mapAvailable(plugin);
assertThat(dto.getDependencies()).containsOnly("one", "two"); 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");
}
} }

View File

@@ -24,6 +24,7 @@ import java.nio.file.Path;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@@ -328,7 +329,7 @@ class DefaultPluginManagerTest {
when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin")); when(reviewPlugin.getDescriptor().getDependencies()).thenReturn(singleton("scm-mail-plugin"));
when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin)); when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(mailPlugin, reviewPlugin));
manager.computeRequiredPlugins(); manager.computeInstallationDependencies();
assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false)); assertThrows(ScmConstraintViolationException.class, () -> manager.uninstall("scm-mail-plugin", false));
} }
@@ -369,6 +370,51 @@ class DefaultPluginManagerTest {
assertThat(temp.resolve("uninstall")).doesNotExist(); 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 @Nested