Browse Source

Use protobuf instead JsonWriter for plugins apis

tags/8.5.0.37579
Jacek 3 years ago
parent
commit
2eab677f10

+ 27
- 3
server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/UpgradesAction.java View File

@@ -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));
}
}

+ 46
- 19
server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/AvailableAction.java View File

@@ -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) {

+ 19
- 16
server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/InstalledAction.java View File

@@ -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) {

+ 22
- 29
server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PendingAction.java View File

@@ -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());
}
}

+ 61
- 166
server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java View File

@@ -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();

+ 55
- 47
server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/UpdatesAction.java View File

@@ -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) {

+ 9
- 9
server/sonar-webserver-webapi/src/test/java/org/sonar/server/plugins/ws/InstalledActionTest.java View File

@@ -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();

+ 0
- 282
server/sonar-webserver-webapi/src/test/java/org/sonar/server/plugins/ws/PluginWSCommonsTest.java View File

@@ -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");
}

}

+ 135
- 0
sonar-ws/src/main/protobuf/ws-plugins.proto View File

@@ -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;
}



Loading…
Cancel
Save