@@ -27,7 +27,6 @@ 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.PluginWSCommons; | |||
import org.sonar.server.ui.VersionFormatter; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonar.updatecenter.common.Release; | |||
@@ -35,6 +34,7 @@ import org.sonar.updatecenter.common.SonarUpdate; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import static org.apache.commons.lang.StringUtils.isNotBlank; | |||
import static org.sonar.server.plugins.edition.EditionBundledPlugins.isEditionBundled; | |||
/** | |||
* Implementation of the {@code upgrades} action for the System WebService. | |||
@@ -56,6 +56,16 @@ public class UpgradesAction implements SystemWsAction { | |||
private static final String OBJECT_PLUGINS = "plugins"; | |||
private static final String ARRAY_REQUIRE_UPDATE = "requireUpdate"; | |||
private static final String ARRAY_INCOMPATIBLE = "incompatible"; | |||
private static final String PROPERTY_KEY = "key"; | |||
private static final String PROPERTY_NAME = "name"; | |||
private static final String PROPERTY_LICENSE = "license"; | |||
private static final String PROPERTY_CATEGORY = "category"; | |||
private static final String PROPERTY_ORGANIZATION_NAME = "organizationName"; | |||
private static final String PROPERTY_ORGANIZATION_URL = "organizationUrl"; | |||
private static final String PROPERTY_HOMEPAGE_URL = "homepageUrl"; | |||
private static final String PROPERTY_ISSUE_TRACKER_URL = "issueTrackerUrl"; | |||
private static final String PROPERTY_EDITION_BUNDLED = "editionBundled"; | |||
private static final String PROPERTY_TERMS_AND_CONDITIONS_URL = "termsAndConditionsUrl"; | |||
private final UpdateCenterMatrixFactory updateCenterFactory; | |||
@@ -144,7 +154,7 @@ public class UpgradesAction implements SystemWsAction { | |||
for (Release release : pluginsToUpgrade) { | |||
jsonWriter.beginObject(); | |||
PluginWSCommons.writePlugin(jsonWriter, (Plugin) release.getArtifact()); | |||
writePlugin(jsonWriter, (Plugin) release.getArtifact()); | |||
String version = isNotBlank(release.getDisplayVersion()) ? release.getDisplayVersion() : release.getVersion().toString(); | |||
jsonWriter.prop(PROPERTY_VERSION, version); | |||
@@ -159,10 +169,24 @@ public class UpgradesAction implements SystemWsAction { | |||
for (Plugin incompatiblePlugin : incompatiblePlugins) { | |||
jsonWriter.beginObject(); | |||
PluginWSCommons.writePlugin(jsonWriter, incompatiblePlugin); | |||
writePlugin(jsonWriter, incompatiblePlugin); | |||
jsonWriter.endObject(); | |||
} | |||
jsonWriter.endArray(); | |||
} | |||
public static void writePlugin(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()); | |||
jsonWriter.prop(PROPERTY_HOMEPAGE_URL, plugin.getHomepageUrl()); | |||
jsonWriter.prop(PROPERTY_ISSUE_TRACKER_URL, plugin.getIssueTrackerUrl()); | |||
jsonWriter.prop(PROPERTY_EDITION_BUNDLED, isEditionBundled(plugin)); | |||
} | |||
} |
@@ -19,25 +19,36 @@ | |||
*/ | |||
package org.sonar.server.plugins.ws; | |||
import com.google.common.io.Resources; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.stream.Collectors; | |||
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.user.UserSession; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonar.updatecenter.common.PluginUpdate; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonarqube.ws.Plugins.AvailablePlugin; | |||
import org.sonarqube.ws.Plugins.AvailablePluginsWsResponse; | |||
import org.sonarqube.ws.Plugins.AvailablePluginsWsResponse.Builder; | |||
import org.sonarqube.ws.Plugins.Update; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.server.plugins.edition.EditionBundledPlugins.isEditionBundled; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.NAME_KEY_PLUGIN_UPDATE_ORDERING; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildRelease; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildRequires; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.convertUpdateCenterStatus; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class AvailableAction implements PluginsWsAction { | |||
private static final boolean DO_NOT_FORCE_REFRESH = false; | |||
private static final String ARRAY_PLUGINS = "plugins"; | |||
private final UserSession userSession; | |||
private final UpdateCenterMatrixFactory updateCenterFactory; | |||
@@ -64,33 +75,49 @@ public class AvailableAction implements PluginsWsAction { | |||
"Require 'Administer System' permission.") | |||
.setSince("5.2") | |||
.setHandler(this) | |||
.setResponseExample(Resources.getResource(this.getClass(), "example-available_plugins.json")); | |||
.setResponseExample(this.getClass().getResource("example-available_plugins.json")); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) { | |||
userSession.checkIsSystemAdministrator(); | |||
JsonWriter jsonWriter = response.newJsonWriter(); | |||
jsonWriter.beginObject(); | |||
Optional<UpdateCenter> updateCenter = updateCenterFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH); | |||
Collection<AvailablePlugin> plugins = updateCenter.isPresent() ? getPlugins(updateCenter.get()) : Collections.emptyList(); | |||
Builder responseBuilder = AvailablePluginsWsResponse.newBuilder().addAllPlugins(plugins); | |||
updateCenter.ifPresent(u -> responseBuilder.setUpdateCenterRefresh(formatDateTime(u.getDate().getTime()))); | |||
writePlugins(jsonWriter, updateCenter); | |||
PluginWSCommons.writeUpdateCenterProperties(jsonWriter, updateCenter); | |||
writeProtobuf(responseBuilder.build(), request, response); | |||
} | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
private static List<AvailablePlugin> getPlugins(UpdateCenter updateCenter) { | |||
return retrieveAvailablePlugins(updateCenter) | |||
.stream() | |||
.map(pluginUpdate -> { | |||
Plugin plugin = pluginUpdate.getPlugin(); | |||
AvailablePlugin.Builder builder = AvailablePlugin.newBuilder() | |||
.setKey(plugin.getKey()) | |||
.setEditionBundled(isEditionBundled(plugin)) | |||
.setRelease(buildRelease(pluginUpdate.getRelease())) | |||
.setUpdate(buildUpdate(pluginUpdate)); | |||
ofNullable(plugin.getName()).ifPresent(builder::setName); | |||
ofNullable(plugin.getCategory()).ifPresent(builder::setCategory); | |||
ofNullable(plugin.getDescription()).ifPresent(builder::setDescription); | |||
ofNullable(plugin.getLicense()).ifPresent(builder::setLicense); | |||
ofNullable(plugin.getTermsConditionsUrl()).ifPresent(builder::setTermsAndConditionsUrl); | |||
ofNullable(plugin.getOrganization()).ifPresent(builder::setOrganizationName); | |||
ofNullable(plugin.getOrganizationUrl()).ifPresent(builder::setOrganizationUrl); | |||
ofNullable(plugin.getIssueTrackerUrl()).ifPresent(builder::setIssueTrackerUrl); | |||
ofNullable(plugin.getHomepageUrl()).ifPresent(builder::setHomepageUrl); | |||
return builder.build(); | |||
}).collect(Collectors.toList()); | |||
} | |||
private static void writePlugins(JsonWriter jsonWriter, Optional<UpdateCenter> updateCenter) { | |||
jsonWriter.name(ARRAY_PLUGINS).beginArray(); | |||
if (updateCenter.isPresent()) { | |||
for (PluginUpdate pluginUpdate : retrieveAvailablePlugins(updateCenter.get())) { | |||
PluginWSCommons.writePluginUpdate(jsonWriter, pluginUpdate); | |||
} | |||
} | |||
jsonWriter.endArray(); | |||
private static Update buildUpdate(PluginUpdate pluginUpdate) { | |||
return Update.newBuilder() | |||
.setStatus(convertUpdateCenterStatus(pluginUpdate.getStatus())) | |||
.addAllRequires(buildRequires(pluginUpdate)) | |||
.build(); | |||
} | |||
private static Collection<PluginUpdate> retrieveAvailablePlugins(UpdateCenter updateCenter) { |
@@ -20,6 +20,7 @@ | |||
package org.sonar.server.plugins.ws; | |||
import com.google.common.io.Resources; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
@@ -31,7 +32,7 @@ import org.sonar.api.server.ws.Change; | |||
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.core.platform.PluginInfo; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.plugin.PluginDto; | |||
@@ -41,6 +42,8 @@ import org.sonar.server.plugins.ServerPlugin; | |||
import org.sonar.server.plugins.ServerPluginRepository; | |||
import org.sonar.server.plugins.UpdateCenterMatrixFactory; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonarqube.ws.Plugins.InstalledPluginsWsResponse; | |||
import org.sonarqube.ws.Plugins.PluginDetails; | |||
import static com.google.common.collect.ImmutableSortedSet.copyOf; | |||
import static java.lang.String.format; | |||
@@ -48,14 +51,14 @@ import static java.util.Collections.emptyMap; | |||
import static java.util.Collections.singleton; | |||
import static java.util.stream.Collectors.toMap; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.NAME_KEY_COMPARATOR; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.categoryOrNull; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildPluginDetails; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.compatiblePluginsByKey; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
/** | |||
* Implementation of the {@code installed} action for the Plugins WebService. | |||
*/ | |||
public class InstalledAction implements PluginsWsAction { | |||
private static final String ARRAY_PLUGINS = "plugins"; | |||
private static final String FIELD_CATEGORY = "category"; | |||
private static final String PARAM_TYPE = "type"; | |||
@@ -107,24 +110,24 @@ public class InstalledAction implements PluginsWsAction { | |||
dtosByKey = dbClient.pluginDao().selectAll(dbSession).stream().collect(toMap(PluginDto::getKee, Function.identity())); | |||
} | |||
JsonWriter json = response.newJsonWriter(); | |||
json.setSerializeEmptys(false); | |||
json.beginObject(); | |||
List<String> additionalFields = request.paramAsStrings(WebService.Param.FIELDS); | |||
Map<String, Plugin> updateCenterPlugins = (additionalFields == null || additionalFields.isEmpty()) ? emptyMap() : compatiblePluginsByKey(updateCenterMatrixFactory); | |||
json.name(ARRAY_PLUGINS); | |||
json.beginArray(); | |||
List<PluginDetails> pluginList = new LinkedList<>(); | |||
for (ServerPlugin installedPlugin : installedPlugins) { | |||
PluginDto pluginDto = dtosByKey.get(installedPlugin.getPluginInfo().getKey()); | |||
Objects.requireNonNull(pluginDto, () -> format("Plugin %s is installed but not in DB", installedPlugin.getPluginInfo().getKey())); | |||
Plugin updateCenterPlugin = updateCenterPlugins.get(installedPlugin.getPluginInfo().getKey()); | |||
PluginWSCommons.writePluginInfo(json, installedPlugin.getPluginInfo(), categoryOrNull(updateCenterPlugin), pluginDto, installedPlugin); | |||
PluginInfo pluginInfo = installedPlugin.getPluginInfo(); | |||
PluginDto pluginDto = dtosByKey.get(pluginInfo.getKey()); | |||
Objects.requireNonNull(pluginDto, () -> format("Plugin %s is installed but not in DB", pluginInfo.getKey())); | |||
Plugin updateCenterPlugin = updateCenterPlugins.get(pluginInfo.getKey()); | |||
pluginList.add(buildPluginDetails(installedPlugin, pluginInfo, pluginDto, updateCenterPlugin)); | |||
} | |||
json.endArray(); | |||
json.endObject(); | |||
json.close(); | |||
InstalledPluginsWsResponse wsResponse = InstalledPluginsWsResponse.newBuilder() | |||
.addAllPlugins(pluginList) | |||
.build(); | |||
writeProtobuf(wsResponse, request, response); | |||
} | |||
private SortedSet<ServerPlugin> loadInstalledPlugins(@Nullable String typeParam) { |
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.ImmutableSortedSet; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.stream.Collectors; | |||
@@ -30,7 +31,6 @@ import org.sonar.api.server.ws.Change; | |||
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.core.platform.PluginInfo; | |||
import org.sonar.server.plugins.PluginDownloader; | |||
import org.sonar.server.plugins.PluginUninstaller; | |||
@@ -38,21 +38,18 @@ import org.sonar.server.plugins.ServerPluginRepository; | |||
import org.sonar.server.plugins.UpdateCenterMatrixFactory; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonarqube.ws.Plugins.PendingPluginsWsResponse; | |||
import org.sonarqube.ws.Plugins.PluginDetails; | |||
import static com.google.common.io.Resources.getResource; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.NAME_KEY_PLUGIN_METADATA_COMPARATOR; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.categoryOrNull; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildPluginDetails; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.compatiblePluginsByKey; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
/** | |||
* Implementation of the {@code pending} action for the Plugins WebService. | |||
*/ | |||
public class PendingAction implements PluginsWsAction { | |||
private static final String ARRAY_INSTALLING = "installing"; | |||
private static final String ARRAY_REMOVING = "removing"; | |||
private static final String ARRAY_UPDATING = "updating"; | |||
private final UserSession userSession; | |||
private final PluginDownloader pluginDownloader; | |||
private final ServerPluginRepository serverPluginRepository; | |||
@@ -76,24 +73,18 @@ public class PendingAction implements PluginsWsAction { | |||
.setSince("5.2") | |||
.setChangelog(new Change("8.0", "The 'documentationPath' field is added")) | |||
.setHandler(this) | |||
.setResponseExample(getResource(this.getClass(), "example-pending_plugins.json")); | |||
.setResponseExample(this.getClass().getResource("example-pending_plugins.json")); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkIsSystemAdministrator(); | |||
ImmutableMap<String, Plugin> compatiblePluginsByKey = compatiblePluginsByKey(updateCenterMatrixFactory); | |||
JsonWriter jsonWriter = response.newJsonWriter(); | |||
jsonWriter.beginObject(); | |||
writePlugins(jsonWriter, compatiblePluginsByKey); | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
PendingPluginsWsResponse.Builder buildResponse = buildResponse(compatiblePluginsByKey); | |||
writeProtobuf(buildResponse.build(), request, response); | |||
} | |||
private void writePlugins(JsonWriter json, Map<String, Plugin> compatiblePluginsByKey) { | |||
private PendingPluginsWsResponse.Builder buildResponse(ImmutableMap<String, Plugin> compatiblePluginsByKey) { | |||
Collection<PluginInfo> uninstalledPlugins = pluginUninstaller.getUninstalledPlugins(); | |||
Collection<PluginInfo> downloadedPlugins = pluginDownloader.getDownloadedPlugins(); | |||
Collection<PluginInfo> installedPlugins = serverPluginRepository.getPluginInfos(); | |||
@@ -109,18 +100,20 @@ public class PendingAction implements PluginsWsAction { | |||
} | |||
} | |||
writePlugin(json, ARRAY_INSTALLING, newPlugins, compatiblePluginsByKey); | |||
writePlugin(json, ARRAY_UPDATING, updatedPlugins, compatiblePluginsByKey); | |||
writePlugin(json, ARRAY_REMOVING, uninstalledPlugins, compatiblePluginsByKey); | |||
PendingPluginsWsResponse.Builder builder = PendingPluginsWsResponse.newBuilder(); | |||
builder.addAllInstalling(getPlugins(newPlugins, compatiblePluginsByKey)); | |||
builder.addAllUpdating(getPlugins(updatedPlugins, compatiblePluginsByKey)); | |||
builder.addAllRemoving(getPlugins(uninstalledPlugins, compatiblePluginsByKey)); | |||
return builder; | |||
} | |||
private static void writePlugin(JsonWriter json, String propertyName, Collection<PluginInfo> plugins, Map<String, Plugin> compatiblePluginsByKey) { | |||
json.name(propertyName); | |||
json.beginArray(); | |||
for (PluginInfo pluginInfo : ImmutableSortedSet.copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins)) { | |||
Plugin plugin = compatiblePluginsByKey.get(pluginInfo.getKey()); | |||
PluginWSCommons.writePluginInfo(json, pluginInfo, categoryOrNull(plugin), null, null); | |||
} | |||
json.endArray(); | |||
private static List<PluginDetails> getPlugins(Collection<PluginInfo> plugins, Map<String, Plugin> compatiblePluginsByKey) { | |||
return ImmutableSortedSet.copyOf(NAME_KEY_PLUGIN_METADATA_COMPARATOR, plugins) | |||
.stream() | |||
.map(pluginInfo -> { | |||
Plugin plugin = compatiblePluginsByKey.get(pluginInfo.getKey()); | |||
return buildPluginDetails(null, pluginInfo, null, plugin); | |||
}) | |||
.collect(Collectors.toList()); | |||
} | |||
} |
@@ -19,7 +19,6 @@ | |||
*/ | |||
package org.sonar.server.plugins.ws; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Ordering; | |||
@@ -27,53 +26,32 @@ import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import javax.annotation.CheckForNull; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.text.JsonWriter; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.db.plugin.PluginDto; | |||
import org.sonar.server.plugins.ServerPlugin; | |||
import org.sonar.server.plugins.UpdateCenterMatrixFactory; | |||
import org.sonar.server.plugins.edition.EditionBundledPlugins; | |||
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 org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonar.updatecenter.common.Version; | |||
import org.sonarqube.ws.Plugins.PluginDetails; | |||
import org.sonarqube.ws.Plugins.Release; | |||
import org.sonarqube.ws.Plugins.Require; | |||
import org.sonarqube.ws.Plugins.UpdateStatus; | |||
import static com.google.common.collect.Iterables.filter; | |||
import static com.google.common.collect.Iterables.transform; | |||
import static java.lang.String.CASE_INSENSITIVE_ORDER; | |||
import static java.util.Optional.ofNullable; | |||
import static org.apache.commons.lang.StringUtils.isNotBlank; | |||
import static org.sonar.api.utils.DateUtils.formatDate; | |||
import static org.sonar.server.plugins.edition.EditionBundledPlugins.isEditionBundled; | |||
import static org.sonarqube.ws.Plugins.UpdateStatus.COMPATIBLE; | |||
import static org.sonarqube.ws.Plugins.UpdateStatus.DEPS_REQUIRE_SYSTEM_UPGRADE; | |||
import static org.sonarqube.ws.Plugins.UpdateStatus.INCOMPATIBLE; | |||
import static org.sonarqube.ws.Plugins.UpdateStatus.REQUIRES_SYSTEM_UPGRADE; | |||
public class PluginWSCommons { | |||
private static final String PROPERTY_KEY = "key"; | |||
private static final String PROPERTY_NAME = "name"; | |||
private static final String PROPERTY_HASH = "hash"; | |||
private static final String PROPERTY_FILENAME = "filename"; | |||
private static final String PROPERTY_DOCUMENTATION_PATH = "documentationPath"; | |||
private static final String PROPERTY_SONARLINT_SUPPORTED = "sonarLintSupported"; | |||
private static final String PROPERTY_DESCRIPTION = "description"; | |||
private static final String PROPERTY_LICENSE = "license"; | |||
private static final String PROPERTY_VERSION = "version"; | |||
private static final String PROPERTY_CATEGORY = "category"; | |||
private static final String PROPERTY_ORGANIZATION_NAME = "organizationName"; | |||
private static final String PROPERTY_ORGANIZATION_URL = "organizationUrl"; | |||
private static final String PROPERTY_DATE = "date"; | |||
private static final String PROPERTY_UPDATED_AT = "updatedAt"; | |||
private static final String PROPERTY_STATUS = "status"; | |||
private static final String PROPERTY_HOMEPAGE_URL = "homepageUrl"; | |||
private static final String PROPERTY_ISSUE_TRACKER_URL = "issueTrackerUrl"; | |||
private static final String PROPERTY_EDITION_BUNDLED = "editionBundled"; | |||
private static final String PROPERTY_TERMS_AND_CONDITIONS_URL = "termsAndConditionsUrl"; | |||
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 PROPERTY_UPDATE_CENTER_REFRESH = "updateCenterRefresh"; | |||
private static final String PROPERTY_IMPLEMENTATION_BUILD = "implementationBuild"; | |||
private static final String PROPERTY_CHANGE_LOG_URL = "changeLogUrl"; | |||
public static final Ordering<PluginInfo> NAME_KEY_PLUGIN_METADATA_COMPARATOR = Ordering.natural() | |||
.onResultOf(PluginInfo::getName) | |||
@@ -92,156 +70,73 @@ public class PluginWSCommons { | |||
// prevent instantiation | |||
} | |||
public static void writePluginInfo(JsonWriter json, PluginInfo pluginInfo, @Nullable String category, @Nullable PluginDto pluginDto, @Nullable ServerPlugin installedFile) { | |||
json.beginObject(); | |||
json.prop(PROPERTY_KEY, pluginInfo.getKey()); | |||
json.prop(PROPERTY_NAME, pluginInfo.getName()); | |||
json.prop(PROPERTY_DESCRIPTION, pluginInfo.getDescription()); | |||
Version version = pluginInfo.getVersion(); | |||
if (version != null) { | |||
String functionalVersion = isNotBlank(pluginInfo.getDisplayVersion()) ? pluginInfo.getDisplayVersion() : version.getName(); | |||
json.prop(PROPERTY_VERSION, functionalVersion); | |||
} | |||
json.prop(PROPERTY_CATEGORY, category); | |||
json.prop(PROPERTY_LICENSE, pluginInfo.getLicense()); | |||
json.prop(PROPERTY_ORGANIZATION_NAME, pluginInfo.getOrganizationName()); | |||
json.prop(PROPERTY_ORGANIZATION_URL, pluginInfo.getOrganizationUrl()); | |||
json.prop(PROPERTY_EDITION_BUNDLED, EditionBundledPlugins.isEditionBundled(pluginInfo)); | |||
json.prop(PROPERTY_HOMEPAGE_URL, pluginInfo.getHomepageUrl()); | |||
json.prop(PROPERTY_ISSUE_TRACKER_URL, pluginInfo.getIssueTrackerUrl()); | |||
json.prop(PROPERTY_IMPLEMENTATION_BUILD, pluginInfo.getImplementationBuild()); | |||
json.prop(PROPERTY_DOCUMENTATION_PATH, pluginInfo.getDocumentationPath()); | |||
if (pluginDto != null) { | |||
json.prop(PROPERTY_UPDATED_AT, pluginDto.getUpdatedAt()); | |||
} | |||
if (installedFile != null) { | |||
json.prop(PROPERTY_FILENAME, installedFile.getJar().getFile().getName()); | |||
json.prop(PROPERTY_SONARLINT_SUPPORTED, installedFile.getPluginInfo().isSonarLintSupported()); | |||
json.prop(PROPERTY_HASH, installedFile.getJar().getMd5()); | |||
} | |||
json.endObject(); | |||
public static PluginDetails buildPluginDetails(@Nullable ServerPlugin installedPlugin, PluginInfo pluginInfo, | |||
@Nullable PluginDto pluginDto, @Nullable Plugin updateCenterPlugin) { | |||
PluginDetails.Builder builder = PluginDetails.newBuilder() | |||
.setKey(pluginInfo.getKey()) | |||
.setName(pluginInfo.getName()) | |||
.setEditionBundled(isEditionBundled(pluginInfo)) | |||
.setSonarLintSupported(pluginInfo.isSonarLintSupported()); | |||
ofNullable(installedPlugin).ifPresent(serverPlugin -> { | |||
builder.setFilename(installedPlugin.getJar().getFile().getName()); | |||
builder.setHash(installedPlugin.getJar().getMd5()); | |||
}); | |||
ofNullable(pluginInfo.getVersion()).ifPresent(v -> builder.setVersion(isNotBlank(pluginInfo.getDisplayVersion()) ? pluginInfo.getDisplayVersion() : v.getName())); | |||
ofNullable(updateCenterPlugin).flatMap(p -> ofNullable(p.getCategory())).ifPresent(builder::setCategory); | |||
ofNullable(pluginDto).ifPresent(p -> builder.setUpdatedAt(p.getUpdatedAt())); | |||
ofNullable(pluginInfo.getDescription()).ifPresent(builder::setDescription); | |||
ofNullable(pluginInfo.getLicense()).ifPresent(builder::setLicense); | |||
ofNullable(pluginInfo.getOrganizationName()).ifPresent(builder::setOrganizationName); | |||
ofNullable(pluginInfo.getOrganizationUrl()).ifPresent(builder::setOrganizationUrl); | |||
ofNullable(pluginInfo.getHomepageUrl()).ifPresent(builder::setHomepageUrl); | |||
ofNullable(pluginInfo.getIssueTrackerUrl()).ifPresent(builder::setIssueTrackerUrl); | |||
ofNullable(pluginInfo.getImplementationBuild()).ifPresent(builder::setImplementationBuild); | |||
ofNullable(pluginInfo.getDocumentationPath()).ifPresent(builder::setDocumentationPath); | |||
return builder.build(); | |||
} | |||
public static void writePlugin(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()); | |||
jsonWriter.prop(PROPERTY_HOMEPAGE_URL, plugin.getHomepageUrl()); | |||
jsonWriter.prop(PROPERTY_ISSUE_TRACKER_URL, plugin.getIssueTrackerUrl()); | |||
jsonWriter.prop(PROPERTY_EDITION_BUNDLED, isEditionBundled(plugin)); | |||
} | |||
public static void writePluginUpdate(JsonWriter json, PluginUpdate pluginUpdate) { | |||
Plugin plugin = pluginUpdate.getPlugin(); | |||
json.beginObject(); | |||
writePlugin(json, plugin); | |||
writeRelease(json, pluginUpdate.getRelease()); | |||
writeUpdate(json, pluginUpdate); | |||
json.endObject(); | |||
} | |||
public static void writeRelease(JsonWriter jsonWriter, Release release) { | |||
jsonWriter.name(OBJECT_RELEASE).beginObject(); | |||
static Release buildRelease(org.sonar.updatecenter.common.Release release) { | |||
String version = isNotBlank(release.getDisplayVersion()) ? release.getDisplayVersion() : release.getVersion().toString(); | |||
jsonWriter.prop(PROPERTY_VERSION, version); | |||
jsonWriter.propDate(PROPERTY_DATE, release.getDate()); | |||
jsonWriter.prop(PROPERTY_DESCRIPTION, release.getDescription()); | |||
jsonWriter.prop(PROPERTY_CHANGE_LOG_URL, release.getChangelogUrl()); | |||
jsonWriter.endObject(); | |||
Release.Builder releaseBuilder = Release.newBuilder().setVersion(version); | |||
ofNullable(release.getDate()).ifPresent(date -> releaseBuilder.setDate(formatDate(date))); | |||
ofNullable(release.getChangelogUrl()).ifPresent(releaseBuilder::setChangeLogUrl); | |||
ofNullable(release.getDescription()).ifPresent(releaseBuilder::setDescription); | |||
return releaseBuilder.build(); | |||
} | |||
/** | |||
* Write an "update" object to the specified jsonwriter. | |||
* <pre> | |||
* "update": { | |||
* "status": "COMPATIBLE", | |||
* "requires": [ | |||
* { | |||
* "key": "java", | |||
* "name": "Java", | |||
* "description": "SonarQube rule engine." | |||
* } | |||
* ] | |||
* } | |||
* </pre> | |||
*/ | |||
static void writeUpdate(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { | |||
jsonWriter.name(OBJECT_UPDATE).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 static void writeUpdateProperties(JsonWriter jsonWriter, PluginUpdate pluginUpdate) { | |||
jsonWriter.prop(PROPERTY_STATUS, toJSon(pluginUpdate.getStatus())); | |||
jsonWriter.name(ARRAY_REQUIRES).beginArray(); | |||
Release release = pluginUpdate.getRelease(); | |||
for (Plugin child : filter(transform(release.getOutgoingDependencies(), Release::getArtifact), 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(); | |||
static List<Require> buildRequires(PluginUpdate pluginUpdate) { | |||
return pluginUpdate.getRelease().getOutgoingDependencies().stream().map( | |||
org.sonar.updatecenter.common.Release::getArtifact) | |||
.filter(release -> release instanceof Plugin) | |||
.map(artifact -> (Plugin) artifact) | |||
.map(artifact -> { | |||
Require.Builder builder = Require.newBuilder() | |||
.setKey(artifact.getKey()); | |||
ofNullable(artifact.getName()).ifPresent(builder::setName); | |||
ofNullable(artifact.getDescription()).ifPresent(builder::setDescription); | |||
return builder.build(); | |||
}) | |||
.collect(Collectors.toList()); | |||
} | |||
@VisibleForTesting | |||
static String toJSon(PluginUpdate.Status status) { | |||
static UpdateStatus convertUpdateCenterStatus(PluginUpdate.Status status) { | |||
switch (status) { | |||
case COMPATIBLE: | |||
return "COMPATIBLE"; | |||
return COMPATIBLE; | |||
case INCOMPATIBLE: | |||
return "INCOMPATIBLE"; | |||
return INCOMPATIBLE; | |||
case REQUIRE_SONAR_UPGRADE: | |||
return "REQUIRES_SYSTEM_UPGRADE"; | |||
return REQUIRES_SYSTEM_UPGRADE; | |||
case DEPENDENCIES_REQUIRE_SONAR_UPGRADE: | |||
return "DEPS_REQUIRE_SYSTEM_UPGRADE"; | |||
return DEPS_REQUIRE_SYSTEM_UPGRADE; | |||
default: | |||
throw new IllegalArgumentException("Unsupported value of PluginUpdate.Status " + status); | |||
} | |||
} | |||
/** | |||
* Write properties of the specified UpdateCenter to the specified JsonWriter. | |||
* <pre> | |||
* "updateCenterRefresh": "2015-04-24T16:08:36+0200" | |||
* </pre> | |||
*/ | |||
public static void writeUpdateCenterProperties(JsonWriter json, Optional<UpdateCenter> updateCenter) { | |||
if (updateCenter.isPresent()) { | |||
json.propDateTime(PROPERTY_UPDATE_CENTER_REFRESH, updateCenter.get().getDate()); | |||
} | |||
} | |||
@CheckForNull | |||
static String categoryOrNull(@Nullable Plugin plugin) { | |||
return plugin != null ? plugin.getCategory() : null; | |||
} | |||
private static List<Plugin> compatiblePlugins(UpdateCenterMatrixFactory updateCenterMatrixFactory) { | |||
Optional<UpdateCenter> updateCenter = updateCenterMatrixFactory.getUpdateCenter(false); | |||
return updateCenter.isPresent() ? updateCenter.get().findAllCompatiblePlugins() : Collections.emptyList(); |
@@ -21,20 +21,33 @@ package org.sonar.server.plugins.ws; | |||
import com.google.common.collect.ImmutableSortedSet; | |||
import com.google.common.collect.Ordering; | |||
import com.google.common.io.Resources; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.stream.Collectors; | |||
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.server.user.UserSession; | |||
import org.sonar.updatecenter.common.Plugin; | |||
import org.sonar.updatecenter.common.PluginUpdate; | |||
import org.sonar.updatecenter.common.UpdateCenter; | |||
import org.sonarqube.ws.Plugins.AvailableUpdate; | |||
import org.sonarqube.ws.Plugins.UpdatablePlugin; | |||
import org.sonarqube.ws.Plugins.UpdatesPluginsWsResponse; | |||
import org.sonarqube.ws.Plugins.UpdatesPluginsWsResponse.Builder; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.server.plugins.edition.EditionBundledPlugins.isEditionBundled; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildRelease; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.buildRequires; | |||
import static org.sonar.server.plugins.ws.PluginWSCommons.convertUpdateCenterStatus; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
/** | |||
* Implementation of the {@code updates} action for the Plugins WebService. | |||
@@ -42,13 +55,12 @@ import org.sonar.updatecenter.common.UpdateCenter; | |||
public class UpdatesAction 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 String HTML_TAG_BR = "<br/>"; | |||
private static final Ordering<PluginUpdateAggregate> NAME_KEY_PLUGIN_UPGRADE_AGGREGATE_ORDERING = Ordering.from(PluginWSCommons.NAME_KEY_PLUGIN_ORDERING) | |||
.onResultOf(PluginUpdateAggregate::getPlugin); | |||
private static final Ordering<PluginUpdate> PLUGIN_UPDATE_BY_VERSION_ORDERING = Ordering.natural() | |||
.onResultOf(input -> input.getRelease().getVersion().toString()); | |||
.onResultOf(input -> Objects.requireNonNull(input).getRelease().getVersion().toString()); | |||
private final UserSession userSession; | |||
private final UpdateCenterMatrixFactory updateCenterMatrixFactory; | |||
@@ -65,66 +77,62 @@ public class UpdatesAction implements PluginsWsAction { | |||
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/>" + | |||
HTML_TAG_BR + | |||
"Each newer version is listed, ordered from the oldest to the newest, with its own update/compatibility status." + | |||
"<br/>" + | |||
HTML_TAG_BR + | |||
"Plugin information is retrieved from Update Center. Date and time at which Update Center was last refreshed is provided in the response." + | |||
"<br/>" + | |||
"Update status values are: [COMPATIBLE, INCOMPATIBLE, REQUIRES_UPGRADE, DEPS_REQUIRE_UPGRADE].<br/>" + | |||
HTML_TAG_BR + | |||
"Update status values are: [COMPATIBLE, INCOMPATIBLE, REQUIRES_UPGRADE, DEPS_REQUIRE_UPGRADE]." + | |||
HTML_TAG_BR + | |||
"Require 'Administer System' permission.") | |||
.setSince("5.2") | |||
.setHandler(this) | |||
.setResponseExample(Resources.getResource(this.getClass(), "example-updates_plugins.json")); | |||
.setResponseExample(this.getClass().getResource("example-updates_plugins.json")); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkIsSystemAdministrator(); | |||
JsonWriter jsonWriter = response.newJsonWriter(); | |||
jsonWriter.beginObject(); | |||
Optional<UpdateCenter> updateCenter = updateCenterMatrixFactory.getUpdateCenter(DO_NOT_FORCE_REFRESH); | |||
writePlugins(jsonWriter, updateCenter); | |||
PluginWSCommons.writeUpdateCenterProperties(jsonWriter, updateCenter); | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
} | |||
Collection<UpdatablePlugin> plugins = updateCenter.isPresent() ? getPluginUpdates(updateCenter.get()) : Collections.emptyList(); | |||
Builder responseBuilder = UpdatesPluginsWsResponse.newBuilder().addAllPlugins(plugins); | |||
updateCenter.ifPresent(u -> responseBuilder.setUpdateCenterRefresh(formatDateTime(u.getDate().getTime()))); | |||
private void writePlugins(JsonWriter jsonWriter, Optional<UpdateCenter> updateCenter) { | |||
jsonWriter.name(ARRAY_PLUGINS); | |||
jsonWriter.beginArray(); | |||
if (updateCenter.isPresent()) { | |||
for (PluginUpdateAggregate aggregate : retrieveUpdatablePlugins(updateCenter.get())) { | |||
writePluginUpdateAggregate(jsonWriter, aggregate); | |||
} | |||
} | |||
jsonWriter.endArray(); | |||
writeProtobuf(responseBuilder.build(), request, response); | |||
} | |||
private static void writePluginUpdateAggregate(JsonWriter jsonWriter, PluginUpdateAggregate aggregate) { | |||
jsonWriter.beginObject(); | |||
Plugin plugin = aggregate.getPlugin(); | |||
PluginWSCommons.writePlugin(jsonWriter, plugin); | |||
writeUpdates(jsonWriter, aggregate.getUpdates()); | |||
jsonWriter.endObject(); | |||
private List<UpdatablePlugin> getPluginUpdates(UpdateCenter updateCenter) { | |||
return retrieveUpdatablePlugins(updateCenter) | |||
.stream() | |||
.map(pluginUpdateAggregate -> { | |||
Plugin plugin = pluginUpdateAggregate.getPlugin(); | |||
UpdatablePlugin.Builder builder = UpdatablePlugin.newBuilder() | |||
.setKey(plugin.getKey()) | |||
.setEditionBundled(isEditionBundled(plugin)) | |||
.addAllUpdates(buildUpdates(pluginUpdateAggregate)); | |||
ofNullable(plugin.getName()).ifPresent(builder::setName); | |||
ofNullable(plugin.getCategory()).ifPresent(builder::setCategory); | |||
ofNullable(plugin.getDescription()).ifPresent(builder::setDescription); | |||
ofNullable(plugin.getLicense()).ifPresent(builder::setLicense); | |||
ofNullable(plugin.getTermsConditionsUrl()).ifPresent(builder::setTermsAndConditionsUrl); | |||
ofNullable(plugin.getOrganization()).ifPresent(builder::setOrganizationName); | |||
ofNullable(plugin.getOrganizationUrl()).ifPresent(builder::setOrganizationUrl); | |||
ofNullable(plugin.getIssueTrackerUrl()).ifPresent(builder::setIssueTrackerUrl); | |||
ofNullable(plugin.getHomepageUrl()).ifPresent(builder::setHomepageUrl); | |||
return builder.build(); | |||
}).collect(Collectors.toList()); | |||
} | |||
private static 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 static Collection<AvailableUpdate> buildUpdates(PluginUpdateAggregate pluginUpdateAggregate) { | |||
return ImmutableSortedSet.copyOf(PLUGIN_UPDATE_BY_VERSION_ORDERING, pluginUpdateAggregate.getUpdates()).stream() | |||
.map(pluginUpdate -> AvailableUpdate.newBuilder() | |||
.setRelease(buildRelease(pluginUpdate.getRelease())) | |||
.setStatus(convertUpdateCenterStatus(pluginUpdate.getStatus())) | |||
.addAllRequires(buildRequires(pluginUpdate)) | |||
.build()) | |||
.collect(Collectors.toList()); | |||
} | |||
private Collection<PluginUpdateAggregate> retrieveUpdatablePlugins(UpdateCenter updateCenter) { |
@@ -177,14 +177,14 @@ public class InstalledActionTest { | |||
public void empty_fields_are_not_serialized_to_json() throws IOException { | |||
when(serverPluginRepository.getPlugins()).thenReturn( | |||
singletonList(newInstalledPlugin(new PluginInfo("foo") | |||
.setName("") | |||
.setDescription("") | |||
.setLicense("") | |||
.setOrganizationName("") | |||
.setOrganizationUrl("") | |||
.setImplementationBuild("") | |||
.setHomepageUrl("") | |||
.setIssueTrackerUrl("")))); | |||
.setName(null) | |||
.setDescription(null) | |||
.setLicense(null) | |||
.setOrganizationName(null) | |||
.setOrganizationUrl(null) | |||
.setImplementationBuild(null) | |||
.setHomepageUrl(null) | |||
.setIssueTrackerUrl(null)))); | |||
db.pluginDbTester().insertPlugin( | |||
p -> p.setKee("foo"), | |||
p -> p.setType(Type.EXTERNAL), | |||
@@ -193,7 +193,7 @@ public class InstalledActionTest { | |||
String response = tester.newRequest().execute().getInput(); | |||
JsonObject json = Json.parse(response).asObject().get("plugins").asArray().get(0).asObject(); | |||
assertThat(json.get("key")).isNotNull(); | |||
assertThat(json.get("name")).isNull(); | |||
assertThat(json.get("name")).isNotNull(); | |||
assertThat(json.get("description")).isNull(); | |||
assertThat(json.get("license")).isNull(); | |||
assertThat(json.get("organizationName")).isNull(); |
@@ -1,282 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 SonarSource SA | |||
* mailto:info AT sonarsource DOT com | |||
* | |||
* This program 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. | |||
* | |||
* This program 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 java.io.File; | |||
import java.io.IOException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.TemporaryFolder; | |||
import org.sonar.api.utils.text.JsonWriter; | |||
import org.sonar.core.platform.PluginInfo; | |||
import org.sonar.db.plugin.PluginDto; | |||
import org.sonar.server.plugins.PluginFilesAndMd5.FileAndMd5; | |||
import org.sonar.server.plugins.ServerPlugin; | |||
import org.sonar.server.ws.DumbResponse; | |||
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 org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.utils.DateUtils.parseDate; | |||
import static org.sonar.server.plugins.PluginType.BUNDLED; | |||
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 { | |||
@Rule | |||
public TemporaryFolder temp = new TemporaryFolder(); | |||
private DumbResponse response = new DumbResponse(); | |||
private JsonWriter jsonWriter = response.newJsonWriter(); | |||
@Test | |||
public void verify_properties_written_by_writePluginMetadata() { | |||
PluginWSCommons.writePluginInfo(jsonWriter, gitPluginInfo(), null, null, null); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).withStrictArrayOrder().isSimilarTo("{" + | |||
" \"key\": \"scmgit\"," + | |||
" \"name\": \"Git\"," + | |||
" \"description\": \"Git SCM Provider.\"," + | |||
" \"version\": \"1.0\"," + | |||
" \"license\": \"GNU LGPL 3\"," + | |||
" \"organizationName\": \"SonarSource\"," + | |||
" \"organizationUrl\": \"http://www.sonarsource.com\"," + | |||
" \"homepageUrl\": \"https://redirect.sonarsource.com/plugins/scmgit.html\"," + | |||
" \"issueTrackerUrl\": \"http://jira.sonarsource.com/browse/SONARSCGIT\"" + | |||
"}"); | |||
} | |||
@Test | |||
public void verify_properties_written_by_writePluginMetadata_with_dto() { | |||
PluginDto pluginDto = new PluginDto().setUpdatedAt(123456L); | |||
PluginWSCommons.writePluginInfo(jsonWriter, gitPluginInfo(), null, pluginDto, null); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).withStrictArrayOrder().isSimilarTo("{" + | |||
" \"key\": \"scmgit\"," + | |||
" \"name\": \"Git\"," + | |||
" \"description\": \"Git SCM Provider.\"," + | |||
" \"version\": \"1.0\"," + | |||
" \"license\": \"GNU LGPL 3\"," + | |||
" \"organizationName\": \"SonarSource\"," + | |||
" \"organizationUrl\": \"http://www.sonarsource.com\"," + | |||
" \"homepageUrl\": \"https://redirect.sonarsource.com/plugins/scmgit.html\"," + | |||
" \"issueTrackerUrl\": \"http://jira.sonarsource.com/browse/SONARSCGIT\"," + | |||
" \"updatedAt\": 123456" + | |||
"}"); | |||
} | |||
@Test | |||
public void verify_properties_written_by_writeMetadata_with_compressed_plugin() throws IOException { | |||
PluginDto dto = new PluginDto().setUpdatedAt(100L); | |||
FileAndMd5 loadedJar = new FileAndMd5(temp.newFile()); | |||
FileAndMd5 compressedJar = new FileAndMd5(temp.newFile()); | |||
ServerPlugin serverPlugin = new ServerPlugin(gitPluginInfo(), BUNDLED, null, loadedJar, compressedJar, null); | |||
PluginWSCommons.writePluginInfo(jsonWriter, gitPluginInfo(), null, dto, serverPlugin); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).withStrictArrayOrder().isSimilarTo("{" + | |||
" \"key\": \"scmgit\"," + | |||
" \"name\": \"Git\"," + | |||
" \"description\": \"Git SCM Provider.\"," + | |||
" \"version\": \"1.0\"," + | |||
" \"license\": \"GNU LGPL 3\"," + | |||
" \"organizationName\": \"SonarSource\"," + | |||
" \"organizationUrl\": \"http://www.sonarsource.com\"," + | |||
" \"homepageUrl\": \"https://redirect.sonarsource.com/plugins/scmgit.html\"," + | |||
" \"issueTrackerUrl\": \"http://jira.sonarsource.com/browse/SONARSCGIT\"," + | |||
" \"sonarLintSupported\": true," + | |||
" \"updatedAt\": 100," + | |||
" \"filename\": \"" + loadedJar.getFile().getName() + "\"," + | |||
" \"hash\": \"" + loadedJar.getMd5() + "\"" + | |||
"}"); | |||
} | |||
@Test | |||
public void verify_properties_written_by_writeMetadata() { | |||
PluginWSCommons.writePluginInfo(jsonWriter, gitPluginInfo(), "cat_1", null, null); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).withStrictArrayOrder().isSimilarTo("{" + | |||
" \"key\": \"scmgit\"," + | |||
" \"name\": \"Git\"," + | |||
" \"description\": \"Git SCM Provider.\"," + | |||
" \"version\": \"1.0\"," + | |||
" \"license\": \"GNU LGPL 3\"," + | |||
" \"category\":\"cat_1\"" + | |||
" \"organizationName\": \"SonarSource\"," + | |||
" \"organizationUrl\": \"http://www.sonarsource.com\"," + | |||
" \"homepageUrl\": \"https://redirect.sonarsource.com/plugins/scmgit.html\"," + | |||
" \"issueTrackerUrl\": \"http://jira.sonarsource.com/browse/SONARSCGIT\"," + | |||
"}"); | |||
} | |||
@Test | |||
public void verify_properties_written_by_writePluginUpdate() { | |||
PluginWSCommons.writePluginUpdate(jsonWriter, PluginUpdate.createForPluginRelease(newRelease(), version("1.0"))); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).isSimilarTo("{" + | |||
" \"key\": \"pkey\"," + | |||
" \"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\"" + | |||
" }" + | |||
"}"); | |||
} | |||
@Test | |||
public void verify_properties_written_by_writeMetadata_from_plugin() { | |||
jsonWriter.beginObject(); | |||
PluginWSCommons.writePlugin(jsonWriter, newPlugin()); | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).isSimilarTo("{" + | |||
" \"key\": \"pkey\"," + | |||
" \"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() { | |||
jsonWriter.beginObject(); | |||
PluginWSCommons.writeRelease(jsonWriter, newRelease().setDisplayVersion("1.0 (build 42)")); | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).withStrictArrayOrder().isSimilarTo("{" + | |||
" \"release\": {" + | |||
" \"version\": \"1.0 (build 42)\"," + | |||
" \"date\": \"2015-04-16\"," + | |||
" \"description\": \"release description\"," + | |||
" \"changeLogUrl\": \"http://change.org/plugin\"" + | |||
" }" + | |||
"}"); | |||
} | |||
@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_SYSTEM_UPGRADE"); | |||
} | |||
@Test | |||
public void status_DEPENDENCIES_REQUIRE_SONAR_UPGRADE_is_displayed_DEPS_REQUIRE_SYSTEM_UPGRADE_in_JSON() { | |||
assertThat(toJSon(DEPENDENCIES_REQUIRE_SONAR_UPGRADE)).isEqualTo("DEPS_REQUIRE_SYSTEM_UPGRADE"); | |||
} | |||
@Test | |||
public void writeUpdate_renders_key_name_and_description_of_requirements() { | |||
PluginUpdate pluginUpdate = new PluginUpdate(); | |||
pluginUpdate.setRelease( | |||
new Release(newPlugin(), version("1.0")).addOutgoingDependency(newRelease())); | |||
jsonWriter.beginObject(); | |||
PluginWSCommons.writeUpdate(jsonWriter, pluginUpdate); | |||
jsonWriter.endObject(); | |||
jsonWriter.close(); | |||
assertJson(response.outputAsString()).isSimilarTo("{" + | |||
" \"update\": {" + | |||
" \"requires\": [" + | |||
" {" + | |||
" \"key\": \"pkey\"," + | |||
" \"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(Plugin.factory(key), version("1.0")); | |||
} | |||
private PluginInfo gitPluginInfo() { | |||
return new PluginInfo("scmgit") | |||
.setName("Git") | |||
.setDescription("Git SCM Provider.") | |||
.setVersion(Version.create("1.0")) | |||
.setLicense("GNU LGPL 3") | |||
.setOrganizationName("SonarSource") | |||
.setOrganizationUrl("http://www.sonarsource.com") | |||
.setHomepageUrl("https://redirect.sonarsource.com/plugins/scmgit.html") | |||
.setIssueTrackerUrl("http://jira.sonarsource.com/browse/SONARSCGIT") | |||
.setSonarLintSupported(true) | |||
.setJarFile(new File("sonar-scm-git-plugin-1.0.jar")); | |||
} | |||
private Plugin newPlugin() { | |||
return Plugin.factory("pkey") | |||
.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 Release newRelease() { | |||
return new Release(newPlugin(), version("1.0")).setDate(parseDate("2015-04-16")) | |||
.setDownloadUrl("http://toto.com/file.jar") | |||
.setDescription("release description") | |||
.setChangelogUrl("http://change.org/plugin"); | |||
} | |||
} |
@@ -0,0 +1,135 @@ | |||
// SonarQube, open source software quality management tool. | |||
// Copyright (C) 2008-2016 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. | |||
syntax = "proto2"; | |||
package sonarqube.ws.plugins; | |||
import "ws-commons.proto"; | |||
option java_package = "org.sonarqube.ws"; | |||
option java_outer_classname = "Plugins"; | |||
option optimize_for = SPEED; | |||
// WS api/plugins/available | |||
message AvailablePluginsWsResponse { | |||
repeated AvailablePlugin plugins = 1; | |||
optional string updateCenterRefresh = 2; | |||
} | |||
message AvailablePlugin { | |||
optional string key = 1; | |||
optional string name = 2; | |||
optional string category = 3; | |||
optional string description = 4; | |||
optional string license = 5; | |||
optional string termsAndConditionsUrl = 6; | |||
optional string organizationName = 7; | |||
optional string organizationUrl = 8; | |||
optional string issueTrackerUrl = 9; | |||
optional string homepageUrl = 10; | |||
optional bool editionBundled = 11; | |||
optional Release release = 12; | |||
optional Update update = 13; | |||
} | |||
message Release { | |||
optional string version = 1; | |||
optional string date = 2; | |||
optional string description = 3; | |||
optional string changeLogUrl = 4; | |||
} | |||
message Update { | |||
optional UpdateStatus status = 1; | |||
repeated Require requires = 2; | |||
} | |||
enum UpdateStatus { | |||
COMPATIBLE = 0; | |||
INCOMPATIBLE = 1; | |||
REQUIRES_SYSTEM_UPGRADE = 2; | |||
DEPS_REQUIRE_SYSTEM_UPGRADE = 3; | |||
} | |||
message Require { | |||
optional string key = 1; | |||
optional string name = 2; | |||
optional string description = 3; | |||
} | |||
// WS api/plugins/updates | |||
message UpdatesPluginsWsResponse { | |||
repeated UpdatablePlugin plugins = 1; | |||
optional string updateCenterRefresh = 2; | |||
} | |||
message UpdatablePlugin { | |||
optional string key = 1; | |||
optional string name = 2; | |||
optional string category = 3; | |||
optional string description = 4; | |||
optional string license = 5; | |||
optional string termsAndConditionsUrl = 6; | |||
optional string organizationName = 7; | |||
optional string organizationUrl = 8; | |||
optional string issueTrackerUrl = 9; | |||
optional string homepageUrl = 10; | |||
optional bool editionBundled = 11; | |||
repeated AvailableUpdate updates = 12; | |||
} | |||
message AvailableUpdate { | |||
optional Release release = 1; | |||
optional UpdateStatus status = 2; | |||
repeated Require requires = 3; | |||
} | |||
// WS api/plugins/installed | |||
message InstalledPluginsWsResponse { | |||
repeated PluginDetails plugins = 1; | |||
} | |||
message PluginDetails { | |||
optional string key = 1; | |||
optional string name = 2; | |||
optional string description = 3; | |||
optional string category = 4; | |||
optional string version = 5; | |||
optional string license = 6; | |||
optional string organizationName = 7; | |||
optional string organizationUrl = 8; | |||
optional bool editionBundled = 9; | |||
optional string homepageUrl = 10; | |||
optional string issueTrackerUrl = 11; | |||
optional string implementationBuild = 12; | |||
optional string filename = 13; | |||
optional string hash = 14; | |||
optional bool sonarLintSupported = 15; | |||
optional string documentationPath = 16; | |||
optional int64 updatedAt = 17; | |||
} | |||
// WS api/plugins/pending | |||
message PendingPluginsWsResponse { | |||
repeated PluginDetails installing = 1; | |||
repeated PluginDetails updating = 2; | |||
repeated PluginDetails removing = 3; | |||
} | |||