From 20a35162923ae2fc61a75cbb20c6798bbe86a09d Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Fri, 17 Apr 2015 15:54:26 +0200 Subject: [PATCH] SONAR-6379 add Java WS to list plugins updates factor code from AvailablePluginsWsAction and UpdatablePluginsWsAction into PluginWSCommons because there are extremely similar --- .../server/platform/ServerComponents.java | 2 + .../plugins/ws/AvailablePluginsWsAction.java | 121 +------------ .../server/plugins/ws/PluginWSCommons.java | 104 +++++++++++ .../plugins/ws/UpdatesPluginsWsAction.java | 124 +++++++++++++ .../plugins/ws/example-updates_plugins.json | 53 ++++++ ...tUpdateCenterBasedPluginsWsActionTest.java | 84 +++++++++ .../ws/AvailablePluginsWsActionTest.java | 81 ++------- .../plugins/ws/PluginWSCommonsTest.java | 164 +++++++++++++++++- .../ws/UpdatesPluginsWsActionTest.java | 138 +++++++++++++++ .../properties_per_plugin.json | 36 ++++ 10 files changed, 713 insertions(+), 194 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/plugins/ws/UpdatablePluginsWsActionTest/properties_per_plugin.json 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 b338e2e6502..08be0f8b8b5 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 @@ -222,6 +222,7 @@ 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.UpdatePluginsWsAction; +import org.sonar.server.plugins.ws.UpdatesPluginsWsAction; import org.sonar.server.properties.ProjectSettingsFactory; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; @@ -889,6 +890,7 @@ class ServerComponents { pico.addSingleton(PluginWSCommons.class); pico.addSingleton(InstalledPluginsWsAction.class); pico.addSingleton(AvailablePluginsWsAction.class); + pico.addSingleton(UpdatesPluginsWsAction.class); pico.addSingleton(PendingPluginsWsAction.class); pico.addSingleton(UpdatePluginsWsAction.class); pico.addSingleton(PluginsWs.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/AvailablePluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/AvailablePluginsWsAction.java index 33b8c0c43eb..e16955a2b07 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/AvailablePluginsWsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/AvailablePluginsWsAction.java @@ -19,52 +19,27 @@ */ package org.sonar.server.plugins.ws; -import com.google.common.base.Function; import com.google.common.io.Resources; 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 org.sonar.server.plugins.UpdateCenterMatrixFactory; -import org.sonar.updatecenter.common.Artifact; -import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.PluginUpdate; -import org.sonar.updatecenter.common.Release; -import javax.annotation.Nullable; import java.util.List; -import static com.google.common.collect.Iterables.filter; -import static com.google.common.collect.Iterables.transform; -import static org.sonar.server.plugins.ws.PluginWSCommons.OBJECT_ARTIFACT; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_DATE; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_DESCRIPTION; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_KEY; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_NAME; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_ORGANIZATION_NAME; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_ORGANIZATION_URL; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_STATUS; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_TERMS_AND_CONDITIONS_URL; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_URL; -import static org.sonar.server.plugins.ws.PluginWSCommons.PROPERTY_VERSION; - -/** - * Implementation of the {@code available} action for the Plugins WebService. - */ public class AvailablePluginsWsAction implements PluginsWsAction { private static final boolean DO_NOT_FORCE_REFRESH = false; - private static final String OBJECT_UPDATE = "update"; - private static final String OBJECT_RELEASE = "release"; - private static final String ARRAY_REQUIRES = "requires"; private static final String ARRAY_PLUGINS = "plugins"; - private static final String PROPERTY_CATEGORY = "category"; - private static final String PROPERTY_LICENSE = "license"; private final UpdateCenterMatrixFactory updateCenterFactory; + private final PluginWSCommons pluginWSCommons; - public AvailablePluginsWsAction(UpdateCenterMatrixFactory updateCenterFactory) { + public AvailablePluginsWsAction(UpdateCenterMatrixFactory updateCenterFactory, PluginWSCommons pluginWSCommons) { this.updateCenterFactory = updateCenterFactory; + this.pluginWSCommons = pluginWSCommons; } @Override @@ -92,7 +67,7 @@ public class AvailablePluginsWsAction implements PluginsWsAction { jsonWriter.name(ARRAY_PLUGINS); jsonWriter.beginArray(); for (PluginUpdate pluginUpdate : retrieveAvailablePlugins()) { - writePluginUpdate(jsonWriter, pluginUpdate); + pluginWSCommons.writePluginUpdate(jsonWriter, pluginUpdate); } jsonWriter.endArray(); jsonWriter.endObject(); @@ -101,92 +76,4 @@ public class AvailablePluginsWsAction implements PluginsWsAction { private List retrieveAvailablePlugins() { return updateCenterFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH).findAvailablePlugins(); } - - private void writePluginUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { - jsonWriter.beginObject(); - Plugin plugin = pluginUpdate.getPlugin(); - - writeMetadata(jsonWriter, plugin); - - writeRelease(jsonWriter, pluginUpdate.getRelease()); - - writeUpdate(jsonWriter, pluginUpdate); - - jsonWriter.endObject(); - } - - private void writeMetadata(JsonWriter jsonWriter, Plugin plugin) { - jsonWriter.prop(PROPERTY_KEY, plugin.getKey()); - jsonWriter.prop(PROPERTY_NAME, plugin.getName()); - jsonWriter.prop(PROPERTY_CATEGORY, plugin.getCategory()); - jsonWriter.prop(PROPERTY_DESCRIPTION, plugin.getDescription()); - jsonWriter.prop(PROPERTY_LICENSE, plugin.getLicense()); - jsonWriter.prop(PROPERTY_TERMS_AND_CONDITIONS_URL, plugin.getTermsConditionsUrl()); - jsonWriter.prop(PROPERTY_ORGANIZATION_NAME, plugin.getOrganization()); - jsonWriter.prop(PROPERTY_ORGANIZATION_URL, plugin.getOrganizationUrl()); - } - - private void writeRelease(JsonWriter jsonWriter, Release release) { - jsonWriter.name(OBJECT_RELEASE); - jsonWriter.beginObject(); - jsonWriter.prop(PROPERTY_VERSION, release.getVersion().toString()); - jsonWriter.propDate(PROPERTY_DATE, release.getDate()); - writeArchive(jsonWriter, release); - jsonWriter.endObject(); - } - - private void writeArchive(JsonWriter jsonWriter, Release release) { - jsonWriter.name(OBJECT_ARTIFACT); - jsonWriter.beginObject(); - jsonWriter.prop(PROPERTY_NAME, release.getFilename()); - jsonWriter.prop(PROPERTY_URL, release.getDownloadUrl()); - jsonWriter.endObject(); - } - - private void writeUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { - jsonWriter.name(OBJECT_UPDATE); - jsonWriter.beginObject(); - jsonWriter.prop(PROPERTY_STATUS, toJSon(pluginUpdate.getStatus())); - - jsonWriter.name(ARRAY_REQUIRES); - jsonWriter.beginArray(); - Release release = pluginUpdate.getRelease(); - for (Plugin child : filter(transform(release.getOutgoingDependencies(), ReleaseToArtifact.INSTANCE), Plugin.class)) { - jsonWriter.beginObject(); - jsonWriter.prop(PROPERTY_KEY, child.getKey()); - jsonWriter.prop(PROPERTY_NAME, child.getName()); - jsonWriter.prop(PROPERTY_DESCRIPTION, child.getDescription()); - jsonWriter.endObject(); - } - jsonWriter.endArray(); - - jsonWriter.endObject(); - } - - private static String toJSon(PluginUpdate.Status status) { - switch (status) { - case COMPATIBLE: - return "COMPATIBLE"; - case INCOMPATIBLE: - return "INCOMPATIBLE"; - case REQUIRE_SONAR_UPGRADE: - return "REQUIRES_UPGRADE"; - case DEPENDENCIES_REQUIRE_SONAR_UPGRADE: - return "DEPS_REQUIRE_UPGRADE"; - default: - throw new IllegalArgumentException("Unsupported value of PluginUpdate.Status " + status); - } - } - - private enum ReleaseToArtifact implements Function { - INSTANCE; - - @Override - public Artifact apply(@Nullable Release input) { - if (input == null) { - return null; - } - return input.getArtifact(); - } - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java index 176f369e727..9bb852a5ac1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java @@ -19,12 +19,21 @@ */ package org.sonar.server.plugins.ws; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.collect.Ordering; import org.sonar.api.platform.PluginMetadata; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.updatecenter.common.Artifact; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; +import org.sonar.updatecenter.common.Release; import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static com.google.common.collect.Iterables.filter; +import static com.google.common.collect.Iterables.transform; public class PluginWSCommons { static final String PROPERTY_KEY = "key"; @@ -32,6 +41,7 @@ public class PluginWSCommons { static final String PROPERTY_DESCRIPTION = "description"; static final String PROPERTY_LICENSE = "license"; static final String PROPERTY_VERSION = "version"; + static final String PROPERTY_CATEGORY = "category"; static final String PROPERTY_ORGANIZATION_NAME = "organizationName"; static final String PROPERTY_ORGANIZATION_URL = "organizationUrl"; static final String PROPERTY_DATE = "date"; @@ -41,6 +51,9 @@ public class PluginWSCommons { static final String OBJECT_ARTIFACT = "artifact"; static final String PROPERTY_URL = "url"; static final String PROPERTY_TERMS_AND_CONDITIONS_URL = "termsAndConditionsUrl"; + static final String OBJECT_UPDATE = "update"; + static final String OBJECT_RELEASE = "release"; + static final String ARRAY_REQUIRES = "requires"; public static final Ordering NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural() .onResultOf(PluginMetadataToName.INSTANCE) @@ -79,6 +92,97 @@ public class PluginWSCommons { jsonWriter.endObject(); } + public void writePluginUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { + jsonWriter.beginObject(); + Plugin plugin = pluginUpdate.getPlugin(); + + writeMetadata(jsonWriter, plugin); + + writeRelease(jsonWriter, pluginUpdate.getRelease()); + + writeUpdate(jsonWriter, pluginUpdate); + + jsonWriter.endObject(); + } + + public void writeMetadata(JsonWriter jsonWriter, Plugin plugin) { + jsonWriter.prop(PROPERTY_KEY, plugin.getKey()); + jsonWriter.prop(PROPERTY_NAME, plugin.getName()); + jsonWriter.prop(PROPERTY_CATEGORY, plugin.getCategory()); + jsonWriter.prop(PROPERTY_DESCRIPTION, plugin.getDescription()); + jsonWriter.prop(PROPERTY_LICENSE, plugin.getLicense()); + jsonWriter.prop(PROPERTY_TERMS_AND_CONDITIONS_URL, plugin.getTermsConditionsUrl()); + jsonWriter.prop(PROPERTY_ORGANIZATION_NAME, plugin.getOrganization()); + jsonWriter.prop(PROPERTY_ORGANIZATION_URL, plugin.getOrganizationUrl()); + } + + public void writeRelease(JsonWriter jsonWriter, Release release) { + jsonWriter.name(OBJECT_RELEASE); + jsonWriter.beginObject(); + jsonWriter.prop(PROPERTY_VERSION, release.getVersion().toString()); + jsonWriter.propDate(PROPERTY_DATE, release.getDate()); + + writeArtifact(jsonWriter, release); + + jsonWriter.endObject(); + } + + public void writeArtifact(JsonWriter jsonWriter, Release release) { + jsonWriter.name(OBJECT_ARTIFACT); + jsonWriter.beginObject(); + jsonWriter.prop(PROPERTY_NAME, release.getFilename()); + jsonWriter.prop(PROPERTY_URL, release.getDownloadUrl()); + jsonWriter.endObject(); + } + + public void writeUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { + jsonWriter.name(OBJECT_UPDATE); + jsonWriter.beginObject(); + jsonWriter.prop(PROPERTY_STATUS, toJSon(pluginUpdate.getStatus())); + + jsonWriter.name(ARRAY_REQUIRES); + jsonWriter.beginArray(); + Release release = pluginUpdate.getRelease(); + for (Plugin child : filter(transform(release.getOutgoingDependencies(), ReleaseToArtifact.INSTANCE), Plugin.class)) { + jsonWriter.beginObject(); + jsonWriter.prop(PROPERTY_KEY, child.getKey()); + jsonWriter.prop(PROPERTY_NAME, child.getName()); + jsonWriter.prop(PROPERTY_DESCRIPTION, child.getDescription()); + jsonWriter.endObject(); + } + jsonWriter.endArray(); + + jsonWriter.endObject(); + } + + @VisibleForTesting + static String toJSon(PluginUpdate.Status status) { + switch (status) { + case COMPATIBLE: + return "COMPATIBLE"; + case INCOMPATIBLE: + return "INCOMPATIBLE"; + case REQUIRE_SONAR_UPGRADE: + return "REQUIRES_UPGRADE"; + case DEPENDENCIES_REQUIRE_SONAR_UPGRADE: + return "DEPS_REQUIRE_UPGRADE"; + default: + throw new IllegalArgumentException("Unsupported value of PluginUpdate.Status " + status); + } + } + + private enum ReleaseToArtifact implements Function { + INSTANCE; + + @Override + public Artifact apply(@Nullable Release input) { + if (input == null) { + return null; + } + return input.getArtifact(); + } + } + private enum PluginMetadataToName implements Function { INSTANCE; diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java new file mode 100644 index 00000000000..9a9362f2daf --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java @@ -0,0 +1,124 @@ +/* + * 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.collect.ImmutableSortedSet; +import com.google.common.collect.Ordering; +import com.google.common.io.Resources; +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 org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.updatecenter.common.PluginUpdate; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Comparator; + +import static java.lang.String.CASE_INSENSITIVE_ORDER; + +/** + * Implementation of the {@code updates} action for the Plugins WebService. + */ +public class UpdatesPluginsWsAction implements PluginsWsAction { + + private static final boolean DO_NOT_FORCE_REFRESH = false; + private static final String ARRAY_PLUGINS = "plugins"; + private static final Comparator NAME_KEY_PLUGIN_UPDATE_ORDERING = Ordering.from(CASE_INSENSITIVE_ORDER) + .onResultOf(PluginUpdateToName.INSTANCE) + .compound( + Ordering.from(CASE_INSENSITIVE_ORDER).onResultOf(PluginUpdateToKey.INSTANCE) + ); + + private final UpdateCenterMatrixFactory updateCenterMatrixFactory; + private final PluginWSCommons pluginWSCommons; + + public UpdatesPluginsWsAction(UpdateCenterMatrixFactory updateCenterMatrixFactory, + PluginWSCommons pluginWSCommons) { + this.updateCenterMatrixFactory = updateCenterMatrixFactory; + this.pluginWSCommons = pluginWSCommons; + } + + @Override + public void define(WebService.NewController controller) { + controller.createAction("updates") + .setDescription("Lists plugins installed on the SonarQube instance for which at least one newer version is available, sorted by plugin name." + + "br/>" + + "Each newer version is a separate entry in the returned list, with its update/compatibility status." + + "
" + + "Update status values are: [COMPATIBLE, INCOMPATIBLE, REQUIRES_UPGRADE, DEPS_REQUIRE_UPGRADE]") + .setSince("5.2") + .setHandler(this) + .setResponseExample(Resources.getResource(this.getClass(), "example-updates_plugins.json")); + } + + @Override + public void handle(Request request, Response response) throws Exception { + JsonWriter jsonWriter = response.newJsonWriter(); + jsonWriter.beginObject(); + + writePlugins(jsonWriter); + + jsonWriter.close(); + } + + private void writePlugins(JsonWriter jsonWriter) { + jsonWriter.name(ARRAY_PLUGINS); + jsonWriter.beginArray(); + for (PluginUpdate pluginUpdate : retrieveUpdatablePlugins()) { + pluginWSCommons.writePluginUpdate(jsonWriter, pluginUpdate); + } + jsonWriter.endArray(); + jsonWriter.endObject(); + } + + private Collection retrieveUpdatablePlugins() { + return ImmutableSortedSet.copyOf( + NAME_KEY_PLUGIN_UPDATE_ORDERING, + updateCenterMatrixFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH).findPluginUpdates() + ); + } + + private enum PluginUpdateToKey implements Function { + INSTANCE; + + @Override + public String apply(@Nullable PluginUpdate input) { + if (input == null) { + return null; + } + return input.getPlugin().getKey(); + } + } + + private enum PluginUpdateToName implements Function { + INSTANCE; + + @Override + public String apply(@Nullable PluginUpdate input) { + if (input == null) { + return null; + } + return input.getPlugin().getName(); + } + } +} diff --git a/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json b/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json new file mode 100644 index 00000000000..2cfc80ca7ae --- /dev/null +++ b/server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json @@ -0,0 +1,53 @@ +{ + "plugins": [ + { + "key": "abap", + "name": "ABAP", + "category": "Languages", + "description": "Enable analysis and reporting on ABAP projects", + "license": "Commercial", + "organizationName": "SonarSource", + "organizationUrl": "http://www.sonarsource.com", + "termsAndConditionsUrl": "http://dist.sonarsource.com/SonarSource_Terms_And_Conditions.pdf", + "release": { + "version": "3.2", + "date": "2015-03-10", + "artifact": { + "name": "sonar-abap-plugin-3.2.jar", + "url": "http://dist.sonarsource.com/abap/download/sonar-abap-plugin-3.2.jar" + } + }, + "update": { + "status": "COMPATIBLE", + "requires": [] + } + }, + { + "key": "android", + "name": "Android", + "category": "Languages", + "description": "Import Android Lint reports.", + "license": "GNU LGPL 3", + "organizationName": "SonarSource and Jerome Van Der Linden, Stephane Nicolas, Florian Roncari, Thomas Bores", + "organizationUrl": "http://www.sonarsource.com", + "release": { + "version": "1.0", + "date": "2014-03-31", + "artifact": { + "name": "sonar-android-plugin-1.0.jar", + "url": "http://repository.codehaus.org/org/codehaus/sonar-plugins/android/sonar-android-plugin/1.0/sonar-android-plugin-1.0.jar" + } + }, + "update": { + "status": "COMPATIBLE", + "requires": [ + { + "key": "java", + "name": "Java", + "description": "SonarQube rule engine." + } + ] + } + } + ] +} \ No newline at end of file diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java new file mode 100644 index 00000000000..204a15c9aa2 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java @@ -0,0 +1,84 @@ +/* + * 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.Before; +import org.sonar.api.server.ws.Request; +import org.sonar.api.utils.DateUtils; +import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; +import org.sonar.updatecenter.common.Release; +import org.sonar.updatecenter.common.UpdateCenter; + +import java.net.URL; + +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.updatecenter.common.Version.create; + +public class AbstractUpdateCenterBasedPluginsWsActionTest { + protected static final String DUMMY_CONTROLLER_KEY = "dummy"; + protected static final String JSON_EMPTY_PLUGIN_LIST = + "{" + + " \"plugins\":" + "[]" + + "}"; + protected static final Plugin PLUGIN_1 = new Plugin("p_key_1").setName("p_name_1"); + protected static final Plugin PLUGIN_2 = new Plugin("p_key_2").setName("p_name_2").setDescription("p_desc_2"); + + protected static final Plugin FULL_PROPERTIES_PLUGIN = new Plugin("p_key") + .setName("p_name") + .setCategory("p_category") + .setDescription("p_description") + .setLicense("p_license") + .setOrganization("p_orga_name") + .setOrganizationUrl("p_orga_url") + .setTermsConditionsUrl("p_t_and_c_url"); + protected static final Release FULL_PROPERTIES_PLUGIN_RELEASE = release(FULL_PROPERTIES_PLUGIN, "1.12.1") + .setDate(DateUtils.parseDate("2015-04-16")) + .setDownloadUrl("http://p_file.jar") + .addOutgoingDependency(release(PLUGIN_1, "0.3.6")) + .addOutgoingDependency(release(PLUGIN_2, "1.0.0")); + + protected UpdateCenterMatrixFactory updateCenterFactory = mock(UpdateCenterMatrixFactory.class); + protected UpdateCenter updateCenter = mock(UpdateCenter.class); + protected Request request = mock(Request.class); + protected WsTester.TestResponse response = new WsTester.TestResponse(); + + protected static Release release(Plugin plugin1, String version) { + return new Release(plugin1, create(version)); + } + + protected static PluginUpdate pluginUpdate(Release pluginRelease, PluginUpdate.Status compatible) { + return PluginUpdate.createWithStatus(pluginRelease, compatible); + } + + protected static URL resource(String s) { + Class clazz = AvailablePluginsWsActionTest.class; + return clazz.getResource(clazz.getSimpleName() + "/" + s); + } + + @Before + public void wireMocksTogether() throws Exception { + when(updateCenterFactory.getUpdateCenter(anyBoolean())).thenReturn(updateCenter); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java index 954653a571e..fcf1389e1cc 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java @@ -19,59 +19,23 @@ */ package org.sonar.server.plugins.ws; -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.utils.DateUtils; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; -import org.sonar.server.ws.WsTester; -import org.sonar.updatecenter.common.Plugin; -import org.sonar.updatecenter.common.PluginUpdate; -import org.sonar.updatecenter.common.Release; -import org.sonar.updatecenter.common.UpdateCenter; - -import java.net.URL; - import static com.google.common.collect.ImmutableList.of; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; import static org.sonar.test.JsonAssert.assertJson; import static org.sonar.updatecenter.common.PluginUpdate.Status.COMPATIBLE; import static org.sonar.updatecenter.common.PluginUpdate.Status.DEPENDENCIES_REQUIRE_SONAR_UPGRADE; import static org.sonar.updatecenter.common.PluginUpdate.Status.INCOMPATIBLE; import static org.sonar.updatecenter.common.PluginUpdate.Status.REQUIRE_SONAR_UPGRADE; -import static org.sonar.updatecenter.common.Version.create; - -public class AvailablePluginsWsActionTest { - private static final String DUMMY_CONTROLLER_KEY = "dummy"; - private static final String JSON_EMPTY_PLUGIN_LIST = - "{" + - " \"plugins\":" + "[]" + - "}"; - private static final Plugin PLUGIN_1 = new Plugin("p_key_1").setName("p_name_1"); - private static final Plugin PLUGIN_2 = new Plugin("p_key_2").setName("p_name_2").setDescription("p_desc_2"); - - @Mock - private UpdateCenterMatrixFactory updateCenterFactory; - @Mock - private UpdateCenter updateCenter; - @InjectMocks - private AvailablePluginsWsAction underTest; - - private Request request = mock(Request.class); - private WsTester.TestResponse response = new WsTester.TestResponse(); - - @Before - public void createAndWireMocksTogether() throws Exception { - initMocks(this); - when(updateCenterFactory.getUpdateCenter(anyBoolean())).thenReturn(updateCenter); - } + +import org.junit.Test; +import org.sonar.api.server.ws.WebService; +import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.PluginUpdate; + +public class AvailablePluginsWsActionTest extends AbstractUpdateCenterBasedPluginsWsActionTest { + + private AvailablePluginsWsAction underTest = new AvailablePluginsWsAction(updateCenterFactory, new PluginWSCommons()); @Test public void action_available_is_defined() throws Exception { @@ -99,21 +63,8 @@ public class AvailablePluginsWsActionTest { @Test public void verify_properties_displayed_in_json_per_plugin() throws Exception { - Plugin plugin = new Plugin("p_key") - .setName("p_name") - .setCategory("p_category") - .setDescription("p_description") - .setLicense("p_license") - .setOrganization("p_orga_name") - .setOrganizationUrl("p_orga_url") - .setTermsConditionsUrl("p_t_and_c_url"); - Release pluginRelease = release(plugin, "1.12.1") - .setDate(DateUtils.parseDate("2015-04-16")) - .setDownloadUrl("http://p_file.jar") - .addOutgoingDependency(release(PLUGIN_1, "0.3.6")) - .addOutgoingDependency(release(PLUGIN_2, "1.0.0")); when(updateCenter.findAvailablePlugins()).thenReturn(of( - pluginUpdate(pluginRelease, COMPATIBLE) + pluginUpdate(FULL_PROPERTIES_PLUGIN_RELEASE, COMPATIBLE) )); underTest.handle(request, response); @@ -121,10 +72,6 @@ public class AvailablePluginsWsActionTest { assertJson(response.outputAsString()).isSimilarTo(resource("properties_per_plugin.json")); } - private Release release(Plugin plugin1, String version) { - return new Release(plugin1, create(version)); - } - @Test public void status_COMPATIBLE_is_displayed_COMPATIBLE_in_JSON() throws Exception { checkStatusDisplayedInJson(COMPATIBLE, "COMPATIBLE"); @@ -165,12 +112,4 @@ public class AvailablePluginsWsActionTest { ); } - private static PluginUpdate pluginUpdate(Release pluginRelease, PluginUpdate.Status compatible) { - return PluginUpdate.createWithStatus(pluginRelease, compatible); - } - - private static URL resource(String s) { - Class clazz = AvailablePluginsWsActionTest.class; - return clazz.getResource(clazz.getSimpleName() + "/" + s); - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java index b4b8a31f351..2c77cf638be 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java @@ -23,14 +23,25 @@ import org.junit.Test; import org.sonar.api.utils.text.JsonWriter; import org.sonar.core.plugins.DefaultPluginMetadata; import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; +import org.sonar.updatecenter.common.Release; +import org.sonar.updatecenter.common.Version; import java.io.File; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.utils.DateUtils.parseDate; import static org.sonar.core.plugins.DefaultPluginMetadata.create; +import static org.sonar.server.plugins.ws.PluginWSCommons.toJSon; import static org.sonar.test.JsonAssert.assertJson; +import static org.sonar.updatecenter.common.PluginUpdate.Status.COMPATIBLE; +import static org.sonar.updatecenter.common.PluginUpdate.Status.DEPENDENCIES_REQUIRE_SONAR_UPGRADE; +import static org.sonar.updatecenter.common.PluginUpdate.Status.INCOMPATIBLE; +import static org.sonar.updatecenter.common.PluginUpdate.Status.REQUIRE_SONAR_UPGRADE; public class PluginWSCommonsTest { - public static final DefaultPluginMetadata GIT_PLUGIN_METADATA = create("scmgit") + private static final DefaultPluginMetadata GIT_PLUGIN_METADATA = create("scmgit") .setName("Git") .setDescription("Git SCM Provider.") .setVersion("1.0") @@ -40,13 +51,23 @@ public class PluginWSCommonsTest { .setHomepage("http://redirect.sonarsource.com/plugins/scmgit.html") .setIssueTrackerUrl("http://jira.codehaus.org/browse/SONARSCGIT") .setFile(new File("/home/user/sonar-scm-git-plugin-1.0.jar")); + private static final Plugin PLUGIN = new Plugin("p_key") + .setName("p_name") + .setCategory("p_category") + .setDescription("p_description") + .setLicense("p_license") + .setOrganization("p_orga_name") + .setOrganizationUrl("p_orga_url") + .setTermsConditionsUrl("p_t_and_c_url"); + private static final Release RELEASE = new Release(PLUGIN, version("1.0")).setDate(parseDate("2015-04-16")) + .setDownloadUrl("http://toto.com/file.jar"); private WsTester.TestResponse response = new WsTester.TestResponse(); private JsonWriter jsonWriter = response.newJsonWriter(); private PluginWSCommons underTest = new PluginWSCommons(); @Test - public void verify_properties_written_by_writePluginMetadata() throws Exception { + public void verify_properties_written_by_writePluginMetadata() { underTest.writePluginMetadata(jsonWriter, GIT_PLUGIN_METADATA); jsonWriter.close(); @@ -67,7 +88,7 @@ public class PluginWSCommonsTest { } @Test - public void verify_properties_written_by_writeMetadata() throws Exception { + public void verify_properties_written_by_writeMetadata() { jsonWriter.beginObject(); underTest.writeMetadata(jsonWriter, GIT_PLUGIN_METADATA); jsonWriter.endObject(); @@ -87,7 +108,7 @@ public class PluginWSCommonsTest { } @Test - public void writeArtifact_supports_null_file() throws Exception { + public void writeArtifact_from_pluginMetadata_supports_null_file() { jsonWriter.beginObject(); underTest.writeArtifact(jsonWriter, DefaultPluginMetadata.create("key")); jsonWriter.endObject(); @@ -97,7 +118,7 @@ public class PluginWSCommonsTest { } @Test - public void writeArtifact_writes_artifact_object_and_file_name() throws Exception { + public void writeArtifact_from_pluginMetadata_writes_artifact_object_and_file_name() { jsonWriter.beginObject(); underTest.writeArtifact(jsonWriter, GIT_PLUGIN_METADATA); jsonWriter.endObject(); @@ -109,4 +130,135 @@ public class PluginWSCommonsTest { " }" + "}"); } -} \ No newline at end of file + + @Test + public void verify_properties_written_by_writePluginUpdate() { + underTest.writePluginUpdate(jsonWriter, PluginUpdate.createForPluginRelease(RELEASE, version("1.0"))); + + jsonWriter.close(); + assertJson(response.outputAsString()).isSimilarTo("{" + + " \"key\": \"p_key\"," + + " \"name\": \"p_name\"," + + " \"description\": \"p_description\"," + + " \"category\": \"p_category\"," + + " \"license\": \"p_license\"," + + " \"organizationName\": \"p_orga_name\"," + + " \"organizationUrl\": \"p_orga_url\"," + + " \"termsAndConditionsUrl\": \"p_t_and_c_url\"" + + " \"release\": {" + + " \"version\": \"1.0\"," + + " \"date\": \"2015-04-16\"," + + " \"artifact\": {" + + " \"name\": \"file.jar\"," + + " \"url\": \"http://toto.com/file.jar\"" + + " }" + + " }" + + "}"); + } + + @Test + public void verify_properties_written_by_writeMetadata_from_plugin() { + jsonWriter.beginObject(); + underTest.writeMetadata(jsonWriter, PLUGIN); + jsonWriter.endObject(); + + jsonWriter.close(); + assertJson(response.outputAsString()).isSimilarTo("{" + + " \"key\": \"p_key\"," + + " \"name\": \"p_name\"," + + " \"description\": \"p_description\"," + + " \"category\": \"p_category\"," + + " \"license\": \"p_license\"," + + " \"organizationName\": \"p_orga_name\"," + + " \"organizationUrl\": \"p_orga_url\"," + + " \"termsAndConditionsUrl\": \"p_t_and_c_url\"" + + "}"); + } + + @Test + public void writeRelease_writes_artifact_object_and_file_name() { + jsonWriter.beginObject(); + underTest.writeRelease(jsonWriter, RELEASE); + jsonWriter.endObject(); + + jsonWriter.close(); + assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo("{" + + " \"release\": {" + + " \"version\": \"1.0\"," + + " \"date\": \"2015-04-16\"," + + " \"artifact\": {" + + " \"name\": \"file.jar\"," + + " \"url\": \"http://toto.com/file.jar\"" + + " }" + + " }" + + "}"); + } + + @Test + public void writeArtifact_from_release_writes_artifact_object_and_file_name() { + jsonWriter.beginObject(); + underTest.writeArtifact(jsonWriter, release("p_key").setDownloadUrl("http://toto.com/file.jar")); + jsonWriter.endObject(); + + jsonWriter.close(); + assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo("{" + + " \"artifact\": {" + + " \"name\": \"file.jar\"," + + " \"url\": \"http://toto.com/file.jar\"" + + " }" + + "}"); + } + + @Test + public void status_COMPATIBLE_is_displayed_COMPATIBLE_in_JSON() { + assertThat(toJSon(COMPATIBLE)).isEqualTo("COMPATIBLE"); + } + + @Test + public void status_INCOMPATIBLE_is_displayed_INCOMPATIBLE_in_JSON() { + assertThat(toJSon(INCOMPATIBLE)).isEqualTo("INCOMPATIBLE"); + } + + @Test + public void status_REQUIRE_SONAR_UPGRADE_is_displayed_REQUIRES_UPGRADE_in_JSON() { + assertThat(toJSon(REQUIRE_SONAR_UPGRADE)).isEqualTo("REQUIRES_UPGRADE"); + } + + @Test + public void status_DEPENDENCIES_REQUIRE_SONAR_UPGRADE_is_displayed_DEPS_REQUIRE_UPGRADE_in_JSON() { + assertThat(toJSon(DEPENDENCIES_REQUIRE_SONAR_UPGRADE)).isEqualTo("DEPS_REQUIRE_UPGRADE"); + } + + @Test + public void writeUpdate_renders_key_name_and_description_of_outgoing_dependencies() { + PluginUpdate pluginUpdate = new PluginUpdate(); + pluginUpdate.setRelease( + new Release(PLUGIN, version("1.0")).addOutgoingDependency(RELEASE) + ); + + jsonWriter.beginObject(); + underTest.writeUpdate(jsonWriter, pluginUpdate); + jsonWriter.endObject(); + + jsonWriter.close(); + assertJson(response.outputAsString()).isSimilarTo("{" + + " \"update\": {" + + " \"requires\": [" + + " {" + + " \"key\": \"p_key\"," + + " \"name\": \"p_name\"," + + " \"description\": \"p_description\"" + + " }" + + " ]" + + " }" + + "}"); + } + + private static Version version(String version) { + return Version.create(version); + } + + private static Release release(String key) { + return new Release(new Plugin(key), version("1.0")); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java new file mode 100644 index 00000000000..81f0e860232 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java @@ -0,0 +1,138 @@ +/* + * 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.WebService; +import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; +import org.sonar.updatecenter.common.Release; +import org.sonar.updatecenter.common.Version; + +import static com.google.common.collect.ImmutableList.of; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; +import static org.sonar.test.JsonAssert.assertJson; +import static org.sonar.updatecenter.common.PluginUpdate.Status.COMPATIBLE; + +public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPluginsWsActionTest { + + private UpdatesPluginsWsAction underTest = new UpdatesPluginsWsAction(updateCenterFactory, new PluginWSCommons()); + + @Test + public void action_updatable_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("updates"); + + 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_no_plugin_available() throws Exception { + underTest.handle(request, response); + + assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo(JSON_EMPTY_PLUGIN_LIST); + } + + @Test + public void verify_properties_displayed_in_json_per_plugin() throws Exception { + when(updateCenter.findPluginUpdates()).thenReturn(of( + pluginUpdate(FULL_PROPERTIES_PLUGIN_RELEASE, COMPATIBLE) + )); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo(resource("properties_per_plugin.json")); + } + + @Test + public void status_COMPATIBLE_is_displayed_COMPATIBLE_in_JSON() throws Exception { + when(updateCenter.findPluginUpdates()).thenReturn(of( + pluginUpdate(release(PLUGIN_1, "1.0.0"), COMPATIBLE) + )); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).isSimilarTo( + "{" + + " \"plugins\": [" + + " {" + + " \"update\": {" + + " \"status\": \"COMPATIBLE\"" + + " }" + + " }" + + " ]" + + "}" + ); + } + + @Test + public void plugins_are_sorted_by_name_then_key_and_made_unique() throws Exception { + when(updateCenter.findPluginUpdates()).thenReturn(of( + pluginUpdate("key2", "name2"), + pluginUpdate("key1", "name2"), + pluginUpdate("key2", "name2"), + pluginUpdate("key0", "name0"), + pluginUpdate("key1", "name1") + )); + + underTest.handle(request, response); + + assertJson(response.outputAsString()).setStrictArrayOrder(true).isSimilarTo( + "{" + + " \"plugins\": [" + + " {" + + " \"key\": \"key0\"," + + " \"name\": \"name0\"," + + " }," + + " {" + + " \"key\": \"key1\"," + + " \"name\": \"name1\"," + + " }," + + " {" + + " \"key\": \"key1\"," + + " \"name\": \"name2\"," + + " }," + + " {" + + " \"key\": \"key2\"," + + " \"name\": \"name2\"," + + " }," + + " ]" + + "}" + ); + } + + private static PluginUpdate pluginUpdate(String key, String name) { + return PluginUpdate.createWithStatus( + new Release(new Plugin(key).setName(name), Version.create("1.0")), + COMPATIBLE + ); + } +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/plugins/ws/UpdatablePluginsWsActionTest/properties_per_plugin.json b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ws/UpdatablePluginsWsActionTest/properties_per_plugin.json new file mode 100644 index 00000000000..055feb3713b --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/plugins/ws/UpdatablePluginsWsActionTest/properties_per_plugin.json @@ -0,0 +1,36 @@ +{ + "plugins": [ + { + "key": "p_key", + "name": "p_name", + "category": "p_category", + "description": "p_description", + "license": "p_license", + "organizationName": "p_orga_name", + "organizationUrl": "p_orga_url", + "termsAndConditionsUrl": "p_t_and_c_url", + "release": { + "version": "1.12.1", + "date": "2015-04-16", + "artifact": { + "name": "p_file.jar", + "url": "http://p_file.jar" + } + }, + "update": { + "status": "COMPATIBLE", + "requires": [ + { + "key": "p_key_1", + "name": "p_name_1" + }, + { + "key": "p_key_2", + "name": "p_name_2", + "description": "p_desc_2" + } + ] + } + } + ] +} -- 2.39.5