]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6376 add Java Ws /api/plugins/installed 216/head
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 15 Apr 2015 10:18:55 +0000 (12:18 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Wed, 15 Apr 2015 12:29:42 +0000 (14:29 +0200)
provides the same data as RoR WS /api/updatecenter/installed_plugins but only in JSON format

server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWs.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsTest.java [new file with mode: 0644]

index 70c8a7085878f7479d49c74c26ad9490c0e40574..7085d2a46a2a806cd23c6f2afe6f7250ba4f11b3 100644 (file)
@@ -147,6 +147,8 @@ import org.sonar.server.permission.ws.PermissionsWs;
 import org.sonar.server.platform.monitoring.*;
 import org.sonar.server.platform.ws.*;
 import org.sonar.server.plugins.*;
+import org.sonar.server.plugins.ws.InstalledPluginsWsAction;
+import org.sonar.server.plugins.ws.PluginsWs;
 import org.sonar.server.properties.ProjectSettingsFactory;
 import org.sonar.server.qualitygate.QgateProjectFinder;
 import org.sonar.server.qualitygate.QualityGates;
@@ -679,6 +681,10 @@ class ServerComponents {
       DatabaseMonitor.class
       ));
 
+    // Plugins WS
+    pico.addSingleton(InstalledPluginsWsAction.class);
+    pico.addSingleton(PluginsWs.class);
+
     // Compute engine
     pico.addSingleton(ReportQueue.class);
     pico.addSingleton(ComputationThreadLauncher.class);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java
