From 66d6f7484be1ca152b9ae9e98723fda6c5dddbe4 Mon Sep 17 00:00:00 2001 From: Steve Marion Date: Wed, 6 Dec 2023 18:02:56 +0100 Subject: [PATCH] SONAR-21194 add requiredForLanguages response fields to plugins/installed endpoint. --- .../server/plugins/ws/InstalledActionIT.java | 73 ++++++++++--------- .../server/plugins/ws/InstalledAction.java | 3 +- .../server/plugins/ws/PluginWSCommons.java | 7 +- .../plugins/ws/example-installed_plugins.json | 12 ++- .../org/sonar/core/platform/PluginInfo.java | 19 ++++- .../sonar/core/platform/PluginInfoTest.java | 3 + sonar-ws/src/main/protobuf/ws-plugins.proto | 1 + 7 files changed, 74 insertions(+), 44 deletions(-) diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/plugins/ws/InstalledActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/plugins/ws/InstalledActionIT.java index c14fd46f10d..8eff4e3227c 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/plugins/ws/InstalledActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/plugins/ws/InstalledActionIT.java @@ -30,7 +30,6 @@ import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Optional; -import java.util.Random; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -39,11 +38,11 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.Action; import org.sonar.api.utils.System2; import org.sonar.core.platform.PluginInfo; +import org.sonar.core.plugin.PluginType; import org.sonar.db.DbTester; import org.sonar.db.plugin.PluginDto.Type; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.plugins.PluginFilesAndMd5.FileAndMd5; -import org.sonar.core.plugin.PluginType; import org.sonar.server.plugins.ServerPlugin; import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.plugins.UpdateCenterMatrixFactory; @@ -86,14 +85,14 @@ public class InstalledActionIT { @DataProvider public static Object[][] editionBundledLicenseValues() { return new Object[][] { - {"sonarsource"}, - {"SonarSource"}, - {"SonaRSOUrce"}, - {"SONARSOURCE"}, - {"commercial"}, - {"Commercial"}, - {"COMMERCIAL"}, - {"COmmERCiaL"}, + {"sonarsource", "SONARSOURCE"}, + {"SonarSource", "SONARSOURCE"}, + {"SonaRSOUrce", "SonarSource"}, + {"SONARSOURCE", "SonarSource"}, + {"commercial", "SONARSOURCE"}, + {"Commercial", "SONARSOURCE"}, + {"COMMERCIAL", "SonarSource"}, + {"COmmERCiaL", "SonarSource"}, }; } @@ -203,6 +202,7 @@ public class InstalledActionIT { assertThat(json.get("organizationUrl")).isNull(); assertThat(json.get("homepageUrl")).isNull(); assertThat(json.get("issueTrackerUrl")).isNull(); + assertThat(json.get("requiredForLanguage")).isNull(); } private ServerPlugin newInstalledPlugin(PluginInfo plugin) throws IOException { @@ -226,6 +226,7 @@ public class InstalledActionIT { .setHomepageUrl("homepage_url") .setIssueTrackerUrl("issueTracker_url") .setImplementationBuild("sou_rev_sha1") + .addRequiredForLanguage("bar") .setSonarLintSupported(true)); when(serverPluginRepository.getPlugins()).thenReturn(singletonList(plugin)); db.pluginDbTester().insertPlugin( @@ -236,29 +237,31 @@ public class InstalledActionIT { String response = tester.newRequest().execute().getInput(); verifyNoMoreInteractions(updateCenterMatrixFactory); - assertJson(response).isSimilarTo( - "{" + - " \"plugins\":" + - " [" + - " {" + - " \"key\": \"foo\"," + - " \"name\": \"plugName\"," + - " \"description\": \"desc_it\"," + - " \"version\": \"1.0\"," + - " \"license\": \"license_hey\"," + - " \"organizationName\": \"org_name\"," + - " \"organizationUrl\": \"org_url\",\n" + - " \"editionBundled\": false," + - " \"homepageUrl\": \"homepage_url\"," + - " \"issueTrackerUrl\": \"issueTracker_url\"," + - " \"implementationBuild\": \"sou_rev_sha1\"," + - " \"sonarLintSupported\": true," + - " \"filename\": \"" + plugin.getJar().getFile().getName() + "\"," + - " \"hash\": \"" + plugin.getJar().getMd5() + "\"," + - " \"updatedAt\": 100" + - " }" + - " ]" + - "}"); + String expected = String.format(""" + { + "plugins": + [ + { + "key": "foo", + "name": "plugName", + "description": "desc_it", + "version": "1.0", + "license": "license_hey", + "organizationName": "org_name", + "organizationUrl": "org_url", + "editionBundled": false, + "homepageUrl": "homepage_url", + "issueTrackerUrl": "issueTracker_url", + "implementationBuild": "sou_rev_sha1", + "sonarLintSupported": true, + "filename": "%s", + "hash": "%s", + "updatedAt": 100, + "requiredForLanguages": ["bar"] + } + ] + }""", plugin.getJar().getFile().getName(), plugin.getJar().getMd5()); + assertJson(response).isSimilarTo(expected); } @Test @@ -362,10 +365,8 @@ public class InstalledActionIT { @Test @UseDataProvider("editionBundledLicenseValues") - public void commercial_plugins_from_SonarSource_has_flag_editionBundled_true_based_on_jar_info(String license) throws Exception { + public void commercial_plugins_from_SonarSource_has_flag_editionBundled_true_based_on_jar_info(String license, String organization) throws Exception { String jarFilename = getClass().getSimpleName() + "/" + "some.jar"; - Random random = new Random(); - String organization = random.nextBoolean() ? "SonarSource" : "SONARSOURCE"; String pluginKey = "plugKey"; File jar = new File(getClass().getResource(jarFilename).toURI()); when(serverPluginRepository.getPlugins()).thenReturn(asList( diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/InstalledAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/InstalledAction.java index 7052efb57c7..4f54f8dd78d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/InstalledAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/InstalledAction.java @@ -33,12 +33,12 @@ import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.core.platform.PluginInfo; +import org.sonar.core.plugin.PluginType; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.permission.GlobalPermission; import org.sonar.db.plugin.PluginDto; import org.sonar.db.plugin.PluginDto.Type; -import org.sonar.core.plugin.PluginType; import org.sonar.server.plugins.ServerPlugin; import org.sonar.server.plugins.ServerPluginRepository; import org.sonar.server.plugins.UpdateCenterMatrixFactory; @@ -84,6 +84,7 @@ public class InstalledAction implements PluginsWsAction { "Requires authentication.") .setSince("5.2") .setChangelog( + new Change("10.4", "The response field 'requiredForLanguages' is added for plugins that support it"), new Change("9.8", "The 'documentationPath' field is deprecated"), new Change("9.7", "Authentication check added"), new Change("8.0", "The 'documentationPath' field is added"), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java index 6f9ce1950dc..0dcd9c09d94 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/plugins/ws/PluginWSCommons.java @@ -94,6 +94,7 @@ public class PluginWSCommons { ofNullable(pluginInfo.getIssueTrackerUrl()).ifPresent(builder::setIssueTrackerUrl); ofNullable(pluginInfo.getImplementationBuild()).ifPresent(builder::setImplementationBuild); ofNullable(pluginInfo.getDocumentationPath()).ifPresent(builder::setDocumentationPath); + builder.addAllRequiredForLanguages(pluginInfo.getRequiredForLanguages()); return builder.build(); } @@ -109,9 +110,9 @@ public class PluginWSCommons { static List buildRequires(PluginUpdate pluginUpdate) { return pluginUpdate.getRelease().getOutgoingDependencies().stream().map( - org.sonar.updatecenter.common.Release::getArtifact) - .filter(release -> release instanceof Plugin) - .map(artifact -> (Plugin) artifact) + org.sonar.updatecenter.common.Release::getArtifact) + .filter(Plugin.class::isInstance) + .map(Plugin.class::cast) .map(artifact -> { Require.Builder builder = Require.newBuilder() .setKey(artifact.getKey()); diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json index 19934ee780a..b3447ab4b3e 100644 --- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json +++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/plugins/ws/example-installed_plugins.json @@ -16,7 +16,8 @@ "hash": "abcdef123456", "sonarLintSupported": false, "documentationPath": "static/documentation.md", - "updatedAt": 123456789 + "updatedAt": 123456789, + "requiredForLanguages": [] }, { "key": "java", @@ -34,7 +35,11 @@ "hash": "abcdef123456", "sonarLintSupported": true, "documentationPath": "static/documentation.md", - "updatedAt": 123456789 + "updatedAt": 123456789, + "requiredForLanguages": [ + "java", + "xml" + ] }, { "key": "scmsvn", @@ -51,7 +56,8 @@ "filename": "sonar-scm-svn-plugin-1.0.jar", "hash": "abcdef123456", "sonarLintSupported": false, - "updatedAt": 123456789 + "updatedAt": 123456789, + "requiredForLanguages": [] } ] } diff --git a/sonar-core/src/main/java/org/sonar/core/platform/PluginInfo.java b/sonar-core/src/main/java/org/sonar/core/platform/PluginInfo.java index ba2616b7860..2c3e9864457 100644 --- a/sonar-core/src/main/java/org/sonar/core/platform/PluginInfo.java +++ b/sonar-core/src/main/java/org/sonar/core/platform/PluginInfo.java @@ -36,9 +36,9 @@ import java.util.zip.ZipEntry; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; -import org.sonar.api.utils.MessageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.utils.MessageException; import org.sonar.updatecenter.common.PluginManifest; import org.sonar.updatecenter.common.Version; @@ -100,6 +100,8 @@ public class PluginInfo implements Comparable { private final Set requiredPlugins = new HashSet<>(); + private final Set requiredForLanguages = new HashSet<>(); + public PluginInfo(String key) { requireNonNull(key, "Plugin key is missing from manifest"); this.key = key; @@ -210,6 +212,10 @@ public class PluginInfo implements Comparable { return requiredPlugins; } + public Set getRequiredForLanguages() { + return requiredForLanguages; + } + public PluginInfo setName(@Nullable String name) { this.name = (name != null ? name : this.key); return this; @@ -299,6 +305,11 @@ public class PluginInfo implements Comparable { return this; } + public PluginInfo addRequiredForLanguage(String lang) { + this.requiredForLanguages.add(lang); + return this; + } + /** * Find out if this plugin is compatible with a given version of Sonar Plugin API. * The version of plugin api embedded in SQ must be greater than or equal to the minimal version @@ -406,6 +417,12 @@ public class PluginInfo implements Comparable { .filter(t -> !"license".equals(t.key)) .forEach(this::addRequiredPlugin); } + + String[] requiredForLanguagesFromManifest = manifest.getRequiredForLanguages(); + if (requiredForLanguagesFromManifest != null) { + Arrays.stream(requiredForLanguagesFromManifest) + .forEach(this::addRequiredForLanguage); + } } private static String getDocumentationPath(File file) { diff --git a/sonar-core/src/test/java/org/sonar/core/platform/PluginInfoTest.java b/sonar-core/src/test/java/org/sonar/core/platform/PluginInfoTest.java index 5c8aefa2e4c..0ef6d8e5b3e 100644 --- a/sonar-core/src/test/java/org/sonar/core/platform/PluginInfoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/platform/PluginInfoTest.java @@ -190,6 +190,7 @@ public class PluginInfoTest { assertThat(pluginInfo.getMinimalSonarPluginApiVersion()).isNull(); assertThat(pluginInfo.getRequiredPlugins()).isEmpty(); assertThat(pluginInfo.isSonarLintSupported()).isFalse(); + assertThat(pluginInfo.getRequiredForLanguages()).isEmpty(); } @Test @@ -210,6 +211,7 @@ public class PluginInfoTest { manifest.setIssueTrackerUrl("http://jira.com"); manifest.setRequirePlugins(new String[] {"java:2.0", "pmd:1.3"}); manifest.setSonarLintSupported(true); + manifest.setRequiredForLanguages(new String[]{"java", "xml"}); File jarFile = temp.newFile(); PluginInfo pluginInfo = PluginInfo.create(jarFile, manifest); @@ -225,6 +227,7 @@ public class PluginInfoTest { assertThat(pluginInfo.getMinimalSonarPluginApiVersion().getName()).isEqualTo("4.5.1"); assertThat(pluginInfo.getRequiredPlugins()).extracting("key").containsOnly("java", "pmd"); assertThat(pluginInfo.isSonarLintSupported()).isTrue(); + assertThat(pluginInfo.getRequiredForLanguages()).containsOnly("java", "xml"); } @Test diff --git a/sonar-ws/src/main/protobuf/ws-plugins.proto b/sonar-ws/src/main/protobuf/ws-plugins.proto index 41f7c89ddd8..2fcb7cc2b6d 100644 --- a/sonar-ws/src/main/protobuf/ws-plugins.proto +++ b/sonar-ws/src/main/protobuf/ws-plugins.proto @@ -124,6 +124,7 @@ message PluginDetails { optional string documentationPath = 16; optional int64 updatedAt = 17; optional string type = 18; + repeated string requiredForLanguages = 19; } // WS api/plugins/pending -- 2.39.5