diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index 3ee4a4ea73..dab5c2652e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -4,7 +4,7 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.plugin.AvailablePlugin; -import sonia.scm.plugin.InstalledPluginDescriptor; +import sonia.scm.plugin.InstalledPlugin; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; import sonia.scm.web.VndMediaType; @@ -19,6 +19,7 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; @@ -51,10 +52,15 @@ public class AvailablePluginResource { @Produces(VndMediaType.PLUGIN_COLLECTION) public Response getAvailablePlugins() { PluginPermissions.read().check(); - List available = pluginManager.getAvailable(); + List installed = pluginManager.getInstalled(); + List available = pluginManager.getAvailable().stream().filter(a -> notInstalled(a, installed)).collect(Collectors.toList()); return Response.ok(collectionMapper.mapAvailable(available)).build(); } + private boolean notInstalled(AvailablePlugin a, List installed) { + return installed.stream().noneMatch(installedPlugin -> installedPlugin.getDescriptor().getInformation().getName().equals(a.getDescriptor().getInformation().getName())); + } + /** * Returns available plugin. * diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java index c108d4ee7a..436e0b630a 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -18,6 +18,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.plugin.AvailablePlugin; import sonia.scm.plugin.AvailablePluginDescriptor; +import sonia.scm.plugin.InstalledPlugin; +import sonia.scm.plugin.InstalledPluginDescriptor; import sonia.scm.plugin.PluginCondition; import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginManager; @@ -25,7 +27,6 @@ import sonia.scm.web.VndMediaType; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; - import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.Collections; @@ -34,6 +35,7 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -90,9 +92,10 @@ class AvailablePluginResourceTest { @Test void getAvailablePlugins() throws URISyntaxException, UnsupportedEncodingException { - AvailablePlugin plugin = createPlugin(); + AvailablePlugin plugin = createAvailablePlugin(); when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(plugin)); + when(pluginManager.getInstalled()).thenReturn(Collections.emptyList()); when(collectionMapper.mapAvailable(Collections.singletonList(plugin))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); @@ -105,13 +108,32 @@ class AvailablePluginResourceTest { assertThat(response.getContentAsString()).contains("\"marker\":\"x\""); } + @Test + void shouldNotReturnInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException { + AvailablePlugin availablePlugin = createAvailablePlugin(); + InstalledPlugin installedPlugin = createInstalledPlugin(); + + when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(availablePlugin)); + when(pluginManager.getInstalled()).thenReturn(Collections.singletonList(installedPlugin)); + lenient().when(collectionMapper.mapAvailable(Collections.singletonList(availablePlugin))).thenReturn(new MockedResultDto()); + + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); + request.accept(VndMediaType.PLUGIN_COLLECTION); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + assertThat(response.getContentAsString()).doesNotContain("\"marker\":\"x\""); + } + @Test void getAvailablePlugin() throws UnsupportedEncodingException, URISyntaxException { PluginInformation pluginInformation = new PluginInformation(); pluginInformation.setName("pluginName"); pluginInformation.setVersion("2.0.0"); - AvailablePlugin plugin = createPlugin(pluginInformation); + AvailablePlugin plugin = createAvailablePlugin(pluginInformation); when(pluginManager.getAvailable("pluginName")).thenReturn(Optional.of(plugin)); @@ -152,17 +174,31 @@ class AvailablePluginResourceTest { } } - private AvailablePlugin createPlugin() { - return createPlugin(new PluginInformation()); + private AvailablePlugin createAvailablePlugin() { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setName("scm-some-plugin"); + return createAvailablePlugin(pluginInformation); } - private AvailablePlugin createPlugin(PluginInformation pluginInformation) { + private AvailablePlugin createAvailablePlugin(PluginInformation pluginInformation) { AvailablePluginDescriptor descriptor = new AvailablePluginDescriptor( pluginInformation, new PluginCondition(), Collections.emptySet(), "https://download.hitchhiker.com", null ); return new AvailablePlugin(descriptor); } + private InstalledPlugin createInstalledPlugin() { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setName("scm-some-plugin"); + return createInstalledPlugin(pluginInformation); + } + + private InstalledPlugin createInstalledPlugin(PluginInformation pluginInformation) { + InstalledPluginDescriptor descriptor = mock(InstalledPluginDescriptor.class); + lenient().when(descriptor.getInformation()).thenReturn(pluginInformation); + return new InstalledPlugin(descriptor, null, null, null, false); + } + @Nested class WithoutAuthorization {