From 86a26e1a2721c1804556b4928dee3ea23f020339 Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Wed, 15 Apr 2015 12:18:55 +0200 Subject: [PATCH] SONAR-6376 add Java Ws /api/plugins/installed provides the same data as RoR WS /api/updatecenter/installed_plugins but only in JSON format --- .../server/platform/ServerComponents.java | 6 + .../plugins/ws/InstalledPluginsWsAction.java | 125 +++++++++++++ .../sonar/server/plugins/ws/PluginsWs.java | 46 +++++ .../server/plugins/ws/PluginsWsAction.java | 28 +++ .../plugins/ws/example-installed_plugins.json | 17 ++ .../ws/InstalledPluginsWsActionTest.java | 170 ++++++++++++++++++ .../server/plugins/ws/PluginsWsTest.java | 54 ++++++ 7 files changed, 446 insertions(+) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWs.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWsAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 70c8a708587..7085d2a46a2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -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 index 00000000000..dc5345f0191 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/InstalledPluginsWsAction.java @@ -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 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 pluginMetadatas = retrieveAndSortPluginMetadata(); + writeMetadataList(response, pluginMetadatas); + } + + private SortedSet retrieveAndSortPluginMetadata() { + return ImmutableSortedSet.copyOf( + NAME_KEY_PLUGIN_METADATA_COMPARATOR, + filter(pluginRepository.getMetadata(), NotCorePluginsPredicate.INSTANCE) + ); + } + + private void writeMetadataList(Response response, Collection 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 { + INSTANCE; + + @Override + public boolean apply(PluginMetadata input) { + return !input.isCore(); + } + } + + private enum PluginMetadataToName implements Function { + INSTANCE; + + @Override + public String apply(PluginMetadata input) { + return input.getName(); + } + } + + private enum PluginMetadataToKey implements Function { + 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 index 00000000000..8826ebaea1f --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWs.java @@ -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 index 00000000000..3126d2430b7 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginsWsAction.java @@ -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 index 00000000000..ca65c9072da --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json @@ -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 index 00000000000..55f641c443b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/InstalledPluginsWsActionTest.java @@ -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 index 00000000000..9c220bb1290 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginsWsTest.java @@ -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 + } + } +} -- 2.39.5