]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6380 add Java WS to uninstall a plugin 243/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 22 Apr 2015 09:49:40 +0000 (11:49 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 23 Apr 2015 14:03:04 +0000 (16:03 +0200)
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java [new file with mode: 0644]

index 3d1f3456f2f401c6894c250435c48cb091a47dcf..95a8268d529559416bf26da2f8a7d76031c8c4b2 100644 (file)
  */
 package org.sonar.server.platform;
 
-import com.google.common.collect.Lists;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+import javax.annotation.Nullable;
+
 import org.sonar.api.config.EmailSettings;
 import org.sonar.api.issue.action.Actions;
 import org.sonar.api.platform.ComponentContainer;
@@ -222,6 +228,7 @@ import org.sonar.server.plugins.ws.InstalledPluginsWsAction;
 import org.sonar.server.plugins.ws.PendingPluginsWsAction;
 import org.sonar.server.plugins.ws.PluginWSCommons;
 import org.sonar.server.plugins.ws.PluginsWs;
+import org.sonar.server.plugins.ws.UninstallPluginsWsAction;
 import org.sonar.server.plugins.ws.UpdatePluginsWsAction;
 import org.sonar.server.plugins.ws.UpdatesPluginsWsAction;
 import org.sonar.server.properties.ProjectSettingsFactory;
@@ -383,11 +390,7 @@ import org.sonar.server.view.index.ViewIndexer;
 import org.sonar.server.ws.ListingWs;
 import org.sonar.server.ws.WebServiceEngine;
 
-import javax.annotation.Nullable;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Properties;
+import com.google.common.collect.Lists;
 
 class ServerComponents {
 
@@ -895,6 +898,7 @@ class ServerComponents {
     pico.addSingleton(PendingPluginsWsAction.class);
     pico.addSingleton(InstallPluginsWsAction.class);
     pico.addSingleton(UpdatePluginsWsAction.class);
+    pico.addSingleton(UninstallPluginsWsAction.class);
     pico.addSingleton(PluginsWs.class);
 
     // Compute engine
diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UninstallPluginsWsAction.java
new file mode 100644 (file)
index 0000000..aa74b48
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins.ws;
+
+import com.google.common.base.Predicate;
+import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.platform.PluginRepository;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.server.plugins.ServerPluginJarsInstaller;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.collect.Iterables.find;
+import static java.lang.String.format;
+
+/**
+ * Implementation of the {@code uninstall} action for the Plugins WebService.
+ */
+public class UninstallPluginsWsAction implements PluginsWsAction {
+  private static final String PARAM_KEY = "key";
+
+  private final PluginRepository pluginRepository;
+  private final ServerPluginJarsInstaller pluginJarsInstaller;
+
+  public UninstallPluginsWsAction(PluginRepository pluginRepository, ServerPluginJarsInstaller pluginJarsInstaller) {
+    this.pluginRepository = pluginRepository;
+    this.pluginJarsInstaller = pluginJarsInstaller;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("uninstall")
+      .setPost(true)
+      .setDescription("Uninstalls the plugin specified by its key." +
+        "<br/>" +
+        "Requires user to be authenticated with Administer System permissions")
+      .setHandler(this);
+
+    action.createParam(PARAM_KEY)
+      .setDescription("The key identifying the plugin to uninstall")
+      .setRequired(true);
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
+    String key = request.mandatoryParam(PARAM_KEY);
+    ensurePluginIsInstalled(key);
+    pluginJarsInstaller.uninstall(key);
+    response.noContent();
+  }
+
+  private void ensurePluginIsInstalled(String key) {
+    if (find(pluginRepository.getMetadata(), new PluginKeyPredicate(key), null) == null) {
+      throw new IllegalArgumentException(
+        format("No plugin with key '%s' or plugin '%s' is not installed", key, key));
+    }
+  }
+
+  private static class PluginKeyPredicate implements Predicate<PluginMetadata> {
+    private final String key;
+
+    public PluginKeyPredicate(String key) {
+      this.key = key;
+    }
+
+    @Override
+    public boolean apply(@Nullable PluginMetadata input) {
+      return input != null && key.equals(input.getKey());
+    }
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UninstallPluginsWsActionTest.java
new file mode 100644 (file)
index 0000000..ecc71e4
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.plugins.ws;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.platform.PluginMetadata;
+import org.sonar.api.platform.PluginRepository;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.core.permission.GlobalPermissions;
+import org.sonar.core.plugins.DefaultPluginMetadata;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.plugins.ServerPluginJarsInstaller;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class UninstallPluginsWsActionTest {
+  private static final String DUMMY_CONTROLLER_KEY = "dummy";
+  private static final String CONTROLLER_KEY = "api/plugins";
+  private static final String ACTION_KEY = "uninstall";
+  private static final String KEY_PARAM = "key";
+  private static final String PLUGIN_KEY = "pluginKey";
+
+  private PluginRepository pluginRepository = mock(PluginRepository.class);
+  private ServerPluginJarsInstaller pluginJarsInstaller = mock(ServerPluginJarsInstaller.class);
+  private UninstallPluginsWsAction underTest = new UninstallPluginsWsAction(pluginRepository, pluginJarsInstaller);
+
+  private WsTester wsTester = new WsTester(new PluginsWs(underTest));
+  private Request invalidRequest = wsTester.newGetRequest(CONTROLLER_KEY, ACTION_KEY);
+  private Request validRequest = wsTester.newGetRequest(CONTROLLER_KEY, ACTION_KEY).setParam(KEY_PARAM, PLUGIN_KEY);
+  private WsTester.TestResponse response = new WsTester.TestResponse();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Before
+  public void setUp() throws Exception {
+    MockUserSession.set().setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN);
+  }
+
+  @Test
+  public void user_must_have_system_admin_permission() throws Exception {
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    // no permission on user
+    MockUserSession.set().setGlobalPermissions();
+
+    underTest.handle(validRequest, response);
+  }
+
+  @Test
+  public void action_uninstall_is_defined() throws Exception {
+    WsTester wsTester = new WsTester();
+    WebService.NewController newController = wsTester.context().createController(DUMMY_CONTROLLER_KEY);
+
+    underTest.define(newController);
+    newController.done();
+
+    WebService.Controller controller = wsTester.controller(DUMMY_CONTROLLER_KEY);
+    assertThat(controller.actions()).extracting("key").containsExactly(ACTION_KEY);
+
+    WebService.Action action = controller.actions().iterator().next();
+    assertThat(action.isPost()).isTrue();
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.responseExample()).isNull();
+
+    assertThat(action.params()).hasSize(1);
+    WebService.Param keyParam = action.param(KEY_PARAM);
+    assertThat(keyParam).isNotNull();
+    assertThat(keyParam.isRequired()).isTrue();
+    assertThat(keyParam.description()).isNotNull();
+  }
+
+  @Test
+  public void IAE_is_raised_when_key_param_is_not_provided() throws Exception {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Parameter 'key' is missing");
+
+    underTest.handle(invalidRequest, response);
+  }
+
+  @Test
+  public void IAE_is_raised_when_plugin_is_not_installed() throws Exception {
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("No plugin with key 'pluginKey'");
+
+    underTest.handle(validRequest, response);
+  }
+
+  @Test
+  public void if_plugin_is_installed_uninstallation_is_triggered() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(ImmutableList.<PluginMetadata>of(
+      DefaultPluginMetadata.create(PLUGIN_KEY)
+      ));
+
+    underTest.handle(validRequest, response);
+
+    verify(pluginJarsInstaller).uninstall(PLUGIN_KEY);
+    assertThat(response.outputAsString()).isEmpty();
+  }
+
+}