diff options
10 files changed, 495 insertions, 97 deletions
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 db7e78f8c35..47f16ba9567 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 @@ -227,6 +227,7 @@ import org.sonar.server.plugins.ws.CancelAllPluginsWsAction; import org.sonar.server.plugins.ws.InstallPluginsWsAction; import org.sonar.server.plugins.ws.InstalledPluginsWsAction; import org.sonar.server.plugins.ws.PendingPluginsWsAction; +import org.sonar.server.plugins.ws.PluginUpdateAggregator; import org.sonar.server.plugins.ws.PluginWSCommons; import org.sonar.server.plugins.ws.PluginsWs; import org.sonar.server.plugins.ws.UninstallPluginsWsAction; @@ -893,6 +894,7 @@ class ServerComponents { // Plugins WS pico.addSingleton(PluginWSCommons.class); + pico.addSingleton(PluginUpdateAggregator.class); pico.addSingleton(InstalledPluginsWsAction.class); pico.addSingleton(AvailablePluginsWsAction.class); pico.addSingleton(UpdatesPluginsWsAction.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginUpdateAggregator.java b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginUpdateAggregator.java new file mode 100644 index 00000000000..e15954997f8 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginUpdateAggregator.java @@ -0,0 +1,119 @@ +/* + * 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.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.collect.Iterables.transform; +import static java.lang.String.format; + +public class PluginUpdateAggregator { + + public Collection<PluginUpdateAggregate> aggregate(@Nullable Collection<PluginUpdate> pluginUpdates) { + if (pluginUpdates == null || pluginUpdates.isEmpty()) { + return Collections.emptyList(); + } + + Map<Plugin, PluginUpdateAggregateBuilder> builders = Maps.newHashMap(); + for (PluginUpdate pluginUpdate : pluginUpdates) { + Plugin plugin = pluginUpdate.getPlugin(); + PluginUpdateAggregateBuilder builder = builders.get(plugin); + if (builder == null) { + builder = PluginUpdateAggregateBuilder.builderFor(plugin); + builders.put(plugin, builder); + } + builder.add(pluginUpdate); + } + + return Lists.newArrayList(transform(builders.values(), BuilderToPluginUpdateAggregate.INSTANCE)); + } + + private enum BuilderToPluginUpdateAggregate implements Function<PluginUpdateAggregateBuilder, PluginUpdateAggregate> { + INSTANCE; + + @Override + public PluginUpdateAggregate apply(@Nonnull PluginUpdateAggregateBuilder input) { + return input.build(); + } + } + + @VisibleForTesting + static class PluginUpdateAggregateBuilder { + private final Plugin plugin; + + private final List<PluginUpdate> updates = Lists.newArrayListWithExpectedSize(1); + + // use static method + private PluginUpdateAggregateBuilder(Plugin plugin) { + this.plugin = plugin; + } + + public static PluginUpdateAggregateBuilder builderFor(Plugin plugin) { + return new PluginUpdateAggregateBuilder(checkNotNull(plugin)); + } + + public PluginUpdateAggregateBuilder add(PluginUpdate pluginUpdate) { + checkArgument( + this.plugin.equals(pluginUpdate.getPlugin()), + format("This builder only accepts PluginUpdate instances for plugin %s", plugin)); + this.updates.add(pluginUpdate); + return this; + } + + public PluginUpdateAggregate build() { + return new PluginUpdateAggregate(this); + } + } + + public static class PluginUpdateAggregate { + private final Plugin plugin; + + private final Collection<PluginUpdate> updates; + + protected PluginUpdateAggregate(PluginUpdateAggregateBuilder builder) { + this.plugin = builder.plugin; + this.updates = ImmutableList.copyOf(builder.updates); + } + + public Plugin getPlugin() { + return plugin; + } + + public Collection<PluginUpdate> getUpdates() { + return updates; + } + + } +} 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 5924784a292..a1367b4e66d 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 @@ -30,7 +30,6 @@ import org.sonar.updatecenter.common.PluginUpdate; import org.sonar.updatecenter.common.Release; import javax.annotation.Nonnull; -import javax.annotation.Nullable; import java.util.Comparator; import static com.google.common.collect.Iterables.filter; @@ -60,11 +59,13 @@ public class PluginWSCommons { public static final Ordering<PluginMetadata> NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural() .onResultOf(PluginMetadataToName.INSTANCE) .compound(Ordering.natural().onResultOf(PluginMetadataToKey.INSTANCE)); - public static final Comparator<PluginUpdate> NAME_KEY_PLUGIN_UPDATE_ORDERING = Ordering.from(CASE_INSENSITIVE_ORDER) - .onResultOf(PluginUpdateToName.INSTANCE) - .compound( - Ordering.from(CASE_INSENSITIVE_ORDER).onResultOf(PluginUpdateToKey.INSTANCE) - ); + public static final Comparator<Plugin> NAME_KEY_PLUGIN_ORDERING = Ordering.from(CASE_INSENSITIVE_ORDER) + .onResultOf(PluginToName.INSTANCE) + .compound( + Ordering.from(CASE_INSENSITIVE_ORDER).onResultOf(PluginToKey.INSTANCE) + ); + public static final Comparator<PluginUpdate> NAME_KEY_PLUGIN_UPDATE_ORDERING = Ordering.from(NAME_KEY_PLUGIN_ORDERING) + .onResultOf(PluginUpdateToPlugin.INSTANCE); public void writePluginMetadata(JsonWriter jsonWriter, PluginMetadata pluginMetadata) { jsonWriter.beginObject(); @@ -142,9 +143,44 @@ public class PluginWSCommons { jsonWriter.endObject(); } + /** + * Write an "update" object to the specified jsonwriter. + * <pre> + * "update": { + * "status": "COMPATIBLE", + * "requires": [ + * { + * "key": "java", + * "name": "Java", + * "description": "SonarQube rule engine." + * } + * ] + * } + * </pre> + */ public void writeUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { jsonWriter.name(OBJECT_UPDATE); jsonWriter.beginObject(); + + writeUpdateProperties(jsonWriter, pluginUpdate); + + jsonWriter.endObject(); + } + + /** + * Write the update properties to the specified jsonwriter. + * <pre> + * "status": "COMPATIBLE", + * "requires": [ + * { + * "key": "java", + * "name": "Java", + * "description": "SonarQube rule engine." + * } + * ] + * </pre> + */ + public void writeUpdateProperties(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { jsonWriter.prop(PROPERTY_STATUS, toJSon(pluginUpdate.getStatus())); jsonWriter.name(ARRAY_REQUIRES); @@ -158,8 +194,6 @@ public class PluginWSCommons { jsonWriter.endObject(); } jsonWriter.endArray(); - - jsonWriter.endObject(); } @VisibleForTesting @@ -182,10 +216,7 @@ public class PluginWSCommons { INSTANCE; @Override - public Artifact apply(@Nullable Release input) { - if (input == null) { - return null; - } + public Artifact apply(@Nonnull Release input) { return input.getArtifact(); } } @@ -208,27 +239,30 @@ public class PluginWSCommons { } } - private enum PluginUpdateToKey implements Function<PluginUpdate, String> { + private enum PluginUpdateToPlugin implements Function<PluginUpdate, Plugin> { INSTANCE; @Override - public String apply(@Nullable PluginUpdate input) { - if (input == null) { - return null; - } - return input.getPlugin().getKey(); + public Plugin apply(@Nonnull PluginUpdate input) { + return input.getPlugin(); } } - private enum PluginUpdateToName implements Function<PluginUpdate, String> { + private enum PluginToKey implements Function<Plugin, String> { INSTANCE; @Override - public String apply(@Nullable PluginUpdate input) { - if (input == null) { - return null; - } - return input.getPlugin().getName(); + public String apply(@Nonnull Plugin input) { + return input.getKey(); + } + } + + private enum PluginToName implements Function<Plugin, String> { + INSTANCE; + + @Override + public String apply(@Nonnull Plugin input) { + return input.getName(); } } } 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 index 22af90dc79f..16821425eec 100644 --- 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 @@ -19,18 +19,22 @@ */ 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.server.plugins.ws.PluginUpdateAggregator.PluginUpdateAggregate; +import org.sonar.updatecenter.common.Plugin; import org.sonar.updatecenter.common.PluginUpdate; +import javax.annotation.Nonnull; import java.util.Collection; - -import static org.sonar.server.plugins.ws.PluginWSCommons.NAME_KEY_PLUGIN_UPDATE_ORDERING; +import java.util.List; /** * Implementation of the {@code updates} action for the Plugins WebService. @@ -39,14 +43,27 @@ public class UpdatesPluginsWsAction implements PluginsWsAction { private static final boolean DO_NOT_FORCE_REFRESH = false; private static final String ARRAY_PLUGINS = "plugins"; + private static final String ARRAY_UPDATES = "updates"; + + private static final Ordering<PluginUpdateAggregate> NAME_KEY_PLUGIN_UPGRADE_AGGREGATE_ORDERING = + Ordering.from(PluginWSCommons.NAME_KEY_PLUGIN_ORDERING).onResultOf(PluginUpdateAggregateToPlugin.INSTANCE); + private static final Ordering<PluginUpdate> PLUGIN_UPDATE_BY_VERSION_ORDERING = Ordering.natural().onResultOf(new Function<PluginUpdate, String>() { + @Override + public String apply(@Nonnull PluginUpdate input) { + return input.getRelease().getVersion().toString(); + } + }); private final UpdateCenterMatrixFactory updateCenterMatrixFactory; private final PluginWSCommons pluginWSCommons; + private final PluginUpdateAggregator aggregator; public UpdatesPluginsWsAction(UpdateCenterMatrixFactory updateCenterMatrixFactory, - PluginWSCommons pluginWSCommons) { + PluginWSCommons pluginWSCommons, + PluginUpdateAggregator aggregator) { this.updateCenterMatrixFactory = updateCenterMatrixFactory; this.pluginWSCommons = pluginWSCommons; + this.aggregator = aggregator; } @Override @@ -54,7 +71,7 @@ public class UpdatesPluginsWsAction implements PluginsWsAction { 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." + + "Each newer version is listed, ordered from the oldest to the newest, with its own update/compatibility status." + "<br/>" + "Update status values are: [COMPATIBLE, INCOMPATIBLE, REQUIRES_UPGRADE, DEPS_REQUIRE_UPGRADE]") .setSince("5.2") @@ -75,17 +92,50 @@ public class UpdatesPluginsWsAction implements PluginsWsAction { private void writePlugins(JsonWriter jsonWriter) { jsonWriter.name(ARRAY_PLUGINS); jsonWriter.beginArray(); - for (PluginUpdate pluginUpdate : retrieveUpdatablePlugins()) { - pluginWSCommons.writePluginUpdate(jsonWriter, pluginUpdate); + for (PluginUpdateAggregate aggregate : retrieveUpdatablePlugins()) { + writePluginUpdateAggregate(jsonWriter, aggregate); } jsonWriter.endArray(); jsonWriter.endObject(); } - private Collection<PluginUpdate> retrieveUpdatablePlugins() { + private void writePluginUpdateAggregate(JsonWriter jsonWriter, PluginUpdateAggregate aggregate) { + jsonWriter.beginObject(); + Plugin plugin = aggregate.getPlugin(); + + pluginWSCommons.writeMetadata(jsonWriter, plugin); + + writeUpdates(jsonWriter, aggregate.getUpdates()); + + jsonWriter.endObject(); + } + + private void writeUpdates(JsonWriter jsonWriter, Collection<PluginUpdate> pluginUpdates) { + jsonWriter.name(ARRAY_UPDATES).beginArray(); + for (PluginUpdate pluginUpdate : ImmutableSortedSet.copyOf(PLUGIN_UPDATE_BY_VERSION_ORDERING, pluginUpdates)) { + jsonWriter.beginObject(); + pluginWSCommons.writeRelease(jsonWriter, pluginUpdate.getRelease()); + pluginWSCommons.writeUpdateProperties(jsonWriter, pluginUpdate); + jsonWriter.endObject(); + } + jsonWriter.endArray(); + } + + private Collection<PluginUpdateAggregate> retrieveUpdatablePlugins() { + List<PluginUpdate> pluginUpdates = updateCenterMatrixFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH).findPluginUpdates(); + // aggregates updates of the same plugin to a single object and sort these objects by plugin name then key return ImmutableSortedSet.copyOf( - NAME_KEY_PLUGIN_UPDATE_ORDERING, - updateCenterMatrixFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH).findPluginUpdates() + NAME_KEY_PLUGIN_UPGRADE_AGGREGATE_ORDERING, + aggregator.aggregate(pluginUpdates) ); } + + private enum PluginUpdateAggregateToPlugin implements Function<PluginUpdateAggregate, Plugin> { + INSTANCE; + + @Override + public Plugin apply(@Nonnull PluginUpdateAggregate input) { + return input.getPlugin(); + } + } } 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 index 2cfc80ca7ae..17e592ee7c1 100644 --- 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 @@ -9,18 +9,32 @@ "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" + "updates": [ + { + "release": { + "version": "3.1", + "date": "2014-12-21", + "artifact": { + "name": "sonar-abap-plugin-3.1.jar", + "url": "http://dist.sonarsource.com/abap/download/sonar-abap-plugin-3.1.jar" + } + }, + "status": "INCOMPATIBLE", + "requires": [] + }, + { + "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" + } + }, + "status": "COMPATIBLE", + "requires": [] } - }, - "update": { - "status": "COMPATIBLE", - "requires": [] - } + ] }, { "key": "android", @@ -30,24 +44,26 @@ "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" + "updates": [ + { + "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" + } + }, + "status": "COMPATIBLE", + "requires": [ + { + "key": "java", + "name": "Java", + "description": "SonarQube rule engine." + } + ] } - }, - "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 index 94042b73857..c570497b297 100644 --- 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 @@ -21,7 +21,6 @@ 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; @@ -47,19 +46,6 @@ public class AbstractUpdateCenterBasedPluginsWsActionTest { 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); @@ -81,9 +67,9 @@ public class AbstractUpdateCenterBasedPluginsWsActionTest { protected static PluginUpdate pluginUpdate(String key, String name) { return PluginUpdate.createWithStatus( - new Release(new Plugin(key).setName(name), Version.create("1.0")), - COMPATIBLE - ); + new Release(new Plugin(key).setName(name), Version.create("1.0")), + COMPATIBLE + ); } @Before 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 9cee6366461..27a1f245686 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 @@ -21,11 +21,11 @@ package org.sonar.server.plugins.ws; import org.junit.Test; import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.DateUtils; 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; @@ -38,6 +38,20 @@ import static org.sonar.updatecenter.common.PluginUpdate.Status.REQUIRE_SONAR_UP public class AvailablePluginsWsActionTest extends AbstractUpdateCenterBasedPluginsWsActionTest { + private 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"); + private 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")); + private AvailablePluginsWsAction underTest = new AvailablePluginsWsAction(updateCenterFactory, new PluginWSCommons()); @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregateBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregateBuilderTest.java new file mode 100644 index 00000000000..09c5ee87c3c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregateBuilderTest.java @@ -0,0 +1,58 @@ +/* + * 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.server.plugins.ws.PluginUpdateAggregator.PluginUpdateAggregateBuilder; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.PluginUpdate; +import org.sonar.updatecenter.common.Release; +import org.sonar.updatecenter.common.Version; + +public class PluginUpdateAggregateBuilderTest { + + private static final Plugin PLUGIN_1 = new Plugin("key1"); + private static final Plugin PLUGIN_2 = new Plugin("key2"); + private static final Version SOME_VERSION = Version.create("1.0"); + private static final PluginUpdate.Status SOME_STATUS = PluginUpdate.Status.COMPATIBLE; + + @Test(expected = NullPointerException.class) + public void plugin_can_not_be_null_and_builderFor_enforces_it_with_NPE() throws Exception { + PluginUpdateAggregateBuilder.builderFor(null); + } + + @Test(expected = IllegalArgumentException.class) + public void add_throws_IAE_when_plugin_is_not_equal_to_the_one_of_the_builder() throws Exception { + PluginUpdateAggregateBuilder builder = PluginUpdateAggregateBuilder.builderFor(PLUGIN_1); + + builder.add(createPluginUpdate(PLUGIN_2)); + } + + @Test + public void add_uses_equals_which_takes_only_key_into_account() throws Exception { + PluginUpdateAggregateBuilder builder = PluginUpdateAggregateBuilder.builderFor(PLUGIN_1); + + builder.add(createPluginUpdate(new Plugin(PLUGIN_1.getKey()))); + } + + private static PluginUpdate createPluginUpdate(Plugin plugin) { + return PluginUpdate.createWithStatus(new Release(plugin, SOME_VERSION), SOME_STATUS); + } +}
\ No newline at end of file diff --git a/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregatorTest.java new file mode 100644 index 00000000000..b7e6140a562 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregatorTest.java @@ -0,0 +1,86 @@ +/* + * 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.Test; +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.util.Collection; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PluginUpdateAggregatorTest { + + public static final Version SOME_VERSION = Version.create("1.0"); + public static final PluginUpdate.Status SOME_STATUS = PluginUpdate.Status.COMPATIBLE; + private PluginUpdateAggregator underTest = new PluginUpdateAggregator(); + + @Test + public void aggregates_returns_an_empty_collection_when_plugin_collection_is_null() throws Exception { + assertThat(underTest.aggregate(null)).isEmpty(); + } + + @Test + public void aggregates_returns_an_empty_collection_when_plugin_collection_is_empty() throws Exception { + assertThat(underTest.aggregate(Collections.<PluginUpdate>emptyList())).isEmpty(); + } + + @Test + public void aggregates_groups_pluginUpdate_per_plugin_key() throws Exception { + Collection<PluginUpdateAggregator.PluginUpdateAggregate> aggregates = underTest.aggregate(ImmutableList.of( + createPluginUpdate("key1"), + createPluginUpdate("key1"), + createPluginUpdate("key0"), + createPluginUpdate("key2"), + createPluginUpdate("key0") + )); + + assertThat(aggregates).hasSize(3); + assertThat(aggregates).extracting("plugin.key").containsOnlyOnce("key1", "key0", "key2"); + } + + @Test + public void aggregate_put_pluginUpdates_with_same_plugin_in_the_same_PluginUpdateAggregate() throws Exception { + PluginUpdate pluginUpdate1 = createPluginUpdate("key1"); + PluginUpdate pluginUpdate2 = createPluginUpdate("key1"); + PluginUpdate pluginUpdate3 = createPluginUpdate("key1"); + Collection<PluginUpdateAggregator.PluginUpdateAggregate> aggregates = underTest.aggregate(ImmutableList.of( + pluginUpdate1, + pluginUpdate2, + pluginUpdate3 + )); + + assertThat(aggregates).hasSize(1); + Collection<PluginUpdate> releases = aggregates.iterator().next().getUpdates(); + assertThat(releases).hasSize(3); + assertThat(releases).contains(pluginUpdate1); + assertThat(releases).contains(pluginUpdate2); + assertThat(releases).contains(pluginUpdate3); + } + + private PluginUpdate createPluginUpdate(String pluginKey) { + return PluginUpdate.createWithStatus(new Release(new Plugin(pluginKey), SOME_VERSION), SOME_STATUS); + } +} 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 index a5baa19f4a7..f5cf5b8ef51 100644 --- 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 @@ -21,17 +21,51 @@ package org.sonar.server.plugins.ws; import org.junit.Test; import org.sonar.api.server.ws.WebService; +import org.sonar.api.utils.DateUtils; import org.sonar.server.ws.WsTester; +import org.sonar.updatecenter.common.Plugin; +import org.sonar.updatecenter.common.Release; 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; +import static org.sonar.updatecenter.common.PluginUpdate.Status.INCOMPATIBLE; public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPluginsWsActionTest { - - private UpdatesPluginsWsAction underTest = new UpdatesPluginsWsAction(updateCenterFactory, new PluginWSCommons()); + private static final Plugin JAVA_PLUGIN = new Plugin("java") + .setName("Java") + .setDescription("SonarQube rule engine."); + private static final Plugin ABAP_PLUGIN = new Plugin("abap") + .setName("ABAP") + .setCategory("Languages") + .setDescription("Enable analysis and reporting on ABAP projects") + .setLicense("Commercial") + .setOrganization("SonarSource") + .setOrganizationUrl("http://www.sonarsource.com") + .setTermsConditionsUrl("http://dist.sonarsource.com/SonarSource_Terms_And_Conditions.pdf"); + private static final Release ABAP_31 = release(ABAP_PLUGIN, "3.1") + .setDate(DateUtils.parseDate("2014-12-21")) + .setDownloadUrl("http://dist.sonarsource.com/abap/download/sonar-abap-plugin-3.1.jar"); + private static final Release ABAP_32 = release(ABAP_PLUGIN, "3.2") + .setDate(DateUtils.parseDate("2015-03-10")) + .setDownloadUrl("http://dist.sonarsource.com/abap/download/sonar-abap-plugin-3.2.jar"); + private static final Plugin ANDROID_PLUGIN = new Plugin("android") + .setName("Android") + .setCategory("Languages") + .setDescription("Import Android Lint reports.") + .setLicense("GNU LGPL 3") + .setOrganization("SonarSource and Jerome Van Der Linden, Stephane Nicolas, Florian Roncari, Thomas Bores") + .setOrganizationUrl("http://www.sonarsource.com"); + private static final Release ANDROID_10 = release(ANDROID_PLUGIN, "1.0") + .setDate(DateUtils.parseDate("2014-03-31")) + .setDownloadUrl("http://repository.codehaus.org/org/codehaus/sonar-plugins/android/sonar-android-plugin/1.0/sonar-android-plugin-1.0.jar") + .addOutgoingDependency(release(JAVA_PLUGIN, "1.0")); + + private UpdatesPluginsWsAction underTest = new UpdatesPluginsWsAction(updateCenterFactory, + new PluginWSCommons(), new PluginUpdateAggregator() + ); @Test public void action_updatable_is_defined() throws Exception { @@ -58,14 +92,16 @@ public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPlugins } @Test - public void verify_properties_displayed_in_json_per_plugin() throws Exception { + public void verify_response_against_example() throws Exception { when(updateCenter.findPluginUpdates()).thenReturn(of( - pluginUpdate(FULL_PROPERTIES_PLUGIN_RELEASE, COMPATIBLE) + pluginUpdate(ABAP_32, COMPATIBLE), + pluginUpdate(ABAP_31, INCOMPATIBLE), + pluginUpdate(ANDROID_10, COMPATIBLE) )); underTest.handle(request, response); - assertJson(response.outputAsString()).isSimilarTo(resource("properties_per_plugin.json")); + assertJson(response.outputAsString()).isSimilarTo(getClass().getResource("example-updates_plugins.json")); } @Test @@ -79,10 +115,12 @@ public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPlugins assertJson(response.outputAsString()).isSimilarTo( "{" + " \"plugins\": [" + - " {" + - " \"update\": {" + - " \"status\": \"COMPATIBLE\"" + - " }" + + " {" + + " \"updates\": [" + + " {" + + " \"status\": \"COMPATIBLE\"" + + " }" + + " ]" + " }" + " ]" + "}" @@ -90,10 +128,9 @@ public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPlugins } @Test - public void plugins_are_sorted_by_name_then_key_and_made_unique() throws Exception { + public void plugins_are_sorted_by_name_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") @@ -106,19 +143,15 @@ public class UpdatesPluginsWsActionTest extends AbstractUpdateCenterBasedPlugins " \"plugins\": [" + " {" + " \"key\": \"key0\"," + - " \"name\": \"name0\"," + - " }," + - " {" + - " \"key\": \"key1\"," + - " \"name\": \"name1\"," + + " \"name\": \"name0\"" + " }," + " {" + " \"key\": \"key1\"," + - " \"name\": \"name2\"," + + " \"name\": \"name1\"" + " }," + " {" + " \"key\": \"key2\"," + - " \"name\": \"name2\"," + + " \"name\": \"name2\"" + " }," + " ]" + "}" |