@@ -76,7 +76,8 @@ public class InstalledAction implements PluginsWsAction { | |||
new Change("6.6", "The 'fileHash' field is added"), | |||
new Change("6.6", "The 'sonarLintSupported' field is added"), | |||
new Change("6.6", "The 'updatedAt' field is added"), | |||
new Change("7.0", "The fields 'compressedHash' and 'compressedFilename' are added")) | |||
new Change("7.0", "The fields 'compressedHash' and 'compressedFilename' are added"), | |||
new Change("8.0", "The 'documentationPath' field is added")) | |||
.setHandler(this) | |||
.setResponseExample(Resources.getResource(this.getClass(), "example-installed_plugins.json")); | |||
@@ -28,6 +28,7 @@ import java.util.Collection; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.Nonnull; | |||
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; | |||
@@ -77,6 +78,7 @@ public class PendingAction implements PluginsWsAction { | |||
.setDescription("Get the list of plugins which will either be installed or removed at the next startup of the SonarQube instance, sorted by plugin name.<br/>" + | |||
"Require 'Administer System' permission.") | |||
.setSince("5.2") | |||
.setChangelog(new Change("8.0", "The 'documentationPath' field is added")) | |||
.setHandler(this) | |||
.setResponseExample(getResource(this.getClass(), "example-pending_plugins.json")); | |||
} |
@@ -53,6 +53,7 @@ public class PluginWSCommons { | |||
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"; | |||
@@ -109,6 +110,7 @@ public class PluginWSCommons { | |||
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()); | |||
} |
@@ -15,6 +15,7 @@ | |||
"filename": "sonar-scm-git-plugin-1.0.jar", | |||
"hash": "abcdef123456", | |||
"sonarLintSupported": false, | |||
"documentationPath": "static/documentation.md", | |||
"updatedAt": 123456789 | |||
}, | |||
{ | |||
@@ -32,6 +33,7 @@ | |||
"filename": "sonar-java-plugin-3.0.jar", | |||
"hash": "abcdef123456", | |||
"sonarLintSupported": true, | |||
"documentationPath": "static/documentation.md", | |||
"updatedAt": 123456789 | |||
}, | |||
{ |
@@ -11,7 +11,8 @@ | |||
"organizationUrl": "http://www.sonarsource.com", | |||
"homepageUrl": "https://redirect.sonarsource.com/plugins/scmcvs.html", | |||
"issueTrackerUrl": "http://jira.codehaus.org/browse/SONARSCCVS", | |||
"implementationBuild": "f9735104bfa053abc50edad720c79d89a4f5cd72" | |||
"implementationBuild": "f9735104bfa053abc50edad720c79d89a4f5cd72", | |||
"documentationPath": "static/documentation.md" | |||
} | |||
], | |||
"updating": [ | |||
@@ -41,7 +42,8 @@ | |||
"organizationUrl": "http://www.sonarsource.com", | |||
"homepageUrl": "https://redirect.sonarsource.com/plugins/scmgit.html/sonar-scm-git-plugin", | |||
"issueTrackerUrl": "https://jira.sonarsource.com/browse/SONARSCGIT", | |||
"implementationBuild": "a713dd64daf8719ba4e7f551f9a1966c62690c17" | |||
"implementationBuild": "a713dd64daf8719ba4e7f551f9a1966c62690c17", | |||
"documentationPath": "static/documentation.md" | |||
} | |||
] | |||
} |
@@ -197,6 +197,7 @@ public class InstalledActionTest { | |||
.setHomepageUrl("homepage_url") | |||
.setIssueTrackerUrl("issueTracker_url") | |||
.setImplementationBuild("sou_rev_sha1") | |||
.setDocumentationPath("static/documentation.md") | |||
.setSonarLintSupported(true)); | |||
when(pluginFileSystem.getInstalledFiles()).thenReturn(singletonList(plugin)); | |||
@@ -224,6 +225,7 @@ public class InstalledActionTest { | |||
" \"issueTrackerUrl\": \"issueTracker_url\"," + | |||
" \"implementationBuild\": \"sou_rev_sha1\"," + | |||
" \"sonarLintSupported\": true," + | |||
" \"documentationPath\": \"static/documentation.md\"," + | |||
" \"filename\": \"" + plugin.getLoadedJar().getFile().getName() + "\"," + | |||
" \"hash\": \"" + plugin.getLoadedJar().getMd5() + "\"," + | |||
" \"updatedAt\": 100" + | |||
@@ -376,7 +378,7 @@ public class InstalledActionTest { | |||
@DataProvider | |||
public static Object[][] editionBundledLicenseValues() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{"sonarsource"}, | |||
{"SonarSource"}, | |||
{"SonaRSOUrce"}, |
@@ -27,12 +27,16 @@ import java.io.File; | |||
import java.io.IOException; | |||
import java.util.Arrays; | |||
import java.util.HashSet; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.jar.JarFile; | |||
import java.util.regex.Pattern; | |||
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.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.updatecenter.common.PluginManifest; | |||
import org.sonar.updatecenter.common.Version; | |||
@@ -40,6 +44,7 @@ import org.sonar.updatecenter.common.Version; | |||
import static java.util.Objects.requireNonNull; | |||
public class PluginInfo implements Comparable<PluginInfo> { | |||
private static final Logger LOGGER = Loggers.get(PluginInfo.class); | |||
private static final Joiner SLASH_JOINER = Joiner.on(" / ").skipNulls(); | |||
@@ -140,6 +145,9 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
@CheckForNull | |||
private boolean sonarLintSupported; | |||
@CheckForNull | |||
private String documentationPath; | |||
private final Set<RequiredPlugin> requiredPlugins = new HashSet<>(); | |||
public PluginInfo(String key) { | |||
@@ -234,6 +242,10 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
return sonarLintSupported; | |||
} | |||
public String getDocumentationPath() { | |||
return documentationPath; | |||
} | |||
@CheckForNull | |||
public String getBasePlugin() { | |||
return basePlugin; | |||
@@ -263,6 +275,11 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
return this; | |||
} | |||
public PluginInfo setDocumentationPath(@Nullable String documentationPath) { | |||
this.documentationPath = documentationPath; | |||
return this; | |||
} | |||
/** | |||
* Required | |||
*/ | |||
@@ -313,7 +330,7 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
public PluginInfo setBasePlugin(@Nullable String s) { | |||
if ("l10nen".equals(s)) { | |||
Loggers.get(PluginInfo.class).info("Plugin [{}] defines 'l10nen' as base plugin. " + | |||
LOGGER.info("Plugin [{}] defines 'l10nen' as base plugin. " + | |||
"This metadata can be removed from manifest of l10n plugins since version 5.2.", key); | |||
basePlugin = null; | |||
} else { | |||
@@ -411,6 +428,7 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
info.setName(manifest.getName()); | |||
info.setMainClass(manifest.getMainClass()); | |||
info.setVersion(Version.create(manifest.getVersion())); | |||
info.setDocumentationPath(getDocumentationPath(jarFile)); | |||
// optional fields | |||
info.setDescription(manifest.getDescription()); | |||
@@ -437,4 +455,16 @@ public class PluginInfo implements Comparable<PluginInfo> { | |||
} | |||
return info; | |||
} | |||
private static String getDocumentationPath(File file) { | |||
try (JarFile jarFile = new JarFile(file)) { | |||
return Optional.ofNullable(jarFile.getEntry("static/documentation.md")) | |||
.map(ZipEntry::getName) | |||
.orElse(null); | |||
} catch (IOException e) { | |||
LOGGER.warn("Could not retrieve documentation path from " + file, e); | |||
} | |||
return null; | |||
} | |||
} |
@@ -210,7 +210,7 @@ public class PluginInfoTest { | |||
manifest.setOrganization("SonarSource"); | |||
manifest.setOrganizationUrl("http://sonarsource.com"); | |||
manifest.setIssueTrackerUrl("http://jira.com"); | |||
manifest.setRequirePlugins(new String[] {"java:2.0", "pmd:1.3"}); | |||
manifest.setRequirePlugins(new String[]{"java:2.0", "pmd:1.3"}); | |||
manifest.setSonarLintSupported(true); | |||
File jarFile = temp.newFile(); | |||
@@ -237,7 +237,7 @@ public class PluginInfoTest { | |||
manifest.setVersion("1.0"); | |||
manifest.setName("Java"); | |||
manifest.setMainClass("org.foo.FooPlugin"); | |||
manifest.setRequirePlugins(new String[] {"license:" + version}); | |||
manifest.setRequirePlugins(new String[]{"license:" + version}); | |||
File jarFile = temp.newFile(); | |||
PluginInfo pluginInfo = PluginInfo.create(jarFile, manifest); | |||
@@ -252,7 +252,7 @@ public class PluginInfoTest { | |||
manifest.setVersion("1.0"); | |||
manifest.setName("Java"); | |||
manifest.setMainClass("org.foo.FooPlugin"); | |||
manifest.setRequirePlugins(new String[] {"java:2.0", "license:" + version, "pmd:1.3"}); | |||
manifest.setRequirePlugins(new String[]{"java:2.0", "license:" + version, "pmd:1.3"}); | |||
File jarFile = temp.newFile(); | |||
PluginInfo pluginInfo = PluginInfo.create(jarFile, manifest); | |||
@@ -261,7 +261,7 @@ public class PluginInfoTest { | |||
@DataProvider | |||
public static Object[][] licenseVersions() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{"0.3"}, | |||
{"7.2.0.1253"} | |||
}; | |||
@@ -273,11 +273,21 @@ public class PluginInfoTest { | |||
PluginInfo checkstyleInfo = PluginInfo.create(checkstyleJar); | |||
assertThat(checkstyleInfo.getName()).isEqualTo("Checkstyle"); | |||
assertThat(checkstyleInfo.getDocumentationPath()).isNull(); | |||
assertThat(checkstyleInfo.getMinimalSqVersion()).isEqualTo(Version.create("2.8")); | |||
} | |||
@Test | |||
public void test_toString() throws Exception { | |||
public void create_from_file_with_documentation() { | |||
File jarWithDocs = FileUtils.toFile(getClass().getResource("/org/sonar/core/platform/jar_with_documentation.jar")); | |||
PluginInfo checkstyleInfo = PluginInfo.create(jarWithDocs); | |||
assertThat(checkstyleInfo.getDocumentationPath()).isNotBlank(); | |||
assertThat(checkstyleInfo.getDocumentationPath()).isEqualTo("static/documentation.md"); | |||
} | |||
@Test | |||
public void test_toString() { | |||
PluginInfo pluginInfo = new PluginInfo("java").setVersion(Version.create("1.1")); | |||
assertThat(pluginInfo.toString()).isEqualTo("[java / 1.1]"); | |||