diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java
index 4dce1e9e92..282b9860b3 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java
@@ -53,6 +53,7 @@ public class PluginDto extends HalRepresentation {
private Boolean core;
private Boolean markedForUninstall;
private Set
dependencies;
+ private Set optionalDependencies;
public PluginDto(Links links) {
add(links);
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 1d71735278..0156a976ec 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
@@ -68,6 +68,7 @@ public abstract class PluginDtoMapper {
private void map(PluginDto dto, Plugin plugin) {
dto.setDependencies(plugin.getDescriptor().getDependencies());
+ dto.setOptionalDependencies(plugin.getDescriptor().getOptionalDependencies());
map(plugin.getDescriptor().getInformation(), dto);
if (dto.getCategory() == null) {
dto.setCategory("Miscellaneous");
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 ef1f4e6873..69fc876704 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java
@@ -253,21 +253,40 @@ public class DefaultPluginManager implements PluginManager {
private void collectPluginsToInstallOrUpdate(List plugins, String name) {
if (!isInstalledOrPending(name) || isUpdatable(name)) {
- AvailablePlugin plugin = getAvailable(name).orElseThrow(() -> NotFoundException.notFound(entity(AvailablePlugin.class, name)));
-
- Set dependencies = plugin.getDescriptor().getDependencies();
- if (dependencies != null) {
- for (String dependency : dependencies) {
- collectPluginsToInstallOrUpdate(plugins, dependency);
- }
- }
-
- plugins.add(plugin);
+ collectDependentPlugins(plugins, name);
} else {
LOG.info("plugin {} is already installed or installation is pending, skipping installation", name);
}
}
+ private void collectOptionalPluginToInstallOrUpdate(List plugins, String name) {
+ if (isInstalledOrPending(name) && isUpdatable(name)) {
+ collectDependentPlugins(plugins, name);
+ } else {
+ LOG.info("optional plugin {} is not installed or not updatable", name);
+ }
+ }
+
+ private void collectDependentPlugins(List plugins, String name) {
+ AvailablePlugin plugin = getAvailable(name).orElseThrow(() -> NotFoundException.notFound(entity(AvailablePlugin.class, name)));
+
+ Set dependencies = plugin.getDescriptor().getDependencies();
+ if (dependencies != null) {
+ for (String dependency : dependencies) {
+ collectPluginsToInstallOrUpdate(plugins, dependency);
+ }
+ }
+
+ Set optionalDependencies = plugin.getDescriptor().getOptionalDependencies();
+ if (dependencies != null) {
+ for (String optionalDependency : optionalDependencies) {
+ collectOptionalPluginToInstallOrUpdate(plugins, optionalDependency);
+ }
+ }
+
+ plugins.add(plugin);
+ }
+
private boolean isInstalledOrPending(String name) {
return getInstalled(name).isPresent() || getPending(name).isPresent();
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
index 223e02ad47..c034c98a56 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java
@@ -85,6 +85,9 @@ public final class PluginCenterDto implements Serializable {
@XmlElement(name = "dependencies")
private Set dependencies;
+ @XmlElement(name = "optionalDependencies")
+ private Set optionalDependencies;
+
@XmlElement(name = "_links")
private Map links;
}
diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
index 27d20d638a..5927592d0c 100644
--- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java
@@ -43,7 +43,7 @@ public abstract class PluginCenterDtoMapper {
for (PluginCenterDto.Plugin plugin : pluginCenterDto.getEmbedded().getPlugins()) {
String url = plugin.getLinks().get("download").getHref();
AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor(
- map(plugin), map(plugin.getConditions()), plugin.getDependencies(), url, plugin.getSha256sum()
+ map(plugin), map(plugin.getConditions()), plugin.getDependencies(), plugin.getOptionalDependencies(), url, plugin.getSha256sum()
);
plugins.add(new AvailablePlugin(descriptor));
}
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 7c3c3975c0..9a6b51db7a 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
@@ -164,6 +164,15 @@ class PluginDtoMapperTest {
assertThat(dto.getDependencies()).containsOnly("one", "two");
}
+ @Test
+ void shouldAppendOptionalDependencies() {
+ AvailablePlugin plugin = createAvailable(createPluginInformation());
+ when(plugin.getDescriptor().getOptionalDependencies()).thenReturn(ImmutableSet.of("one", "two"));
+
+ PluginDto dto = mapper.mapAvailable(plugin);
+ assertThat(dto.getOptionalDependencies()).containsOnly("one", "two");
+ }
+
@Test
void shouldAppendUninstallLink() {
when(subject.isPermitted("plugin:write")).thenReturn(true);
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 b1b1db9efe..b16d010c5d 100644
--- a/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/plugin/DefaultPluginManagerTest.java
@@ -260,6 +260,35 @@ class DefaultPluginManagerTest {
verify(installer).install(review);
}
+ @Test
+ void shouldUpdateAlreadyInstalledOptionalDependenciesWhenNewerVersionIsAvailable() {
+ AvailablePlugin review = createAvailable("scm-review-plugin");
+ when(review.getDescriptor().getOptionalDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
+ AvailablePlugin mail = createAvailable("scm-mail-plugin", "1.1.0");
+ when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
+
+ InstalledPlugin installedMail = createInstalled("scm-mail-plugin", "1.0.0");
+ when(loader.getInstalledPlugins()).thenReturn(ImmutableList.of(installedMail));
+
+ manager.install("scm-review-plugin", false);
+
+ verify(installer).install(mail);
+ verify(installer).install(review);
+ }
+
+ @Test
+ void shouldNotUpdateOptionalDependenciesWhenNewerVersionIsAvailableButItIsNotInstalled() {
+ AvailablePlugin review = createAvailable("scm-review-plugin");
+ when(review.getDescriptor().getOptionalDependencies()).thenReturn(ImmutableSet.of("scm-mail-plugin"));
+ AvailablePlugin mail = createAvailable("scm-mail-plugin", "1.1.0");
+ when(center.getAvailable()).thenReturn(ImmutableSet.of(review, mail));
+
+ manager.install("scm-review-plugin", false);
+
+ verify(installer, never()).install(mail);
+ verify(installer).install(review);
+ }
+
@Test
void shouldRollbackOnFailedInstallation() {
AvailablePlugin review = createAvailable("scm-review-plugin");
diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java
index 42ecf64da4..96ceccfb54 100644
--- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java
@@ -67,6 +67,7 @@ class PluginCenterDtoMapperTest {
"555000444",
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
ImmutableSet.of("scm-review-plugin"),
+ ImmutableSet.of(),
ImmutableMap.of("download", new Link("http://download.hitchhiker.com"))
);
@@ -101,6 +102,7 @@ class PluginCenterDtoMapperTest {
"12345678aa",
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
ImmutableSet.of("scm-review-plugin"),
+ ImmutableSet.of(),
ImmutableMap.of("download", new Link("http://download.hitchhiker.com/review"))
);
@@ -115,6 +117,7 @@ class PluginCenterDtoMapperTest {
"555000444",
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
ImmutableSet.of("scm-review-plugin"),
+ ImmutableSet.of(),
ImmutableMap.of("download", new Link("http://download.hitchhiker.com/hitchhiker"))
);