aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginUpdateAggregator.java119
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java82
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java68
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json72
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java20
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java16
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregateBuilderTest.java58
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginUpdateAggregatorTest.java86
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java69
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\"" +
" }," +
" ]" +
"}"