new file mode 100644 (file)
index 0000000..dc5345f
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Ordering;
+import com.google.common.io.Resources;
+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.api.utils.text.JsonWriter;
+
+import java.util.Collection;
+import java.util.SortedSet;
+
+import static com.google.common.base.Objects.firstNonNull;
+import static com.google.common.collect.Iterables.filter;
+
+/**
+ * Implementation of the {@code installed} action for the Plugins WebService.
+ */
+public class InstalledPluginsWsAction implements PluginsWsAction {
+  private static final String DEFAULT_VERSION = "-";
+  private static final String PROPERTY_KEY = "key";
+  private static final String PROPERTY_NAME = "name";
+  private static final String PROPERTY_VERSION = "version";
+
+  private final PluginRepository pluginRepository;
+
+  private static final Ordering<PluginMetadata> NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural()
+    .onResultOf(PluginMetadataToName.INSTANCE)
+    .compound(Ordering.natural().onResultOf(PluginMetadataToKey.INSTANCE));
+
+  public InstalledPluginsWsAction(PluginRepository pluginRepository) {
+    this.pluginRepository = pluginRepository;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    controller.createAction("installed")
+      .setDescription("Get the list of all the plugins installed on the SonarQube instance, sorted by name")
+      .setSince("5.2")
+      .setHandler(this)
+      .setResponseExample(Resources.getResource(this.getClass(), "example-installed_plugins.json"));
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    Collection<PluginMetadata> pluginMetadatas = retrieveAndSortPluginMetadata();
+    writeMetadataList(response, pluginMetadatas);
+  }
+
+  private SortedSet<PluginMetadata> retrieveAndSortPluginMetadata() {
+    return ImmutableSortedSet.copyOf(
+      NAME_KEY_PLUGIN_METADATA_COMPARATOR,
+      filter(pluginRepository.getMetadata(), NotCorePluginsPredicate.INSTANCE)
+      );
+  }
+
+  private void writeMetadataList(Response response, Collection<PluginMetadata> pluginMetadatas) {
+    JsonWriter jsonWriter = response.newJsonWriter();
+    jsonWriter.beginArray();
+    for (PluginMetadata pluginMetadata : pluginMetadatas) {
+      writeMetadata(jsonWriter, pluginMetadata);
+    }
+    jsonWriter.endArray();
+    jsonWriter.close();
+  }
+
+  private void writeMetadata(JsonWriter jsonWriter, PluginMetadata pluginMetadata) {
+    jsonWriter.beginObject();
+    jsonWriter.prop(PROPERTY_KEY, pluginMetadata.getKey());
+    jsonWriter.prop(PROPERTY_NAME, pluginMetadata.getName());
+    jsonWriter.prop(PROPERTY_VERSION, firstNonNull(pluginMetadata.getVersion(), DEFAULT_VERSION));
+    jsonWriter.endObject();
+  }
+
+  private enum NotCorePluginsPredicate implements Predicate<PluginMetadata> {
+    INSTANCE;
+
+    @Override
+    public boolean apply(PluginMetadata input) {
+      return !input.isCore();
+    }
+  }
+
+  private enum PluginMetadataToName implements Function<PluginMetadata, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(PluginMetadata input) {
+      return input.getName();
+    }
+  }
+
+  private enum PluginMetadataToKey implements Function<PluginMetadata, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(PluginMetadata input) {
+      return input.getKey();
+    }
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWs.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWs.java
new file mode 100644 (file)
index 0000000..8826eba
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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 org.sonar.api.server.ws.WebService;
+
+/**
+ * WebService bound to URL {@code api/plugins}.
+ */
+public class PluginsWs implements WebService {
+  private final PluginsWsAction[] actions;
+
+  public PluginsWs(PluginsWsAction... actions) {
+    this.actions = actions;
+  }
+
+  @Override
+  public void define(Context context) {
+    NewController controller = context.createController("api/plugins");
+    controller.setDescription("Plugin management")
+      .setSince("5.2");
+
+    for (PluginsWsAction action : actions) {
+      action.define(controller);
+    }
+
+    controller.done();
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWsAction.java
new file mode 100644 (file)
index 0000000..3126d24
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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 org.sonar.server.ws.WsAction;
+
+/**
+ * Marker interface for the action of the plugins WS implemented by {@link PluginsWs}.
+ */
+public interface PluginsWsAction extends WsAction {
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json b/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json
new file mode 100644 (file)
index 0000000..ca65c90
--- /dev/null
@@ -0,0 +1,17 @@
+[
+  {
+    "key": "findbugs",
+    "name": "Findbugs",
+    "version": "2.1"
+  },
+  {
+    "key": "l10nfr",
+    "name": "French Pack",
+    "version": "1.10"
+  },
+  {
+    "key": "jira",
+    "name": "JIRA",
+    "version": "1.2"
+  }
+]
diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java
new file mode 100644 (file)
index 0000000..55f641c
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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 org.junit.Test;
+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.plugins.DefaultPluginMetadata;
+import org.sonar.server.ws.WsTester;
+
+import static com.google.common.collect.ImmutableList.of;
+import static java.lang.String.valueOf;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class InstalledPluginsWsActionTest {
+  private static final String DUMMY_CONTROLLER_KEY = "dummy";
+  private static final String JSON_EMPTY_ARRAY = "[]";
+
+  private PluginRepository pluginRepository = mock(PluginRepository.class);
+  private InstalledPluginsWsAction underTest = new InstalledPluginsWsAction(pluginRepository);
+
+  private WsTester wsTester = new WsTester();
+  private Request request = mock(Request.class);
+  private WsTester.TestResponse response = new WsTester.TestResponse();
+  private PluginMetadata corePlugin = corePlugin("core1", 10);
+
+  private static PluginMetadata corePlugin(String key, int version) {
+    return DefaultPluginMetadata.create(key).setName(key).setCore(true).setVersion(valueOf(version));
+  }
+
+  private static PluginMetadata plugin(String key, String name, int version) {
+    return DefaultPluginMetadata.create(key).setName(name).setCore(false).setVersion(valueOf(version));
+  }
+
+  private static PluginMetadata plugin(String key, String name) {
+    return DefaultPluginMetadata.create(key).setName(name).setCore(false).setVersion("1.0");
+  }
+
+  @Test
+  public void action_installed_is_defined() throws Exception {
+    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("installed");
+
+    WebService.Action action = controller.actions().iterator().next();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.responseExample()).isNotNull();
+  }
+
+  @Test
+  public void empty_array_is_returned_when_there_is_not_plugin_installed() throws Exception {
+    underTest.handle(request, response);
+
+    assertThat(response.outputAsString()).isEqualTo("[]");
+  }
+
+  @Test
+  public void core_plugin_are_not_returned() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(of(corePlugin));
+
+    underTest.handle(request, response);
+
+    assertThat(response.outputAsString()).isEqualTo(JSON_EMPTY_ARRAY);
+  }
+
+  @Test
+  public void verify_properties_displayed_in_json_per_plugin() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(of(plugin("plugKey", "plugName", 10)));
+
+    underTest.handle(request, response);
+
+    assertJson(response.outputAsString()).isSimilarTo(
+        "[" +
+            "{" +
+            "  \"key\": \"plugKey\"," +
+            "  \"name\": \"plugName\"," +
+            "  \"version\": \"10\"" +
+            "}" +
+            "]"
+    );
+  }
+
+  @Test
+  public void plugins_are_sorted_by_name_then_key_and_only_one_plugin_can_have_a_specific_name() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(
+        of(
+            plugin("A", "name2"),
+            plugin("B", "name1"),
+            plugin("C", "name0"),
+            plugin("D", "name0")
+        )
+    );
+
+    underTest.handle(request, response);
+
+    assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo(
+        "[" +
+            "{\"key\": \"C\"}" + "," +
+            "{\"key\": \"D\"}" + "," +
+            "{\"key\": \"B\"}" + "," +
+            "{\"key\": \"A\"}" +
+            "]"
+    );
+  }
+
+  @Test
+  public void only_one_plugin_can_have_a_specific_name_and_key() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(
+        of(
+            plugin("A", "name2"),
+            plugin("A", "name2")
+        )
+    );
+
+    underTest.handle(request, response);
+
+    assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo(
+        "[" +
+            "{\"key\": \"A\"}" +
+            "]"
+    );
+    assertThat(response.outputAsString()).containsOnlyOnce("name2");
+  }
+
+  @Test
+  public void dash_is_returned_when_version_is_null() throws Exception {
+    when(pluginRepository.getMetadata()).thenReturn(
+        of(
+            (PluginMetadata) DefaultPluginMetadata.create("key").setCore(false).setVersion(null)
+        )
+    );
+
+    underTest.handle(request, response);
+
+    assertJson(response.outputAsString())
+        .isSimilarTo(
+            "[" +
+                "{\"version\": \"-\"}" +
+            "]"
+        );
+
+  }
+}
\ No newline at end of file
diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsTest.java
new file mode 100644 (file)
index 0000000..9c220bb
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 org.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.server.ws.WsTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PluginsWsTest {
+  private WsTester tester = new WsTester(new PluginsWs(new DummyPluginsWsAction()));
+
+  @Test
+  public void defines_controller_and_binds_PluginsWsActions() throws Exception {
+    WebService.Controller controller = tester.controller("api/plugins");
+
+    assertThat(controller).isNotNull();
+    assertThat(controller.since()).isEqualTo("5.2");
+    assertThat(controller.description()).isNotEmpty();
+    assertThat(controller.actions()).extracting("key").containsOnly("dummy");
+  }
+
+  private static class DummyPluginsWsAction implements PluginsWsAction {
+    @Override
+    public void define(WebService.NewController context) {
+      context.createAction("dummy").setHandler(this);
+    }
+
+    @Override
+    public void handle(Request request, Response response) throws Exception {
+      // not relevant to test
+    }
+  }
+}