]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6379 add Java WS to list plugins updates
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Fri, 17 Apr 2015 13:54:26 +0000 (15:54 +0200)
committerSébastien Lesaint <sebastien.lesaint@sonarsource.com>
Thu, 23 Apr 2015 08:01:26 +0000 (10:01 +0200)
factor code from AvailablePluginsWsAction and UpdatablePluginsWsAction into PluginWSCommons because there are extremely similar

server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/AvailablePluginsWsAction.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java
server/sonar-server/src/main/java/org/sonar/server/plugins/ws/UpdatesPluginsWsAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/plugins/ws/example-updates_plugins.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AbstractUpdateCenterBasedPluginsWsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/AvailablePluginsWsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java
server/sonar-server/src/test/java/org/sonar/server/plugins/ws/UpdatesPluginsWsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/plugins/ws/UpdatablePluginsWsActionTest/properties_per_plugin.json [new file with mode: 0644]

index b338e2e650223dd8395e9565641bbd3392fa2b77..08be0f8b8b562977be8f7ff2aa85cd7c01d20806 100644 (file)
@@ -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);
index 33b8c0c43eb146103ddf19e25dce996e60c591fb..e16955a2b07950868cd08d04cc3ed0b4265895a9 100644 (file)
  */
 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<PluginUpdate> 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<Release, Artifact> {
-    INSTANCE;
-
-    @Override
-    public Artifact apply(@Nullable Release input) {
-      if (input == null) {
-        return null;
-      }
-      return input.getArtifact();
-    }
-  }
 }
index 176f369e7270a05aa4d497caf084cb154742cdc0..9bb852a5ac1b5411c312c36e790d6ef3c37ceaf7 100644 (file)
  */
 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<PluginMetadata> 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<Release, Artifact> {
+    INSTANCE;
+
+    @Override
+    public Artifact apply(@Nullable Release input) {
+      if (input == null) {
+        return null;
+      }
+      return input.getArtifact();
+    }
+  }
+
   private enum PluginMetadataToName implements Function<PluginMetadata, String> {
     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 (file)
index 0000000..9a9362f
--- /dev/null
@@ -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<PluginUpdate> 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." +
+        "<br/>" +
+        "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<PluginUpdate> retrieveUpdatablePlugins() {
+    return ImmutableSortedSet.copyOf(
+      NAME_KEY_PLUGIN_UPDATE_ORDERING,
+      updateCenterMatrixFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH).findPluginUpdates()
+      );
+  }
+
+  private enum PluginUpdateToKey implements Function<PluginUpdate, String> {
+    INSTANCE;
+
+    @Override
+    public String apply(@Nullable PluginUpdate input) {
+      if (input == null) {
+        return null;
+      }
+      return input.getPlugin().getKey();
+    }
+  }
+
+  private enum PluginUpdateToName implements Function<PluginUpdate, String> {
+    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 (file)
index 0000000..2cfc80c
--- /dev/null
@@ -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 (file)
index 0000000..204a15c
--- /dev/null
@@ -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<AvailablePluginsWsActionTest> clazz = AvailablePluginsWsActionTest.class;
+    return clazz.getResource(clazz.getSimpleName() + "/" + s);
+  }
+
+  @Before
+  public void wireMocksTogether() throws Exception {
+    when(updateCenterFactory.getUpdateCenter(anyBoolean())).thenReturn(updateCenter);
+  }
+}
index 954653a571e7d8bfe3871285344ef0ae71d91924..fcf1389e1cc223036b9e476acc57d628264238a6 100644 (file)
  */
 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<AvailablePluginsWsActionTest> clazz = AvailablePluginsWsActionTest.class;
-    return clazz.getResource(clazz.getSimpleName() + "/" + s);
-  }
 }
index b4b8a31f351f8690bf66ca533aaa2baaa96fbc2e..2c77cf638bed922b217ecab05537c2431b5eac93 100644 (file)
@@ -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 (file)
index 0000000..81f0e86
--- /dev/null
@@ -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 (file)
index 0000000..055feb3
--- /dev/null
@@ -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"
+          }
+        ]
+      }
+    }
+  ]
+}