mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-15 09:46:16 +01:00
Merged in feature/plugin_center (pull request #291)
Feature/plugin center
This commit is contained in:
@@ -29,46 +29,28 @@
|
||||
<!ELEMENT scm-version (#PCDATA)>
|
||||
|
||||
<!--- contains informations of the plugin for the plugin backend -->
|
||||
<!ELEMENT information (author|artifactId|category|tags|description|groupId|name|screenshots|url|version|wiki)*>
|
||||
<!ELEMENT information (author|category|description|name|version|displayName|avatarUrl)*>
|
||||
|
||||
<!--- plugin author -->
|
||||
<!ELEMENT author (#PCDATA)>
|
||||
|
||||
<!--- maven artifact id -->
|
||||
<!ELEMENT artifactId (#PCDATA)>
|
||||
|
||||
<!--- category of the plugin -->
|
||||
<!ELEMENT category (#PCDATA)>
|
||||
|
||||
<!--- tags of the plugin -->
|
||||
<!ELEMENT tags (tag)*>
|
||||
|
||||
<!--- single tag -->
|
||||
<!ELEMENT tag (#PCDATA)>
|
||||
|
||||
<!--- description of the plugin -->
|
||||
<!ELEMENT description (#PCDATA)>
|
||||
|
||||
<!--- maven groupId id -->
|
||||
<!ELEMENT groupId (#PCDATA)>
|
||||
|
||||
<!--- name of the plugin or the name of the os condition -->
|
||||
<!ELEMENT name (#PCDATA)>
|
||||
|
||||
<!--- contains screenshots of the plugin -->
|
||||
<!ELEMENT screenshots (screenshot)*>
|
||||
|
||||
<!--- single screenshot of the plugin -->
|
||||
<!ELEMENT screenshot (#PCDATA)>
|
||||
|
||||
<!--- the url of the plugin homepage -->
|
||||
<!ELEMENT url (#PCDATA)>
|
||||
|
||||
<!--- the current version of the plugin -->
|
||||
<!ELEMENT version (#PCDATA)>
|
||||
|
||||
<!--- the url of a wiki page -->
|
||||
<!ELEMENT wiki (#PCDATA)>
|
||||
<!--- plugin displayName -->
|
||||
<!ELEMENT displayName (#PCDATA)>
|
||||
|
||||
<!--- url of the plugin avatar -->
|
||||
<!ELEMENT avatarUrl (#PCDATA)>
|
||||
|
||||
<!--- true if the plugin should load child classes first, the default is false -->
|
||||
<!ELEMENT child-first-classloader (#PCDATA)>
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -437,7 +437,7 @@
|
||||
<plugin>
|
||||
<groupId>sonia.scm.maven</groupId>
|
||||
<artifactId>smp-maven-plugin</artifactId>
|
||||
<version>1.0.0-alpha-4</version>
|
||||
<version>1.0.0-alpha-6</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
@@ -73,7 +73,7 @@ public class ScmConfiguration implements Configuration {
|
||||
* Default plugin url
|
||||
*/
|
||||
public static final String DEFAULT_PLUGINURL =
|
||||
"http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false";
|
||||
"http://download.scm-manager.org/api/v2/plugins.json?os={os}&arch={arch}&snapshot=false&version={version}";
|
||||
|
||||
/**
|
||||
* Default url for login information (plugin and feature tips on the login page).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* <p>
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* <p>
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
@@ -13,7 +13,7 @@
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -24,558 +24,88 @@
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* <p>
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.plugin;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.github.sdorra.ssp.PermissionObject;
|
||||
import com.github.sdorra.ssp.StaticPermissions;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import lombok.Data;
|
||||
import sonia.scm.Validateable;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Data
|
||||
@StaticPermissions(
|
||||
value = "plugin",
|
||||
generatedClass = "PluginPermissions",
|
||||
permissions = {},
|
||||
globalPermissions = { "read", "manage" },
|
||||
globalPermissions = {"read", "manage"},
|
||||
custom = true, customGlobal = true
|
||||
)
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "plugin-information")
|
||||
public class PluginInformation
|
||||
implements PermissionObject, Validateable, Cloneable, Serializable
|
||||
{
|
||||
public class PluginInformation implements PermissionObject, Validateable, Cloneable, Serializable {
|
||||
|
||||
/** Field description */
|
||||
private static final long serialVersionUID = 461382048865977206L;
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
private String name;
|
||||
private String version;
|
||||
private String displayName;
|
||||
private String description;
|
||||
private String author;
|
||||
private String category;
|
||||
private String avatarUrl;
|
||||
private PluginCondition condition;
|
||||
private PluginState state;
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @since 1.11
|
||||
*/
|
||||
@Override
|
||||
public PluginInformation clone()
|
||||
{
|
||||
public PluginInformation clone() {
|
||||
PluginInformation clone = new PluginInformation();
|
||||
|
||||
clone.setArtifactId(artifactId);
|
||||
clone.setName(name);
|
||||
clone.setVersion(version);
|
||||
clone.setDisplayName(displayName);
|
||||
clone.setDescription(description);
|
||||
clone.setAuthor(author);
|
||||
clone.setCategory(category);
|
||||
clone.setTags(tags);
|
||||
|
||||
if (condition != null)
|
||||
{
|
||||
clone.setAvatarUrl(avatarUrl);
|
||||
clone.setState(state);
|
||||
if (condition != null) {
|
||||
clone.setCondition(condition.clone());
|
||||
}
|
||||
|
||||
clone.setDescription(description);
|
||||
clone.setGroupId(groupId);
|
||||
clone.setName(name);
|
||||
|
||||
if (Util.isNotEmpty(screenshots))
|
||||
{
|
||||
clone.setScreenshots(new ArrayList<String>(screenshots));
|
||||
}
|
||||
|
||||
clone.setState(state);
|
||||
clone.setUrl(url);
|
||||
clone.setVersion(version);
|
||||
clone.setWiki(wiki);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param obj
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
public String getId() {
|
||||
return getName(true);
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
public String getName(boolean withVersion) {
|
||||
StringBuilder id = new StringBuilder(name);
|
||||
|
||||
final PluginInformation other = (PluginInformation) obj;
|
||||
|
||||
//J-
|
||||
return Objects.equal(artifactId, other.artifactId)
|
||||
&& Objects.equal(author, other.author)
|
||||
&& Objects.equal(category, other.category)
|
||||
&& Objects.equal(tags, other.tags)
|
||||
&& Objects.equal(condition, other.condition)
|
||||
&& Objects.equal(description, other.description)
|
||||
&& Objects.equal(groupId, other.groupId)
|
||||
&& Objects.equal(name, other.name)
|
||||
&& Objects.equal(screenshots, other.screenshots)
|
||||
&& Objects.equal(state, other.state)
|
||||
&& Objects.equal(url, other.url)
|
||||
&& Objects.equal(version, other.version)
|
||||
&& Objects.equal(wiki, other.wiki);
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(artifactId, author, category, tags, condition,
|
||||
description, groupId, name, screenshots, state, url, version, wiki);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("artifactId", artifactId)
|
||||
.add("author", author)
|
||||
.add("category", category)
|
||||
.add("tags", tags)
|
||||
.add("condition", condition)
|
||||
.add("description", description)
|
||||
.add("groupId", groupId)
|
||||
.add("name", name)
|
||||
.add("screenshots", screenshots)
|
||||
.add("state", state)
|
||||
.add("url", url)
|
||||
.add("version", version)
|
||||
.add("wiki", wiki)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getArtifactId()
|
||||
{
|
||||
return artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getAuthor()
|
||||
{
|
||||
return author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCategory()
|
||||
{
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PluginCondition getCondition()
|
||||
{
|
||||
return condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getDescription()
|
||||
{
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getGroupId()
|
||||
{
|
||||
return groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getId()
|
||||
{
|
||||
return getId(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param withVersion
|
||||
*
|
||||
* @return
|
||||
* @since 1.21
|
||||
*/
|
||||
public String getId(boolean withVersion)
|
||||
{
|
||||
StringBuilder id = new StringBuilder(groupId);
|
||||
|
||||
id.append(":").append(artifactId);
|
||||
|
||||
if (withVersion)
|
||||
{
|
||||
if (withVersion) {
|
||||
id.append(":").append(version);
|
||||
}
|
||||
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getScreenshots()
|
||||
{
|
||||
return screenshots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public PluginState getState()
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<String> getTags()
|
||||
{
|
||||
return tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUrl()
|
||||
{
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getWiki()
|
||||
{
|
||||
return wiki;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isValid()
|
||||
{
|
||||
return Util.isNotEmpty(groupId) && Util.isNotEmpty(artifactId)
|
||||
&& Util.isNotEmpty(name) && Util.isNotEmpty(version);
|
||||
public boolean isValid() {
|
||||
return Util.isNotEmpty(name) && Util.isNotEmpty(version);
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param artifactId
|
||||
*/
|
||||
public void setArtifactId(String artifactId)
|
||||
{
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param author
|
||||
*/
|
||||
public void setAuthor(String author)
|
||||
{
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param category
|
||||
*/
|
||||
public void setCategory(String category)
|
||||
{
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param condition
|
||||
*/
|
||||
public void setCondition(PluginCondition condition)
|
||||
{
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param description
|
||||
*/
|
||||
public void setDescription(String description)
|
||||
{
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param groupId
|
||||
*/
|
||||
public void setGroupId(String groupId)
|
||||
{
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param screenshots
|
||||
*/
|
||||
public void setScreenshots(List<String> screenshots)
|
||||
{
|
||||
this.screenshots = screenshots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
public void setState(PluginState state)
|
||||
{
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param tags
|
||||
*/
|
||||
public void setTags(List<String> tags)
|
||||
{
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void setUrl(String url)
|
||||
{
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param version
|
||||
*/
|
||||
public void setVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param wiki
|
||||
*/
|
||||
public void setWiki(String wiki)
|
||||
{
|
||||
this.wiki = wiki;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private String artifactId;
|
||||
|
||||
/** Field description */
|
||||
private String author;
|
||||
|
||||
/** Field description */
|
||||
private String category;
|
||||
|
||||
/** Field description */
|
||||
private PluginCondition condition;
|
||||
|
||||
/** Field description */
|
||||
private String description;
|
||||
|
||||
/** Field description */
|
||||
private String groupId;
|
||||
|
||||
/** Field description */
|
||||
private String name;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "screenshot")
|
||||
@XmlElementWrapper(name = "screenshots")
|
||||
private List<String> screenshots;
|
||||
|
||||
/** Field description */
|
||||
private PluginState state;
|
||||
|
||||
/** Field description */
|
||||
@XmlElement(name = "tag")
|
||||
@XmlElementWrapper(name = "tags")
|
||||
private List<String> tags;
|
||||
|
||||
/** Field description */
|
||||
private String url;
|
||||
|
||||
/** Field description */
|
||||
private String version;
|
||||
|
||||
/** Field description */
|
||||
private String wiki;
|
||||
}
|
||||
|
||||
@@ -75,11 +75,7 @@ public class PluginInformationComparator
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
result = Util.compare(plugin.getGroupId(), other.getGroupId());
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
result = Util.compare(plugin.getArtifactId(), other.getArtifactId());
|
||||
result = Util.compare(plugin.getName(), other.getName());
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
@@ -99,7 +95,6 @@ public class PluginInformationComparator
|
||||
result = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -219,16 +219,10 @@ public final class SmpArchive
|
||||
throw new PluginException("could not find information section");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(info.getGroupId()))
|
||||
if (Strings.isNullOrEmpty(info.getName()))
|
||||
{
|
||||
throw new PluginException(
|
||||
"could not find groupId in plugin descriptor");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(info.getArtifactId()))
|
||||
{
|
||||
throw new PluginException(
|
||||
"could not find artifactId in plugin descriptor");
|
||||
"could not find name in plugin descriptor");
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(info.getVersion()))
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
|
||||
@@ -37,14 +37,11 @@ package sonia.scm.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.io.Command;
|
||||
import sonia.scm.io.CommandResult;
|
||||
import sonia.scm.io.SimpleCommand;
|
||||
import sonia.scm.io.ZipUnArchiver;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
@@ -55,12 +52,13 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
|
||||
@@ -85,7 +85,7 @@ public class SmpArchiveTest
|
||||
public void testExtract()
|
||||
throws IOException, ParserConfigurationException, SAXException
|
||||
{
|
||||
File archive = createArchive("sonia.sample", "sample", "1.0");
|
||||
File archive = createArchive("sonia.sample", "1.0");
|
||||
File target = tempFolder.newFolder();
|
||||
|
||||
IOUtil.mkdirs(target);
|
||||
@@ -112,7 +112,7 @@ public class SmpArchiveTest
|
||||
@Test
|
||||
public void testGetPlugin() throws IOException
|
||||
{
|
||||
File archive = createArchive("sonia.sample", "sample", "1.0");
|
||||
File archive = createArchive("sonia.sample", "1.0");
|
||||
Plugin plugin = SmpArchive.create(archive).getPlugin();
|
||||
|
||||
assertNotNull(plugin);
|
||||
@@ -121,8 +121,7 @@ public class SmpArchiveTest
|
||||
|
||||
assertNotNull(info);
|
||||
|
||||
assertEquals("sonia.sample", info.getGroupId());
|
||||
assertEquals("sample", info.getArtifactId());
|
||||
assertEquals("sonia.sample", info.getName());
|
||||
assertEquals("1.0", info.getVersion());
|
||||
}
|
||||
|
||||
@@ -132,22 +131,9 @@ public class SmpArchiveTest
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test(expected = PluginException.class)
|
||||
public void testWithMissingArtifactId() throws IOException
|
||||
public void testWithMissingName() throws IOException
|
||||
{
|
||||
File archive = createArchive("sonia.sample", null, "1.0");
|
||||
|
||||
SmpArchive.create(archive).getPlugin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test(expected = PluginException.class)
|
||||
public void testWithMissingGroupId() throws IOException
|
||||
{
|
||||
File archive = createArchive(null, "sample", "1.0");
|
||||
File archive = createArchive( null, "1.0");
|
||||
|
||||
SmpArchive.create(archive).getPlugin();
|
||||
}
|
||||
@@ -160,7 +146,7 @@ public class SmpArchiveTest
|
||||
@Test(expected = PluginException.class)
|
||||
public void testWithMissingVersion() throws IOException
|
||||
{
|
||||
File archive = createArchive("sonia.sample", "sample", null);
|
||||
File archive = createArchive("sonia.sample", null);
|
||||
|
||||
SmpArchive.create(archive).getPlugin();
|
||||
}
|
||||
@@ -169,13 +155,12 @@ public class SmpArchiveTest
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param groupId
|
||||
* @param artifactId
|
||||
* @param name
|
||||
* @param version
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private File createArchive(String groupId, String artifactId, String version)
|
||||
private File createArchive(String name, String version)
|
||||
{
|
||||
File archiveFile;
|
||||
|
||||
@@ -183,7 +168,7 @@ public class SmpArchiveTest
|
||||
{
|
||||
File descriptor = tempFolder.newFile();
|
||||
|
||||
writeDescriptor(descriptor, groupId, artifactId, version);
|
||||
writeDescriptor(descriptor, name, version);
|
||||
archiveFile = tempFolder.newFile();
|
||||
|
||||
try (ZipOutputStream zos =
|
||||
@@ -229,14 +214,13 @@ public class SmpArchiveTest
|
||||
*
|
||||
*
|
||||
* @param descriptor
|
||||
* @param groupId
|
||||
* @param artifactId
|
||||
* @param name
|
||||
* @param version
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeDescriptor(File descriptor, String groupId,
|
||||
String artifactId, String version)
|
||||
private void writeDescriptor(File descriptor, String name,
|
||||
String version)
|
||||
throws IOException
|
||||
{
|
||||
try
|
||||
@@ -252,8 +236,7 @@ public class SmpArchiveTest
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement("plugin");
|
||||
writer.writeStartElement("information");
|
||||
writeElement(writer, "groupId", groupId);
|
||||
writeElement(writer, "artifactId", artifactId);
|
||||
writeElement(writer, "name", name);
|
||||
writeElement(writer, "version", version);
|
||||
|
||||
writer.writeEndElement();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>scm-git-plugin</artifactId>
|
||||
<name>scm-git-plugin</name>
|
||||
<packaging>smp</packaging>
|
||||
<url>https://bitbucket.org/sdorra/scm-manager</url>
|
||||
<description>Plugin for the version control system Git</description>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
|
||||
@@ -46,14 +46,10 @@
|
||||
<scm-version>2</scm-version>
|
||||
|
||||
<information>
|
||||
<author>Sebastian Sdorra</author>
|
||||
<category>Git</category>
|
||||
<tags>
|
||||
<tag>git</tag>
|
||||
<tag>scm</tag>
|
||||
<tag>vcs</tag>
|
||||
<tag>dvcs</tag>
|
||||
</tags>
|
||||
<displayName>Git</displayName>
|
||||
<author>Cloudogu GmbH</author>
|
||||
<category>Source Code Management</category>
|
||||
<avatarUrl>/images/git-logo.png</avatarUrl>
|
||||
</information>
|
||||
|
||||
<conditions>
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>scm-hg-plugin</artifactId>
|
||||
<name>scm-hg-plugin</name>
|
||||
<packaging>smp</packaging>
|
||||
<url>https://bitbucket.org/sdorra/scm-manager</url>
|
||||
<description>Plugin for the version control system Mercurial</description>
|
||||
|
||||
@@ -40,11 +40,10 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.web.HgUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -46,15 +46,10 @@ jo
|
||||
<scm-version>2</scm-version>
|
||||
|
||||
<information>
|
||||
<author>Sebastian Sdorra</author>
|
||||
<category>Mercurial</category>
|
||||
<tags>
|
||||
<tag>mercurial</tag>
|
||||
<tag>hg</tag>
|
||||
<tag>scm</tag>
|
||||
<tag>vcs</tag>
|
||||
<tag>dvcs</tag>
|
||||
</tags>
|
||||
<displayName>Mercurial</displayName>
|
||||
<author>Cloudogu GmbH</author>
|
||||
<category>Source Code Management</category>
|
||||
<avatarUrl>/images/hg-logo.png</avatarUrl>
|
||||
</information>
|
||||
|
||||
<conditions>
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
<artifactId>scm-plugins</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
|
||||
<artifactId>scm-legacy-plugin</artifactId>
|
||||
<description>Support migrated repository urls and v1 passwords</description>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<packaging>smp</packaging>
|
||||
|
||||
@@ -21,6 +22,7 @@
|
||||
<version>${servlet.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.ws.rs</groupId>
|
||||
<artifactId>jsr311-api</artifactId>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import {withRouter} from "react-router-dom";
|
||||
|
||||
class DummyComponent extends React.Component<Props, State> {
|
||||
render() {
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import {
|
||||
ProtectedRoute,
|
||||
apiClient,
|
||||
ErrorNotification,
|
||||
ErrorBoundary
|
||||
} from "@scm-manager/ui-components";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import {binder} from "@scm-manager/ui-extensions";
|
||||
import {apiClient, ErrorBoundary, ErrorNotification, ProtectedRoute} from "@scm-manager/ui-components";
|
||||
import DummyComponent from "./DummyComponent";
|
||||
import type {Links} from "@scm-manager/ui-types";
|
||||
|
||||
|
||||
@@ -46,7 +46,9 @@
|
||||
<scm-version>2</scm-version>
|
||||
|
||||
<information>
|
||||
<author>Sebastian Sdorra</author>
|
||||
<displayName>Legacy</displayName>
|
||||
<author>Cloudogu GmbH</author>
|
||||
<category>Legacy Support</category>
|
||||
</information>
|
||||
|
||||
<conditions>
|
||||
|
||||
@@ -10,7 +10,7 @@ import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>scm-svn-plugin</artifactId>
|
||||
<name>scm-svn-plugin</name>
|
||||
<packaging>smp</packaging>
|
||||
<url>https://bitbucket.org/sdorra/scm-manager</url>
|
||||
<description>Plugin for the version control system Subversion</description>
|
||||
|
||||
@@ -46,14 +46,10 @@
|
||||
<scm-version>2</scm-version>
|
||||
|
||||
<information>
|
||||
<author>Sebastian Sdorra</author>
|
||||
<category>Subversion</category>
|
||||
<tags>
|
||||
<tag>subversion</tag>
|
||||
<tag>scm</tag>
|
||||
<tag>vcs</tag>
|
||||
<tag>svn</tag>
|
||||
</tags>
|
||||
<displayName>Subversion</displayName>
|
||||
<author>Cloudogu GmbH</author>
|
||||
<category>Source Code Management</category>
|
||||
<avatarUrl>/images/svn-logo.gif</avatarUrl>
|
||||
</information>
|
||||
|
||||
<conditions>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { AsyncCreatable, Async } from "react-select";
|
||||
import type { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
|
||||
import {Async, AsyncCreatable} from "react-select";
|
||||
import type {AutocompleteObject, SelectValue} from "@scm-manager/ui-types";
|
||||
import LabelWithHelpIcon from "./forms/LabelWithHelpIcon";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -25,12 +25,17 @@ const styles = {
|
||||
},
|
||||
content: {
|
||||
display: "flex",
|
||||
flexGrow: 1
|
||||
flexGrow: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between"
|
||||
},
|
||||
footer: {
|
||||
display: "flex",
|
||||
marginTop: "auto",
|
||||
paddingBottom: "1.5rem"
|
||||
},
|
||||
noBottomMargin: {
|
||||
marginBottom: "0 !important"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -38,9 +43,11 @@ type Props = {
|
||||
title: string,
|
||||
description: string,
|
||||
avatar: React.Node,
|
||||
contentRight?: React.Node,
|
||||
footerLeft: React.Node,
|
||||
footerRight: React.Node,
|
||||
link: string,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
@@ -55,7 +62,15 @@ class CardColumn extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { avatar, title, description, footerLeft, footerRight, classes } = this.props;
|
||||
const {
|
||||
avatar,
|
||||
title,
|
||||
description,
|
||||
contentRight,
|
||||
footerLeft,
|
||||
footerRight,
|
||||
classes
|
||||
} = this.props;
|
||||
const link = this.createLink();
|
||||
return (
|
||||
<>
|
||||
@@ -64,16 +79,29 @@ class CardColumn extends React.Component<Props> {
|
||||
<figure className={classNames(classes.centerImage, "media-left")}>
|
||||
{avatar}
|
||||
</figure>
|
||||
<div className={classNames("media-content", "text-box", classes.flexFullHeight)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"media-content",
|
||||
"text-box",
|
||||
classes.flexFullHeight
|
||||
)}
|
||||
>
|
||||
<div className={classes.content}>
|
||||
<div className="content shorten-text">
|
||||
<div
|
||||
className={classNames(
|
||||
"content",
|
||||
"shorten-text",
|
||||
classes.noBottomMargin
|
||||
)}
|
||||
>
|
||||
<p className="is-marginless">
|
||||
<strong>{title}</strong>
|
||||
</p>
|
||||
<p className="shorten-text">{description}</p>
|
||||
</div>
|
||||
{contentRight && contentRight}
|
||||
</div>
|
||||
<div className={classNames(classes.footer, "level")}>
|
||||
<div className={classNames("level", classes.footer)}>
|
||||
<div className="level-left is-hidden-mobile">{footerLeft}</div>
|
||||
<div className="level-right is-mobile">{footerRight}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { BackendError, ForbiddenError, UnauthorizedError } from "./errors";
|
||||
import {translate} from "react-i18next";
|
||||
import {BackendError, ForbiddenError, UnauthorizedError} from "./errors";
|
||||
import Notification from "./Notification";
|
||||
import BackendErrorNotification from "./BackendErrorNotification";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {translate} from "react-i18next";
|
||||
import type AutocompleteProps from "./UserGroupAutocomplete";
|
||||
import UserGroupAutocomplete from "./UserGroupAutocomplete";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {translate} from "react-i18next";
|
||||
import type AutocompleteProps from "./UserGroupAutocomplete";
|
||||
import UserGroupAutocomplete from "./UserGroupAutocomplete";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type { SelectValue } from "@scm-manager/ui-types";
|
||||
import type {SelectValue} from "@scm-manager/ui-types";
|
||||
import Autocomplete from "./Autocomplete";
|
||||
|
||||
export type AutocompleteProps = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import DiffFile from "./DiffFile";
|
||||
import type { DiffObjectProps, File } from "./DiffTypes";
|
||||
import type {DiffObjectProps, File} from "./DiffTypes";
|
||||
|
||||
type Props = DiffObjectProps & {
|
||||
diff: File[]
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
Hunk,
|
||||
Diff as DiffComponent,
|
||||
getChangeKey,
|
||||
Change,
|
||||
DiffObjectProps,
|
||||
File
|
||||
} from "react-diff-view";
|
||||
import {Change, Diff as DiffComponent, DiffObjectProps, File, getChangeKey, Hunk} from "react-diff-view";
|
||||
import injectSheets from "react-jss";
|
||||
import classNames from "classnames";
|
||||
import { translate } from "react-i18next";
|
||||
import { ButtonGroup, Button } from "../buttons";
|
||||
import {translate} from "react-i18next";
|
||||
import {Button, ButtonGroup} from "../buttons";
|
||||
|
||||
const styles = {
|
||||
panel: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { apiClient } from "../apiclient";
|
||||
import {apiClient} from "../apiclient";
|
||||
import ErrorNotification from "../ErrorNotification";
|
||||
import parser from "gitdiff-parser";
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||
import { ButtonAddons, Button } from "../../buttons";
|
||||
import { createChangesetLink, createSourcesLink } from "./changesets";
|
||||
import { translate } from "react-i18next";
|
||||
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
||||
import {Button, ButtonAddons} from "../../buttons";
|
||||
import {createChangesetLink, createSourcesLink} from "./changesets";
|
||||
import {translate} from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Changeset, Repository, Tag } from "@scm-manager/ui-types";
|
||||
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
||||
|
||||
import classNames from "classnames";
|
||||
import { Interpolate, translate } from "react-i18next";
|
||||
import {Interpolate, translate} from "react-i18next";
|
||||
import ChangesetId from "./ChangesetId";
|
||||
import injectSheet from "react-jss";
|
||||
import { DateFromNow } from "../..";
|
||||
import {DateFromNow} from "../..";
|
||||
import ChangesetAuthor from "./ChangesetAuthor";
|
||||
import { parseDescription } from "./changesets";
|
||||
import { AvatarWrapper, AvatarImage } from "../../avatar";
|
||||
import { ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
import {parseDescription} from "./changesets";
|
||||
import {AvatarImage, AvatarWrapper} from "../../avatar";
|
||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
import ChangesetTags from "./ChangesetTags";
|
||||
import ChangesetButtonGroup from "./ChangesetButtonGroup";
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// @flow
|
||||
import * as diffs from "./diffs";
|
||||
|
||||
export { diffs };
|
||||
|
||||
export * from "./changesets";
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
//@flow
|
||||
import type { Collection, Links } from "./hal";
|
||||
import type {Collection, Links} from "./hal";
|
||||
|
||||
|
||||
export type Plugin = {
|
||||
name: string,
|
||||
type: string,
|
||||
version: string,
|
||||
author: string,
|
||||
displayName: string,
|
||||
description?: string,
|
||||
author: string,
|
||||
category: string,
|
||||
avatarUrl: string,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"date-format": "Datumsformat",
|
||||
"anonymous-access-enabled": "Anonyme Zugriffe erlauben",
|
||||
"skip-failed-authenticators": "Fehlgeschlagene Authentifizierer überspringen",
|
||||
"plugin-url": "Plugin URL",
|
||||
"plugin-url": "Plugin Center URL",
|
||||
"enabled-xsrf-protection": "XSRF Protection aktivieren",
|
||||
"namespace-strategy": "Namespace Strategie",
|
||||
"login-info-url": "Login Info URL"
|
||||
@@ -55,7 +55,7 @@
|
||||
"help": {
|
||||
"realmDescriptionHelpText": "Beschreibung des Authentication Realm.",
|
||||
"dateFormatHelpText": "Moments Datumsformat. Zulässige Formate sind in der MomentJS Dokumentation beschrieben.",
|
||||
"pluginRepositoryHelpText": "Die URL des Plugin Repositories. Beschreibung der Platzhalter: version = SCM-Manager Version; os = Betriebssystem; arch = Architektur",
|
||||
"pluginUrlHelpText": "Die URL der Plugin Center API. Beschreibung der Platzhalter: version = SCM-Manager Version; os = Betriebssystem; arch = Architektur",
|
||||
"enableForwardingHelpText": "mod_proxy Port Weiterleitung aktivieren.",
|
||||
"enableRepositoryArchiveHelpText": "Repository Archive aktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
||||
"disableGroupingGridHelpText": "Repository Gruppen deaktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"date-format": "Date Format",
|
||||
"anonymous-access-enabled": "Anonymous Access Enabled",
|
||||
"skip-failed-authenticators": "Skip Failed Authenticators",
|
||||
"plugin-url": "Plugin URL",
|
||||
"plugin-url": "Plugin Center URL",
|
||||
"enabled-xsrf-protection": "Enabled XSRF Protection",
|
||||
"namespace-strategy": "Namespace Strategy",
|
||||
"login-info-url": "Login Info URL"
|
||||
@@ -55,7 +55,7 @@
|
||||
"help": {
|
||||
"realmDescriptionHelpText": "Enter authentication realm description.",
|
||||
"dateFormatHelpText": "Moments date format. Please have a look at the MomentJS documentation.",
|
||||
"pluginRepositoryHelpText": "The url of the plugin repository. Explanation of the placeholders: version = SCM-Manager Version; os = Operation System; arch = Architecture",
|
||||
"pluginUrlHelpText": "The url of the Plugin Center API. Explanation of the placeholders: version = SCM-Manager Version; os = Operation System; arch = Architecture",
|
||||
"enableForwardingHelpText": "Enable mod_proxy port forwarding.",
|
||||
"enableRepositoryArchiveHelpText": "Enable repository archives. A complete page reload is required after a change of this value.",
|
||||
"disableGroupingGridHelpText": "Disable repository Groups. A complete page reload is required after a change of this value.",
|
||||
|
||||
@@ -29,6 +29,7 @@ class GeneralSettings extends React.Component<Props> {
|
||||
t,
|
||||
realmDescription,
|
||||
loginInfoUrl,
|
||||
pluginUrl,
|
||||
enabledXsrfProtection,
|
||||
namespaceStrategy,
|
||||
hasUpdatePermission,
|
||||
@@ -78,6 +79,17 @@ class GeneralSettings extends React.Component<Props> {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("general-settings.plugin-url")}
|
||||
onChange={this.handlePluginCenterUrlChange}
|
||||
value={pluginUrl}
|
||||
disabled={!hasUpdatePermission}
|
||||
helpText={t("help.pluginUrlHelpText")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -85,7 +97,6 @@ class GeneralSettings extends React.Component<Props> {
|
||||
handleLoginInfoUrlChange = (value: string) => {
|
||||
this.props.onChange(true, value, "loginInfoUrl");
|
||||
};
|
||||
|
||||
handleRealmDescriptionChange = (value: string) => {
|
||||
this.props.onChange(true, value, "realmDescription");
|
||||
};
|
||||
@@ -95,6 +106,9 @@ class GeneralSettings extends React.Component<Props> {
|
||||
handleNamespaceStrategyChange = (value: string) => {
|
||||
this.props.onChange(true, value, "namespaceStrategy");
|
||||
};
|
||||
handlePluginCenterUrlChange = (value: string) => {
|
||||
this.props.onChange(true, value, "pluginUrl");
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("config")(GeneralSettings);
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {Redirect, Route, Switch} from "react-router-dom";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { History } from "history";
|
||||
import { connect } from "react-redux";
|
||||
import { compose } from "redux";
|
||||
import type { Links } from "@scm-manager/ui-types";
|
||||
import { Page, Navigation, NavLink, Section, SubNavigation } from "@scm-manager/ui-components";
|
||||
import { getLinks } from "../../modules/indexResource";
|
||||
import {
|
||||
Page,
|
||||
Navigation,
|
||||
NavLink,
|
||||
Section,
|
||||
SubNavigation
|
||||
} from "@scm-manager/ui-components";
|
||||
import {
|
||||
getLinks,
|
||||
getAvailablePluginsLink,
|
||||
getInstalledPluginsLink
|
||||
} from "../../modules/indexResource";
|
||||
import AdminDetails from "./AdminDetails";
|
||||
import PluginsOverview from "../plugins/containers/PluginsOverview";
|
||||
import GlobalConfig from "./GlobalConfig";
|
||||
@@ -18,6 +28,8 @@ import CreateRepositoryRole from "../roles/containers/CreateRepositoryRole";
|
||||
|
||||
type Props = {
|
||||
links: Links,
|
||||
availablePluginsLink: string,
|
||||
installedPluginsLink: string,
|
||||
|
||||
// context objects
|
||||
t: string => string,
|
||||
@@ -28,7 +40,7 @@ type Props = {
|
||||
class Admin extends React.Component<Props> {
|
||||
stripEndingSlash = (url: string) => {
|
||||
if (url.endsWith("/")) {
|
||||
if(url.includes("role")) {
|
||||
if (url.includes("role")) {
|
||||
return url.substring(0, url.length - 2);
|
||||
}
|
||||
return url.substring(0, url.length - 1);
|
||||
@@ -47,7 +59,7 @@ class Admin extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { links, t } = this.props;
|
||||
const { links, availablePluginsLink, installedPluginsLink, t } = this.props;
|
||||
|
||||
const url = this.matchedUrl();
|
||||
const extensionProps = {
|
||||
@@ -62,34 +74,54 @@ class Admin extends React.Component<Props> {
|
||||
<Switch>
|
||||
<Redirect exact from={url} to={`${url}/info`} />
|
||||
<Route path={`${url}/info`} exact component={AdminDetails} />
|
||||
<Route path={`${url}/settings/general`} exact component={GlobalConfig} />
|
||||
<Redirect exact from={`${url}/plugins`} to={`${url}/plugins/installed/`} />
|
||||
<Route
|
||||
path={`${url}/settings/general`}
|
||||
exact
|
||||
component={GlobalConfig}
|
||||
/>
|
||||
<Redirect
|
||||
exact
|
||||
from={`${url}/plugins`}
|
||||
to={`${url}/plugins/installed/`}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/plugins/installed`}
|
||||
exact
|
||||
render={() => (
|
||||
<PluginsOverview baseUrl={`${url}/plugins/installed`} installed={true} />
|
||||
<PluginsOverview
|
||||
baseUrl={`${url}/plugins/installed`}
|
||||
installed={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/plugins/installed/:page`}
|
||||
exact
|
||||
render={() => (
|
||||
<PluginsOverview baseUrl={`${url}/plugins/installed`} installed={true} />
|
||||
<PluginsOverview
|
||||
baseUrl={`${url}/plugins/installed`}
|
||||
installed={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/plugins/available`}
|
||||
exact
|
||||
render={() => (
|
||||
<PluginsOverview baseUrl={`${url}/plugins/available`} installed={false} />
|
||||
<PluginsOverview
|
||||
baseUrl={`${url}/plugins/available`}
|
||||
installed={false}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/plugins/available/:page`}
|
||||
exact
|
||||
render={() => (
|
||||
<PluginsOverview baseUrl={`${url}/plugins/available`} installed={false} />
|
||||
<PluginsOverview
|
||||
baseUrl={`${url}/plugins/available`}
|
||||
installed={false}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
@@ -109,17 +141,13 @@ class Admin extends React.Component<Props> {
|
||||
<Route
|
||||
path={`${url}/roles/create`}
|
||||
render={() => (
|
||||
<CreateRepositoryRole
|
||||
history={this.props.history}
|
||||
/>
|
||||
<CreateRepositoryRole history={this.props.history} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/roles/:page`}
|
||||
exact
|
||||
render={() => (
|
||||
<RepositoryRoles baseUrl={`${url}/roles`} />
|
||||
)}
|
||||
render={() => <RepositoryRoles baseUrl={`${url}/roles`} />}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="admin.route"
|
||||
@@ -136,24 +164,26 @@ class Admin extends React.Component<Props> {
|
||||
icon="fas fa-info-circle"
|
||||
label={t("admin.menu.informationNavLink")}
|
||||
/>
|
||||
{
|
||||
links.plugins &&
|
||||
{(availablePluginsLink || installedPluginsLink) && (
|
||||
<SubNavigation
|
||||
to={`${url}/plugins/`}
|
||||
icon="fas fa-puzzle-piece"
|
||||
label={t("plugins.menu.pluginsNavLink")}
|
||||
>
|
||||
{installedPluginsLink && (
|
||||
<NavLink
|
||||
to={`${url}/plugins/installed/`}
|
||||
label={t("plugins.menu.installedNavLink")}
|
||||
/>
|
||||
{/* Activate this again after available plugins page is created */}
|
||||
{/*<NavLink*/}
|
||||
{/* to={`${url}/plugins/available/`}*/}
|
||||
{/* label={t("plugins.menu.availableNavLink")}*/}
|
||||
{/*/>*/}
|
||||
)}
|
||||
{availablePluginsLink && (
|
||||
<NavLink
|
||||
to={`${url}/plugins/available/`}
|
||||
label={t("plugins.menu.availableNavLink")}
|
||||
/>
|
||||
)}
|
||||
</SubNavigation>
|
||||
}
|
||||
)}
|
||||
<NavLink
|
||||
to={`${url}/roles/`}
|
||||
icon="fas fa-user-shield"
|
||||
@@ -191,8 +221,12 @@ class Admin extends React.Component<Props> {
|
||||
|
||||
const mapStateToProps = (state: any) => {
|
||||
const links = getLinks(state);
|
||||
const availablePluginsLink = getAvailablePluginsLink(state);
|
||||
const installedPluginsLink = getInstalledPluginsLink(state);
|
||||
return {
|
||||
links
|
||||
links,
|
||||
availablePluginsLink,
|
||||
installedPluginsLink
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Plugin } from "@scm-manager/ui-types";
|
||||
import { Image } from "@scm-manager/ui-components";
|
||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
import type {Plugin} from "@scm-manager/ui-types";
|
||||
import {Image} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin
|
||||
@@ -14,7 +14,7 @@ export default class PluginAvatar extends React.Component<Props> {
|
||||
return (
|
||||
<p className="image is-64x64">
|
||||
<ExtensionPoint name="plugins.plugin-avatar" props={{ plugin }}>
|
||||
<Image src="/images/blib.jpg" alt="Logo" />
|
||||
<Image src={plugin.avatarUrl ? plugin.avatarUrl : "/images/blib.jpg"} alt="Logo" />
|
||||
</ExtensionPoint>
|
||||
</p>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Plugin } from "@scm-manager/ui-types";
|
||||
import { CardColumn } from "@scm-manager/ui-components";
|
||||
import injectSheet from "react-jss";
|
||||
import type {Plugin} from "@scm-manager/ui-types";
|
||||
import {CardColumn} from "@scm-manager/ui-components";
|
||||
import PluginAvatar from "./PluginAvatar";
|
||||
|
||||
type Props = {
|
||||
plugin: Plugin
|
||||
plugin: Plugin,
|
||||
|
||||
// context props
|
||||
classes: any
|
||||
};
|
||||
|
||||
const styles = {
|
||||
link: {
|
||||
pointerEvents: "cursor"
|
||||
}
|
||||
};
|
||||
|
||||
class PluginEntry extends React.Component<Props> {
|
||||
@@ -13,6 +23,17 @@ class PluginEntry extends React.Component<Props> {
|
||||
return <PluginAvatar plugin={plugin} />;
|
||||
};
|
||||
|
||||
createContentRight = (plugin: Plugin) => {
|
||||
const { classes } = this.props;
|
||||
if (plugin._links && plugin._links.install && plugin._links.install.href) {
|
||||
return (
|
||||
<div className={classes.link} onClick={() => console.log(plugin._links.install.href) /*TODO trigger plugin installation*/}>
|
||||
<i className="fas fa-cloud-download-alt fa-2x has-text-info" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
createFooterLeft = (plugin: Plugin) => {
|
||||
return <small className="level-item">{plugin.author}</small>;
|
||||
};
|
||||
@@ -24,6 +45,7 @@ class PluginEntry extends React.Component<Props> {
|
||||
render() {
|
||||
const { plugin } = this.props;
|
||||
const avatar = this.createAvatar(plugin);
|
||||
const contentRight = this.createContentRight(plugin);
|
||||
const footerLeft = this.createFooterLeft(plugin);
|
||||
const footerRight = this.createFooterRight(plugin);
|
||||
|
||||
@@ -32,8 +54,9 @@ class PluginEntry extends React.Component<Props> {
|
||||
<CardColumn
|
||||
link="#"
|
||||
avatar={avatar}
|
||||
title={plugin.name}
|
||||
title={plugin.displayName ? plugin.displayName : plugin.name}
|
||||
description={plugin.description}
|
||||
contentRight={contentRight}
|
||||
footerLeft={footerLeft}
|
||||
footerRight={footerRight}
|
||||
/>
|
||||
@@ -41,4 +64,4 @@ class PluginEntry extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginEntry;
|
||||
export default injectSheet(styles)(PluginEntry);
|
||||
|
||||
@@ -6,7 +6,7 @@ export default function groupByCategory(
|
||||
): PluginGroup[] {
|
||||
let groups = {};
|
||||
for (let plugin of plugins) {
|
||||
const groupName = plugin.type;
|
||||
const groupName = plugin.category;
|
||||
|
||||
let group = groups[groupName];
|
||||
if (!group) {
|
||||
|
||||
@@ -18,7 +18,10 @@ import {
|
||||
isFetchPluginsPending
|
||||
} from "../modules/plugins";
|
||||
import PluginsList from "../components/PluginsList";
|
||||
import { getPluginsLink } from "../../../modules/indexResource";
|
||||
import {
|
||||
getAvailablePluginsLink,
|
||||
getInstalledPluginsLink
|
||||
} from "../../../modules/indexResource";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
@@ -26,7 +29,8 @@ type Props = {
|
||||
collection: PluginCollection,
|
||||
baseUrl: string,
|
||||
installed: boolean,
|
||||
pluginsLink: string,
|
||||
availablePluginsLink: string,
|
||||
installedPluginsLink: string,
|
||||
|
||||
// context objects
|
||||
t: string => string,
|
||||
@@ -37,8 +41,27 @@ type Props = {
|
||||
|
||||
class PluginsOverview extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchPluginsByLink, pluginsLink } = this.props;
|
||||
fetchPluginsByLink(pluginsLink);
|
||||
const {
|
||||
installed,
|
||||
fetchPluginsByLink,
|
||||
availablePluginsLink,
|
||||
installedPluginsLink
|
||||
} = this.props;
|
||||
fetchPluginsByLink(installed ? installedPluginsLink : availablePluginsLink);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
installed,
|
||||
fetchPluginsByLink,
|
||||
availablePluginsLink,
|
||||
installedPluginsLink
|
||||
} = this.props;
|
||||
if (prevProps.installed !== installed) {
|
||||
fetchPluginsByLink(
|
||||
installed ? installedPluginsLink : availablePluginsLink
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -81,13 +104,15 @@ const mapStateToProps = state => {
|
||||
const collection = getPluginCollection(state);
|
||||
const loading = isFetchPluginsPending(state);
|
||||
const error = getFetchPluginsFailure(state);
|
||||
const pluginsLink = getPluginsLink(state);
|
||||
const availablePluginsLink = getAvailablePluginsLink(state);
|
||||
const installedPluginsLink = getInstalledPluginsLink(state);
|
||||
|
||||
return {
|
||||
collection,
|
||||
loading,
|
||||
error,
|
||||
pluginsLink
|
||||
availablePluginsLink,
|
||||
installedPluginsLink
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
|
||||
import type { Links } from "@scm-manager/ui-types";
|
||||
import {Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||
import type {Links} from "@scm-manager/ui-types";
|
||||
|
||||
import Overview from "../repos/containers/Overview";
|
||||
import Users from "../users/containers/Users";
|
||||
import Login from "../containers/Login";
|
||||
import Logout from "../containers/Logout";
|
||||
|
||||
import { ProtectedRoute } from "@scm-manager/ui-components";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import {ProtectedRoute} from "@scm-manager/ui-components";
|
||||
import {binder, ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
|
||||
import CreateUser from "../users/containers/CreateUser";
|
||||
import SingleUser from "../users/containers/SingleUser";
|
||||
|
||||
@@ -116,8 +116,12 @@ export function getUiPluginsLink(state: Object) {
|
||||
return getLink(state, "uiPlugins");
|
||||
}
|
||||
|
||||
export function getPluginsLink(state: Object) {
|
||||
return getLink(state, "plugins");
|
||||
export function getAvailablePluginsLink(state: Object) {
|
||||
return getLink(state, "availablePlugins");
|
||||
}
|
||||
|
||||
export function getInstalledPluginsLink(state: Object) {
|
||||
return getLink(state, "installedPlugins");
|
||||
}
|
||||
|
||||
export function getMeLink(state: Object) {
|
||||
|
||||
@@ -1,33 +1,20 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
fetchRepoByName,
|
||||
getFetchRepoFailure,
|
||||
getRepository,
|
||||
isFetchRepoPending
|
||||
} from "../modules/repos";
|
||||
import {fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import {Redirect, Route, Switch} from "react-router-dom";
|
||||
import type {Repository} from "@scm-manager/ui-types";
|
||||
|
||||
import {
|
||||
Loading,
|
||||
Navigation,
|
||||
SubNavigation,
|
||||
NavLink,
|
||||
Page,
|
||||
Section,
|
||||
ErrorPage
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import {ErrorPage, Loading, Navigation, NavLink, Page, Section, SubNavigation} from "@scm-manager/ui-components";
|
||||
import {translate} from "react-i18next";
|
||||
import RepositoryDetails from "../components/RepositoryDetails";
|
||||
import EditRepo from "./EditRepo";
|
||||
import BranchesOverview from "../branches/containers/BranchesOverview";
|
||||
import CreateBranch from "../branches/containers/CreateBranch";
|
||||
import Permissions from "../permissions/containers/Permissions";
|
||||
|
||||
import type { History } from "history";
|
||||
import type {History} from "history";
|
||||
import EditRepoNavLink from "../components/EditRepoNavLink";
|
||||
import BranchRoot from "../branches/containers/BranchRoot";
|
||||
import ChangesetsRoot from "./ChangesetsRoot";
|
||||
@@ -35,8 +22,8 @@ import ChangesetView from "./ChangesetView";
|
||||
import PermissionsNavLink from "../components/PermissionsNavLink";
|
||||
import Sources from "../sources/containers/Sources";
|
||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||
import { getLinks, getRepositoriesLink } from "../../modules/indexResource";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import {getLinks, getRepositoriesLink} from "../../modules/indexResource";
|
||||
import {binder, ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
namespace: string,
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type {
|
||||
RepositoryRole,
|
||||
PermissionCollection,
|
||||
PermissionCreateEntry,
|
||||
SelectValue
|
||||
} from "@scm-manager/ui-types";
|
||||
import {translate} from "react-i18next";
|
||||
import type {PermissionCollection, PermissionCreateEntry, RepositoryRole, SelectValue} from "@scm-manager/ui-types";
|
||||
import {
|
||||
Subtitle,
|
||||
SubmitButton,
|
||||
Button,
|
||||
GroupAutocomplete,
|
||||
LabelWithHelpIcon,
|
||||
Radio,
|
||||
GroupAutocomplete,
|
||||
SubmitButton,
|
||||
Subtitle,
|
||||
UserAutocomplete
|
||||
} from "@scm-manager/ui-components";
|
||||
import * as validator from "../components/permissionValidation";
|
||||
import RoleSelector from "../components/RoleSelector";
|
||||
import AdvancedPermissionsDialog from "./AdvancedPermissionsDialog";
|
||||
import { findVerbsForRole } from "../modules/permissions";
|
||||
import {findVerbsForRole} from "../modules/permissions";
|
||||
|
||||
type Props = {
|
||||
availableRoles: RepositoryRole[],
|
||||
|
||||
@@ -1,44 +1,34 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import {connect} from "react-redux";
|
||||
import {translate} from "react-i18next";
|
||||
import {
|
||||
createPermission,
|
||||
createPermissionReset,
|
||||
deletePermissionReset,
|
||||
fetchAvailablePermissionsIfNeeded,
|
||||
fetchPermissions,
|
||||
getFetchAvailablePermissionsFailure,
|
||||
getAvailablePermissions,
|
||||
getAvailableRepositoryRoles,
|
||||
getAvailableRepositoryVerbs,
|
||||
getCreatePermissionFailure,
|
||||
getDeletePermissionsFailure,
|
||||
getFetchAvailablePermissionsFailure,
|
||||
getFetchPermissionsFailure,
|
||||
isFetchAvailablePermissionsPending,
|
||||
isFetchPermissionsPending,
|
||||
getModifyPermissionsFailure,
|
||||
getPermissionsOfRepo,
|
||||
hasCreatePermission,
|
||||
createPermission,
|
||||
isCreatePermissionPending,
|
||||
getCreatePermissionFailure,
|
||||
createPermissionReset,
|
||||
getDeletePermissionsFailure,
|
||||
getModifyPermissionsFailure,
|
||||
modifyPermissionReset,
|
||||
deletePermissionReset,
|
||||
getAvailableRepositoryRoles,
|
||||
getAvailableRepositoryVerbs
|
||||
isFetchAvailablePermissionsPending,
|
||||
isFetchPermissionsPending,
|
||||
modifyPermissionReset
|
||||
} from "../modules/permissions";
|
||||
import {
|
||||
Loading,
|
||||
ErrorPage,
|
||||
Subtitle,
|
||||
LabelWithHelpIcon
|
||||
} from "@scm-manager/ui-components";
|
||||
import type {
|
||||
Permission,
|
||||
PermissionCollection,
|
||||
PermissionCreateEntry,
|
||||
RepositoryRole
|
||||
} from "@scm-manager/ui-types";
|
||||
import {ErrorPage, LabelWithHelpIcon, Loading, Subtitle} from "@scm-manager/ui-components";
|
||||
import type {Permission, PermissionCollection, PermissionCreateEntry, RepositoryRole} from "@scm-manager/ui-types";
|
||||
import SinglePermission from "./SinglePermission";
|
||||
import CreatePermissionForm from "./CreatePermissionForm";
|
||||
import type { History } from "history";
|
||||
import { getPermissionsLink } from "../../modules/repos";
|
||||
import type {History} from "history";
|
||||
import {getPermissionsLink} from "../../modules/repos";
|
||||
import {
|
||||
getGroupAutoCompleteLink,
|
||||
getRepositoryRolesLink,
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import type {Branch, Repository} from "@scm-manager/ui-types";
|
||||
import FileTree from "../components/FileTree";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
BranchSelector,
|
||||
Breadcrumb
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import {BranchSelector, Breadcrumb, ErrorNotification, Loading} from "@scm-manager/ui-components";
|
||||
import {translate} from "react-i18next";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "../../branches/modules/branches";
|
||||
import { compose } from "redux";
|
||||
import {compose} from "redux";
|
||||
import Content from "./Content";
|
||||
import { fetchSources, isDirectory } from "../modules/sources";
|
||||
import {fetchSources, isDirectory} from "../modules/sources";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
|
||||
@@ -464,32 +464,28 @@
|
||||
<groupId>sonia.scm.maven</groupId>
|
||||
<artifactId>smp-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<smpArtifacts>
|
||||
<artifact>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
<artifactId>scm-hg-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>smp</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
<artifactId>scm-svn-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>smp</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
<artifactId>scm-git-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>smp</type>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
</artifact>
|
||||
<artifact>
|
||||
<groupId>sonia.scm.plugins</groupId>
|
||||
<artifactId>scm-legacy-plugin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>smp</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</artifact>
|
||||
</smpArtifacts>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
import sonia.scm.plugin.Plugin;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginManager;
|
||||
import sonia.scm.plugin.PluginPermissions;
|
||||
import sonia.scm.plugin.PluginState;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
|
||||
public class AvailablePluginResource {
|
||||
|
||||
private final PluginDtoCollectionMapper collectionMapper;
|
||||
private final PluginManager pluginManager;
|
||||
private final PluginDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginManager pluginManager, PluginDtoMapper mapper) {
|
||||
this.collectionMapper = collectionMapper;
|
||||
this.pluginManager = pluginManager;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of available plugins.
|
||||
*
|
||||
* @return collection of available plugins.
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(CollectionDto.class)
|
||||
@Produces(VndMediaType.PLUGIN_COLLECTION)
|
||||
public Response getAvailablePlugins() {
|
||||
PluginPermissions.read().check();
|
||||
Collection<PluginInformation> plugins = pluginManager.getAvailable()
|
||||
.stream()
|
||||
.filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE))
|
||||
.collect(Collectors.toList());
|
||||
return Response.ok(collectionMapper.map(plugins)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns available plugin.
|
||||
*
|
||||
* @return available plugin.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{name}/{version}")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 404, condition = "not found"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@TypeHint(PluginDto.class)
|
||||
@Produces(VndMediaType.PLUGIN)
|
||||
public Response getAvailablePlugin(@PathParam("name") String name, @PathParam("version") String version) {
|
||||
PluginPermissions.read().check();
|
||||
Optional<PluginInformation> plugin = pluginManager.getAvailable()
|
||||
.stream()
|
||||
.filter(p -> p.getId().equals(name + ":" + version))
|
||||
.findFirst();
|
||||
if (plugin.isPresent()) {
|
||||
return Response.ok(mapper.map(plugin.get())).build();
|
||||
} else {
|
||||
throw notFound(entity(Plugin.class, name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers plugin installation.
|
||||
* @param name plugin artefact name
|
||||
* @param version plugin version
|
||||
* @return HTTP Status.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{name}/{version}/install")
|
||||
@Consumes(VndMediaType.PLUGIN)
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response installPlugin(@PathParam("name") String name, @PathParam("version") String version) {
|
||||
PluginPermissions.manage().check();
|
||||
pluginManager.install(name + ":" + version);
|
||||
return Response.ok().build();
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@ public class IndexDtoGenerator extends HalAppenderMapper {
|
||||
link("logout", resourceLinks.authentication().logout())
|
||||
);
|
||||
if (PluginPermissions.read().isPermitted()) {
|
||||
builder.single(link("plugins", resourceLinks.pluginCollection().self()));
|
||||
builder.single(link("installedPlugins", resourceLinks.installedPluginCollection().self()));
|
||||
builder.single(link("availablePlugins", resourceLinks.availablePluginCollection().self()));
|
||||
}
|
||||
if (UserPermissions.list().isPermitted()) {
|
||||
builder.single(link("users", resourceLinks.userCollection().self()));
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
import sonia.scm.plugin.Plugin;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.PluginManager;
|
||||
import sonia.scm.plugin.PluginPermissions;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
@@ -22,17 +23,19 @@ import java.util.Optional;
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
|
||||
public class PluginResource {
|
||||
public class InstalledPluginResource {
|
||||
|
||||
private final PluginLoader pluginLoader;
|
||||
private final PluginDtoCollectionMapper collectionMapper;
|
||||
private final PluginDtoMapper mapper;
|
||||
private final PluginManager pluginManager;
|
||||
|
||||
@Inject
|
||||
public PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper) {
|
||||
public InstalledPluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) {
|
||||
this.pluginLoader = pluginLoader;
|
||||
this.collectionMapper = collectionMapper;
|
||||
this.mapper = mapper;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,12 +60,12 @@ public class PluginResource {
|
||||
/**
|
||||
* Returns the installed plugin with the given id.
|
||||
*
|
||||
* @param id id of plugin
|
||||
* @param name name of plugin
|
||||
*
|
||||
* @return installed plugin with specified id
|
||||
*/
|
||||
@GET
|
||||
@Path("{id}")
|
||||
@Path("/{name}")
|
||||
@StatusCodes({
|
||||
@ResponseCode(code = 200, condition = "success"),
|
||||
@ResponseCode(code = 404, condition = "not found"),
|
||||
@@ -70,18 +73,17 @@ public class PluginResource {
|
||||
})
|
||||
@TypeHint(PluginDto.class)
|
||||
@Produces(VndMediaType.PLUGIN)
|
||||
public Response getInstalledPlugin(@PathParam("id") String id) {
|
||||
public Response getInstalledPlugin(@PathParam("name") String name) {
|
||||
PluginPermissions.read().check();
|
||||
Optional<PluginDto> pluginDto = pluginLoader.getInstalledPlugins()
|
||||
.stream()
|
||||
.filter(plugin -> id.equals(plugin.getPlugin().getInformation().getId(false)))
|
||||
.filter(plugin -> name.equals(plugin.getPlugin().getInformation().getName()))
|
||||
.map(mapper::map)
|
||||
.findFirst();
|
||||
if (pluginDto.isPresent()) {
|
||||
return Response.ok(pluginDto.get()).build();
|
||||
} else {
|
||||
throw notFound(entity(Plugin.class, id));
|
||||
throw notFound(entity(Plugin.class, name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,5 +54,7 @@ public class MapperModule extends AbstractModule {
|
||||
bind(UIPluginDtoCollectionMapper.class);
|
||||
|
||||
bind(ScmPathInfoStore.class).in(ServletScopes.REQUEST);
|
||||
|
||||
bind(PluginDtoMapper.class).to(Mappers.getMapper(PluginDtoMapper.class).getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@@ -13,10 +12,12 @@ import lombok.Setter;
|
||||
public class PluginDto extends HalRepresentation {
|
||||
|
||||
private String name;
|
||||
private String type;
|
||||
private String version;
|
||||
private String author;
|
||||
private String displayName;
|
||||
private String description;
|
||||
private String author;
|
||||
private String category;
|
||||
private String avatarUrl;
|
||||
|
||||
public PluginDto(Links links) {
|
||||
add(links);
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.google.inject.Inject;
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -24,13 +25,26 @@ public class PluginDtoCollectionMapper {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public HalRepresentation map(Collection<PluginWrapper> plugins) {
|
||||
public HalRepresentation map(List<PluginWrapper> plugins) {
|
||||
List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList());
|
||||
return new HalRepresentation(createLinks(), embedDtos(dtos));
|
||||
return new HalRepresentation(createInstalledPluginsLinks(), embedDtos(dtos));
|
||||
}
|
||||
|
||||
private Links createLinks() {
|
||||
String baseUrl = resourceLinks.pluginCollection().self();
|
||||
public HalRepresentation map(Collection<PluginInformation> plugins) {
|
||||
List<PluginDto> dtos = plugins.stream().map(mapper::map).collect(toList());
|
||||
return new HalRepresentation(createAvailablePluginsLinks(), embedDtos(dtos));
|
||||
}
|
||||
|
||||
private Links createInstalledPluginsLinks() {
|
||||
String baseUrl = resourceLinks.installedPluginCollection().self();
|
||||
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.with(Links.linkingTo().self(baseUrl).build());
|
||||
return linksBuilder.build();
|
||||
}
|
||||
|
||||
private Links createAvailablePluginsLinks() {
|
||||
String baseUrl = resourceLinks.availablePluginCollection().self();
|
||||
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.with(Links.linkingTo().self(baseUrl).build());
|
||||
|
||||
@@ -1,32 +1,54 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.AfterMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import org.mapstruct.ObjectFactory;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginState;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
public class PluginDtoMapper {
|
||||
|
||||
private final ResourceLinks resourceLinks;
|
||||
@Mapper
|
||||
public abstract class PluginDtoMapper {
|
||||
|
||||
@Inject
|
||||
public PluginDtoMapper(ResourceLinks resourceLinks) {
|
||||
this.resourceLinks = resourceLinks;
|
||||
}
|
||||
private ResourceLinks resourceLinks;
|
||||
|
||||
public PluginDto map(PluginWrapper plugin) {
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.plugin()
|
||||
.self(plugin.getPlugin().getInformation().getId(false)));
|
||||
return map(plugin.getPlugin().getInformation());
|
||||
}
|
||||
|
||||
PluginDto pluginDto = new PluginDto(linksBuilder.build());
|
||||
pluginDto.setName(plugin.getPlugin().getInformation().getName());
|
||||
pluginDto.setType(plugin.getPlugin().getInformation().getCategory() != null ? plugin.getPlugin().getInformation().getCategory() : "Miscellaneous");
|
||||
pluginDto.setVersion(plugin.getPlugin().getInformation().getVersion());
|
||||
pluginDto.setAuthor(plugin.getPlugin().getInformation().getAuthor());
|
||||
pluginDto.setDescription(plugin.getPlugin().getInformation().getDescription());
|
||||
public abstract PluginDto map(PluginInformation plugin);
|
||||
|
||||
return pluginDto;
|
||||
@AfterMapping
|
||||
protected void appendCategory(@MappingTarget PluginDto dto) {
|
||||
if (dto.getCategory() == null) {
|
||||
dto.setCategory("Miscellaneous");
|
||||
}
|
||||
}
|
||||
|
||||
@ObjectFactory
|
||||
public PluginDto createDto(PluginInformation pluginInformation) {
|
||||
Links.Builder linksBuilder;
|
||||
if (pluginInformation.getState() != null && pluginInformation.getState().equals(PluginState.AVAILABLE)) {
|
||||
linksBuilder = linkingTo()
|
||||
.self(resourceLinks.availablePlugin()
|
||||
.self(pluginInformation.getName(), pluginInformation.getVersion()));
|
||||
|
||||
linksBuilder.single(link("install", resourceLinks.availablePlugin().install(pluginInformation.getName(), pluginInformation.getVersion())));
|
||||
}
|
||||
else {
|
||||
linksBuilder = linkingTo()
|
||||
.self(resourceLinks.installedPlugin()
|
||||
.self(pluginInformation.getName()));
|
||||
}
|
||||
|
||||
return new PluginDto(linksBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,23 @@ import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.Path;
|
||||
|
||||
@Path("v2/")
|
||||
@Path("v2/plugins")
|
||||
public class PluginRootResource {
|
||||
|
||||
private Provider<PluginResource> pluginResourceProvider;
|
||||
private Provider<InstalledPluginResource> installedPluginResourceProvider;
|
||||
private Provider<AvailablePluginResource> availablePluginResourceProvider;
|
||||
|
||||
@Inject
|
||||
public PluginRootResource(Provider<PluginResource> pluginResourceProvider) {
|
||||
this.pluginResourceProvider = pluginResourceProvider;
|
||||
public PluginRootResource(Provider<InstalledPluginResource> installedPluginResourceProvider, Provider<AvailablePluginResource> availablePluginResourceProvider) {
|
||||
this.installedPluginResourceProvider = installedPluginResourceProvider;
|
||||
this.availablePluginResourceProvider = availablePluginResourceProvider;
|
||||
}
|
||||
|
||||
@Path("plugins")
|
||||
public PluginResource plugins() {
|
||||
return pluginResourceProvider.get();
|
||||
@Path("/installed")
|
||||
public InstalledPluginResource installedPlugins() {
|
||||
return installedPluginResourceProvider.get();
|
||||
}
|
||||
|
||||
@Path("/available")
|
||||
public AvailablePluginResource availablePlugins() { return availablePluginResourceProvider.get(); }
|
||||
}
|
||||
|
||||
@@ -651,35 +651,71 @@ class ResourceLinks {
|
||||
}
|
||||
}
|
||||
|
||||
public PluginLinks plugin() {
|
||||
return new PluginLinks(scmPathInfoStore.get());
|
||||
public InstalledPluginLinks installedPlugin() {
|
||||
return new InstalledPluginLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class PluginLinks {
|
||||
private final LinkBuilder pluginLinkBuilder;
|
||||
static class InstalledPluginLinks {
|
||||
private final LinkBuilder installedPluginLinkBuilder;
|
||||
|
||||
PluginLinks(ScmPathInfo pathInfo) {
|
||||
pluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, PluginResource.class);
|
||||
InstalledPluginLinks(ScmPathInfo pathInfo) {
|
||||
installedPluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, InstalledPluginResource.class);
|
||||
}
|
||||
|
||||
String self(String id) {
|
||||
return pluginLinkBuilder.method("plugins").parameters().method("getInstalledPlugin").parameters(id).href();
|
||||
return installedPluginLinkBuilder.method("installedPlugins").parameters().method("getInstalledPlugin").parameters(id).href();
|
||||
}
|
||||
}
|
||||
|
||||
public PluginCollectionLinks pluginCollection() {
|
||||
return new PluginCollectionLinks(scmPathInfoStore.get());
|
||||
public InstalledPluginCollectionLinks installedPluginCollection() {
|
||||
return new InstalledPluginCollectionLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class PluginCollectionLinks {
|
||||
private final LinkBuilder pluginCollectionLinkBuilder;
|
||||
static class InstalledPluginCollectionLinks {
|
||||
private final LinkBuilder installedPluginCollectionLinkBuilder;
|
||||
|
||||
PluginCollectionLinks(ScmPathInfo pathInfo) {
|
||||
pluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, PluginResource.class);
|
||||
InstalledPluginCollectionLinks(ScmPathInfo pathInfo) {
|
||||
installedPluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, InstalledPluginResource.class);
|
||||
}
|
||||
|
||||
String self() {
|
||||
return pluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href();
|
||||
return installedPluginCollectionLinkBuilder.method("installedPlugins").parameters().method("getInstalledPlugins").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
public AvailablePluginLinks availablePlugin() {
|
||||
return new AvailablePluginLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class AvailablePluginLinks {
|
||||
private final LinkBuilder availablePluginLinkBuilder;
|
||||
|
||||
AvailablePluginLinks(ScmPathInfo pathInfo) {
|
||||
availablePluginLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, AvailablePluginResource.class);
|
||||
}
|
||||
|
||||
String self(String name, String version) {
|
||||
return availablePluginLinkBuilder.method("availablePlugins").parameters().method("getAvailablePlugin").parameters(name, version).href();
|
||||
}
|
||||
|
||||
String install(String name, String version) {
|
||||
return availablePluginLinkBuilder.method("availablePlugins").parameters().method("installPlugin").parameters(name, version).href();
|
||||
}
|
||||
}
|
||||
|
||||
public AvailablePluginCollectionLinks availablePluginCollection() {
|
||||
return new AvailablePluginCollectionLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class AvailablePluginCollectionLinks {
|
||||
private final LinkBuilder availablePluginCollectionLinkBuilder;
|
||||
|
||||
AvailablePluginCollectionLinks(ScmPathInfo pathInfo) {
|
||||
availablePluginCollectionLinkBuilder = new LinkBuilder(pathInfo, PluginRootResource.class, AvailablePluginResource.class);
|
||||
}
|
||||
|
||||
String self() {
|
||||
return availablePluginCollectionLinkBuilder.method("availablePlugins").parameters().method("getAvailablePlugins").parameters().href();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,8 @@ import javax.xml.bind.JAXB;
|
||||
|
||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||
|
||||
import static sonia.scm.plugin.PluginCenterDtoMapper.*;
|
||||
|
||||
/**
|
||||
* TODO replace aether stuff.
|
||||
* TODO check AdvancedPluginConfiguration from 1.x
|
||||
@@ -99,7 +101,7 @@ public class DefaultPluginManager implements PluginManager
|
||||
LoggerFactory.getLogger(DefaultPluginManager.class);
|
||||
|
||||
/** enable or disable remote plugins */
|
||||
private static final boolean REMOTE_PLUGINS_ENABLED = false;
|
||||
private static final boolean REMOTE_PLUGINS_ENABLED = true;
|
||||
|
||||
/** Field description */
|
||||
public static final Predicate<PluginInformation> FILTER_UPDATES =
|
||||
@@ -181,8 +183,6 @@ public class DefaultPluginManager implements PluginManager
|
||||
|
||||
PluginCenter center = getPluginCenter();
|
||||
|
||||
// pluginHandler.install(id);
|
||||
|
||||
for (PluginInformation plugin : center.getPlugins())
|
||||
{
|
||||
String pluginId = plugin.getId();
|
||||
@@ -309,14 +309,12 @@ public class DefaultPluginManager implements PluginManager
|
||||
PluginPermissions.manage().check();
|
||||
|
||||
String[] idParts = id.split(":");
|
||||
String groupId = idParts[0];
|
||||
String artefactId = idParts[1];
|
||||
String name = idParts[0];
|
||||
PluginInformation installed = null;
|
||||
|
||||
for (PluginInformation info : getInstalled())
|
||||
{
|
||||
if (groupId.equals(info.getGroupId())
|
||||
&& artefactId.equals(info.getArtifactId()))
|
||||
if (name.equals(info.getName()))
|
||||
{
|
||||
installed = info;
|
||||
|
||||
@@ -326,9 +324,9 @@ public class DefaultPluginManager implements PluginManager
|
||||
|
||||
if (installed == null)
|
||||
{
|
||||
StringBuilder msg = new StringBuilder(groupId);
|
||||
StringBuilder msg = new StringBuilder(name);
|
||||
|
||||
msg.append(":").append(groupId).append(" is not install");
|
||||
msg.append(" is not install");
|
||||
|
||||
throw new PluginNotInstalledException(msg.toString());
|
||||
}
|
||||
@@ -423,7 +421,7 @@ public class DefaultPluginManager implements PluginManager
|
||||
|
||||
for (PluginInformation info : centerPlugins)
|
||||
{
|
||||
if (!installedPlugins.containsKey(info.getId()))
|
||||
if (!installedPlugins.containsKey(info.getName()))
|
||||
{
|
||||
availablePlugins.add(info);
|
||||
}
|
||||
@@ -596,48 +594,28 @@ public class DefaultPluginManager implements PluginManager
|
||||
{
|
||||
synchronized (DefaultPluginManager.class)
|
||||
{
|
||||
String pluginUrl = configuration.getPluginUrl();
|
||||
String pluginUrl = buildPluginUrl(configuration.getPluginUrl());
|
||||
logger.info("fetch plugin information from {}", pluginUrl);
|
||||
|
||||
pluginUrl = buildPluginUrl(pluginUrl);
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("fetch plugin informations from {}", pluginUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* remote plugins are disabled for early 2.0.0-SNAPSHOTS
|
||||
* TODO enable remote plugins later
|
||||
*/
|
||||
if (REMOTE_PLUGINS_ENABLED && Util.isNotEmpty(pluginUrl))
|
||||
{
|
||||
try
|
||||
{
|
||||
center = httpClient.get(pluginUrl).request().contentFromXml(PluginCenter.class);
|
||||
center = new PluginCenter();
|
||||
PluginCenterDto pluginCenterDto = httpClient.get(pluginUrl).request().contentFromJson(PluginCenterDto.class);
|
||||
Set<PluginInformation> pluginInformationSet = map(pluginCenterDto.getEmbedded().getPlugins());
|
||||
center.setPlugins(pluginInformationSet);
|
||||
preparePlugins(center);
|
||||
cache.put(PluginCenter.class.getName(), center);
|
||||
|
||||
/*
|
||||
* if (pluginHandler == null)
|
||||
* {
|
||||
* pluginHandler = new AetherPluginHandler(this,
|
||||
* SCMContext.getContext(), configuration,
|
||||
* advancedPluginConfiguration);
|
||||
* }
|
||||
*
|
||||
* pluginHandler.setPluginRepositories(center.getRepositories());
|
||||
*/
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not load plugins from plugin center", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (center == null)
|
||||
{
|
||||
center = new PluginCenter();
|
||||
}
|
||||
if(center == null) {
|
||||
center = new PluginCenter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,8 +697,7 @@ public class DefaultPluginManager implements PluginManager
|
||||
*/
|
||||
private boolean isSamePlugin(PluginInformation p1, PluginInformation p2)
|
||||
{
|
||||
return p1.getGroupId().equals(p2.getGroupId())
|
||||
&& p1.getArtifactId().equals(p2.getArtifactId());
|
||||
return p1.getName().equals(p2.getName());
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
@@ -115,8 +115,8 @@ public final class ExplodedSmp implements Comparable<ExplodedSmp>
|
||||
}
|
||||
else
|
||||
{
|
||||
String id = plugin.getInformation().getId(false);
|
||||
String oid = o.plugin.getInformation().getId(false);
|
||||
String id = plugin.getInformation().getName(false);
|
||||
String oid = o.plugin.getInformation().getName(false);
|
||||
|
||||
if (depends.contains(oid) && odepends.contains(id))
|
||||
{
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package sonia.scm.plugin;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@XmlRootElement
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public final class PluginCenterDto implements Serializable {
|
||||
|
||||
@XmlElement(name = "_embedded")
|
||||
private Embedded embedded;
|
||||
|
||||
public Embedded getEmbedded() {
|
||||
return embedded;
|
||||
}
|
||||
|
||||
@XmlRootElement(name = "_embedded")
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class Embedded {
|
||||
|
||||
@XmlElement(name = "plugins")
|
||||
private List<Plugin> plugins;
|
||||
|
||||
public List<Plugin> getPlugins() {
|
||||
if (plugins == null) {
|
||||
plugins = ImmutableList.of();
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "plugins")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Plugin {
|
||||
|
||||
private String name;
|
||||
private String version;
|
||||
private String displayName;
|
||||
private String description;
|
||||
private String category;
|
||||
private String author;
|
||||
private String avatarUrl;
|
||||
private String sha256;
|
||||
|
||||
@XmlElement(name = "conditions")
|
||||
private Condition conditions;
|
||||
|
||||
@XmlElement(name = "dependecies")
|
||||
private Dependency dependencies;
|
||||
|
||||
@XmlElement(name = "_links")
|
||||
private Map<String, Link> links;
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "conditions")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public static class Condition {
|
||||
|
||||
private List<String> os;
|
||||
private String arch;
|
||||
private String minVersion;
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@XmlRootElement(name = "dependencies")
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
static class Dependency {
|
||||
private String name;
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Getter
|
||||
static class Link {
|
||||
private String href;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package sonia.scm.plugin;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface PluginCenterDtoMapper {
|
||||
|
||||
@Mapping(source = "conditions", target = "condition")
|
||||
PluginInformation map(PluginCenterDto.Plugin plugin);
|
||||
|
||||
PluginCondition map(PluginCenterDto.Condition condition);
|
||||
|
||||
static Set<PluginInformation> map(List<PluginCenterDto.Plugin> dtos) {
|
||||
PluginCenterDtoMapper mapper = Mappers.getMapper(PluginCenterDtoMapper.class);
|
||||
Set<PluginInformation> plugins = new HashSet<>();
|
||||
for (PluginCenterDto.Plugin plugin : dtos) {
|
||||
plugins.add(mapper.map(plugin));
|
||||
}
|
||||
return plugins;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ public final class PluginNode
|
||||
*/
|
||||
public String getId()
|
||||
{
|
||||
return plugin.getPlugin().getInformation().getId(false);
|
||||
return plugin.getPlugin().getInformation().getName(false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -318,10 +318,7 @@ public final class PluginProcessor
|
||||
{
|
||||
for (Path parent : parentStream)
|
||||
{
|
||||
try (DirectoryStream<Path> direcotries = stream(parent, filter))
|
||||
{
|
||||
paths.addAll(direcotries);
|
||||
}
|
||||
paths.add(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +330,6 @@ public final class PluginProcessor
|
||||
*
|
||||
*
|
||||
* @param parentClassLoader
|
||||
* @param directory
|
||||
* @param smp
|
||||
*
|
||||
* @return
|
||||
@@ -377,7 +373,7 @@ public final class PluginProcessor
|
||||
URL[] urlArray = urls.toArray(new URL[urls.size()]);
|
||||
Plugin plugin = smp.getPlugin();
|
||||
|
||||
String id = plugin.getInformation().getId(false);
|
||||
String id = plugin.getInformation().getName(false);
|
||||
|
||||
if (smp.getPlugin().isChildFirstClassLoader())
|
||||
{
|
||||
@@ -472,7 +468,6 @@ public final class PluginProcessor
|
||||
*
|
||||
*
|
||||
* @param classLoader
|
||||
* @param directory
|
||||
* @param smp
|
||||
*
|
||||
* @return
|
||||
@@ -511,7 +506,6 @@ public final class PluginProcessor
|
||||
*
|
||||
*
|
||||
* @param classLoader
|
||||
* @param smps
|
||||
* @param rootNodes
|
||||
*
|
||||
* @return
|
||||
|
||||
@@ -109,7 +109,7 @@ public final class PluginsInternal
|
||||
{
|
||||
PluginInformation info = plugin.getInformation();
|
||||
|
||||
return new File(new File(parent, info.getGroupId()), info.getArtifactId());
|
||||
return new File(parent, info.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,14 +131,14 @@ public final class PluginsInternal
|
||||
if (directory.exists())
|
||||
{
|
||||
logger.debug("delete directory {} for plugin extraction",
|
||||
archive.getPlugin().getInformation().getId(false));
|
||||
archive.getPlugin().getInformation().getName(false));
|
||||
IOUtil.delete(directory);
|
||||
}
|
||||
|
||||
IOUtil.mkdirs(directory);
|
||||
|
||||
logger.debug("extract plugin {}",
|
||||
archive.getPlugin().getInformation().getId(false));
|
||||
archive.getPlugin().getInformation().getName(false));
|
||||
archive.extract(directory);
|
||||
Files.write(checksum, checksumFile, Charsets.UTF_8);
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.lifecycle.RestartEvent;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.update.repository.MigrationStrategy;
|
||||
import sonia.scm.lifecycle.RestartEvent;
|
||||
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
|
||||
import sonia.scm.update.repository.MigrationStrategy;
|
||||
import sonia.scm.update.repository.V1Repository;
|
||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||
import sonia.scm.util.ValidationUtil;
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.jboss.resteasy.spi.UnhandledException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginManager;
|
||||
import sonia.scm.plugin.PluginState;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AvailablePluginResourceTest {
|
||||
|
||||
private Dispatcher dispatcher;
|
||||
|
||||
@Mock
|
||||
Provider<InstalledPluginResource> installedPluginResourceProvider;
|
||||
|
||||
@Mock
|
||||
Provider<AvailablePluginResource> availablePluginResourceProvider;
|
||||
|
||||
@Mock
|
||||
private PluginDtoCollectionMapper collectionMapper;
|
||||
|
||||
@Mock
|
||||
private PluginManager pluginManager;
|
||||
|
||||
@Mock
|
||||
private PluginDtoMapper mapper;
|
||||
|
||||
@InjectMocks
|
||||
AvailablePluginResource availablePluginResource;
|
||||
|
||||
PluginRootResource pluginRootResource;
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void prepareEnvironment() {
|
||||
dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
pluginRootResource = new PluginRootResource(installedPluginResourceProvider, availablePluginResourceProvider);
|
||||
when(availablePluginResourceProvider.get()).thenReturn(availablePluginResource);
|
||||
dispatcher.getRegistry().addSingletonResource(pluginRootResource);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class withAuthorization {
|
||||
|
||||
@BeforeEach
|
||||
void bindSubject() {
|
||||
ThreadContext.bind(subject);
|
||||
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void unbindSubject() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAvailablePlugins() throws URISyntaxException, UnsupportedEncodingException {
|
||||
PluginInformation pluginInformation = new PluginInformation();
|
||||
pluginInformation.setState(PluginState.AVAILABLE);
|
||||
when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation));
|
||||
when(collectionMapper.map(Collections.singletonList(pluginInformation))).thenReturn(new MockedResultDto());
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available");
|
||||
request.accept(VndMediaType.PLUGIN_COLLECTION);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus());
|
||||
assertThat(response.getContentAsString()).contains("\"marker\":\"x\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAvailablePlugin() throws UnsupportedEncodingException, URISyntaxException {
|
||||
PluginInformation pluginInformation = new PluginInformation();
|
||||
pluginInformation.setState(PluginState.AVAILABLE);
|
||||
pluginInformation.setName("pluginName");
|
||||
pluginInformation.setVersion("2.0.0");
|
||||
when(pluginManager.getAvailable()).thenReturn(Collections.singletonList(pluginInformation));
|
||||
|
||||
PluginDto pluginDto = new PluginDto();
|
||||
pluginDto.setName("pluginName");
|
||||
when(mapper.map(pluginInformation)).thenReturn(pluginDto);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus());
|
||||
assertThat(response.getContentAsString()).contains("\"name\":\"pluginName\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void installPlugin() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
verify(pluginManager).install("pluginName:2.0.0");
|
||||
assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithoutAuthorization {
|
||||
|
||||
@BeforeEach
|
||||
void unbindSubject() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotGetAvailablePluginsIfMissingPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available");
|
||||
request.accept(VndMediaType.PLUGIN_COLLECTION);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotGetAvailablePluginIfMissingPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available/pluginName/2.0.0");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotInstallPluginIfMissingPermission() throws URISyntaxException {
|
||||
ThreadContext.unbindSubject();
|
||||
MockHttpRequest request = MockHttpRequest.post("/v2/plugins/available/pluginName/2.0.0/install");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response));
|
||||
}
|
||||
}
|
||||
|
||||
public class MockedResultDto extends HalRepresentation {
|
||||
public String getMarker() {
|
||||
return "x";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.jboss.resteasy.spi.UnhandledException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.plugin.Plugin;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.PluginState;
|
||||
import sonia.scm.plugin.PluginWrapper;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Provider;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InstalledPluginResourceTest {
|
||||
|
||||
private Dispatcher dispatcher;
|
||||
|
||||
@Mock
|
||||
Provider<InstalledPluginResource> installedPluginResourceProvider;
|
||||
|
||||
@Mock
|
||||
Provider<AvailablePluginResource> availablePluginResourceProvider;
|
||||
|
||||
@Mock
|
||||
private PluginLoader pluginLoader;
|
||||
|
||||
@Mock
|
||||
private PluginDtoCollectionMapper collectionMapper;
|
||||
|
||||
@Mock
|
||||
private PluginDtoMapper mapper;
|
||||
|
||||
@InjectMocks
|
||||
InstalledPluginResource installedPluginResource;
|
||||
|
||||
PluginRootResource pluginRootResource;
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
|
||||
@BeforeEach
|
||||
void prepareEnvironment() {
|
||||
dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
pluginRootResource = new PluginRootResource(installedPluginResourceProvider, availablePluginResourceProvider);
|
||||
when(installedPluginResourceProvider.get()).thenReturn(installedPluginResource);
|
||||
dispatcher.getRegistry().addSingletonResource(pluginRootResource);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class withAuthorization {
|
||||
|
||||
@BeforeEach
|
||||
void bindSubject() {
|
||||
ThreadContext.bind(subject);
|
||||
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void unbindSubject() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException {
|
||||
PluginWrapper pluginWrapper = new PluginWrapper(null, null, null, null);
|
||||
when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper));
|
||||
when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new MockedResultDto());
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed");
|
||||
request.accept(VndMediaType.PLUGIN_COLLECTION);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus());
|
||||
assertThat(response.getContentAsString()).contains("\"marker\":\"x\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInstalledPlugin() throws UnsupportedEncodingException, URISyntaxException {
|
||||
PluginInformation pluginInformation = new PluginInformation();
|
||||
pluginInformation.setVersion("2.0.0");
|
||||
pluginInformation.setName("pluginName");
|
||||
pluginInformation.setState(PluginState.INSTALLED);
|
||||
Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null);
|
||||
PluginWrapper pluginWrapper = new PluginWrapper(plugin, null, null, null);
|
||||
when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper));
|
||||
|
||||
PluginDto pluginDto = new PluginDto();
|
||||
pluginDto.setName("pluginName");
|
||||
when(mapper.map(pluginWrapper)).thenReturn(pluginDto);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus());
|
||||
assertThat(response.getContentAsString()).contains("\"name\":\"pluginName\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithoutAuthorization {
|
||||
|
||||
@BeforeEach
|
||||
void unbindSubject() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotGetInstalledPluginsIfMissingPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed");
|
||||
request.accept(VndMediaType.PLUGIN_COLLECTION);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotGetInstalledPluginIfMissingPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName");
|
||||
request.accept(VndMediaType.PLUGIN);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response));
|
||||
}
|
||||
}
|
||||
|
||||
public class MockedResultDto extends HalRepresentation {
|
||||
public String getMarker() {
|
||||
return "x";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.plugin.PluginInformation;
|
||||
import sonia.scm.plugin.PluginState;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PluginDtoMapperTest {
|
||||
|
||||
@SuppressWarnings("unused") // Is injected
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(URI.create("https://hitchhiker.com/"));
|
||||
|
||||
@InjectMocks
|
||||
private PluginDtoMapperImpl mapper;
|
||||
|
||||
@Test
|
||||
void shouldMapInformation() {
|
||||
PluginInformation information = createPluginInformation();
|
||||
|
||||
PluginDto dto = mapper.map(information);
|
||||
|
||||
assertThat(dto.getName()).isEqualTo("scm-cas-plugin");
|
||||
assertThat(dto.getVersion()).isEqualTo("1.0.0");
|
||||
assertThat(dto.getDisplayName()).isEqualTo("CAS");
|
||||
assertThat(dto.getAuthor()).isEqualTo("Sebastian Sdorra");
|
||||
assertThat(dto.getCategory()).isEqualTo("Authentication");
|
||||
assertThat(dto.getAvatarUrl()).isEqualTo("https://avatar.scm-manager.org/plugins/cas.png");
|
||||
}
|
||||
|
||||
private PluginInformation createPluginInformation() {
|
||||
PluginInformation information = new PluginInformation();
|
||||
information.setName("scm-cas-plugin");
|
||||
information.setVersion("1.0.0");
|
||||
information.setDisplayName("CAS");
|
||||
information.setAuthor("Sebastian Sdorra");
|
||||
information.setCategory("Authentication");
|
||||
information.setAvatarUrl("https://avatar.scm-manager.org/plugins/cas.png");
|
||||
return information;
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAppendInstalledSelfLink() {
|
||||
PluginInformation information = createPluginInformation();
|
||||
information.setState(PluginState.INSTALLED);
|
||||
|
||||
PluginDto dto = mapper.map(information);
|
||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref())
|
||||
.isEqualTo("https://hitchhiker.com/v2/plugins/installed/scm-cas-plugin");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAppendAvailableSelfLink() {
|
||||
PluginInformation information = createPluginInformation();
|
||||
information.setState(PluginState.AVAILABLE);
|
||||
|
||||
PluginDto dto = mapper.map(information);
|
||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref())
|
||||
.isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/1.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAppendInstallLink() {
|
||||
PluginInformation information = createPluginInformation();
|
||||
information.setState(PluginState.AVAILABLE);
|
||||
|
||||
PluginDto dto = mapper.map(information);
|
||||
assertThat(dto.getLinks().getLinkBy("install").get().getHref())
|
||||
.isEqualTo("https://hitchhiker.com/v2/plugins/available/scm-cas-plugin/1.0.0/install");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnMiscellaneousIfCategoryIsNull() {
|
||||
PluginInformation information = createPluginInformation();
|
||||
information.setCategory(null);
|
||||
|
||||
PluginDto dto = mapper.map(information);
|
||||
assertThat(dto.getCategory()).isEqualTo("Miscellaneous");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,6 +36,10 @@ public class ResourceLinksMock {
|
||||
when(resourceLinks.modifications()).thenReturn(new ResourceLinks.ModificationsLinks(uriInfo));
|
||||
when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo));
|
||||
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
||||
when(resourceLinks.installedPluginCollection()).thenReturn(new ResourceLinks.InstalledPluginCollectionLinks(uriInfo));
|
||||
when(resourceLinks.availablePluginCollection()).thenReturn(new ResourceLinks.AvailablePluginCollectionLinks(uriInfo));
|
||||
when(resourceLinks.installedPlugin()).thenReturn(new ResourceLinks.InstalledPluginLinks(uriInfo));
|
||||
when(resourceLinks.availablePlugin()).thenReturn(new ResourceLinks.AvailablePluginLinks(uriInfo));
|
||||
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
||||
when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo));
|
||||
when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo));
|
||||
@@ -46,7 +50,6 @@ public class ResourceLinksMock {
|
||||
when(resourceLinks.repositoryRole()).thenReturn(new ResourceLinks.RepositoryRoleLinks(uriInfo));
|
||||
when(resourceLinks.repositoryRoleCollection()).thenReturn(new ResourceLinks.RepositoryRoleCollectionLinks(uriInfo));
|
||||
when(resourceLinks.namespaceStrategies()).thenReturn(new ResourceLinks.NamespaceStrategiesLinks(uriInfo));
|
||||
when(resourceLinks.pluginCollection()).thenReturn(new ResourceLinks.PluginCollectionLinks(uriInfo));
|
||||
|
||||
return resourceLinks;
|
||||
}
|
||||
|
||||
@@ -60,12 +60,12 @@ public class ExplodedSmpTest
|
||||
@Test
|
||||
public void testCompareTo()
|
||||
{
|
||||
ExplodedSmp e1 = create("a", "c", "1", "a:a");
|
||||
ExplodedSmp e1 = create("a", "c", "1", "a");
|
||||
ExplodedSmp e3 = create("a", "a", "1");
|
||||
ExplodedSmp e2 = create("a", "b", "1");
|
||||
List<ExplodedSmp> es = list(e1, e2, e3);
|
||||
|
||||
is(es, 2, "c");
|
||||
is(es, 2, "a");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,9 +75,9 @@ public class ExplodedSmpTest
|
||||
@Test(expected = PluginCircularDependencyException.class)
|
||||
public void testCompareToCyclicDependency()
|
||||
{
|
||||
ExplodedSmp e1 = create("a", "a", "1", "a:c");
|
||||
ExplodedSmp e2 = create("a", "b", "1");
|
||||
ExplodedSmp e3 = create("a", "c", "1", "a:a");
|
||||
ExplodedSmp e1 = create("a", "1", "c");
|
||||
ExplodedSmp e2 = create("b", "1");
|
||||
ExplodedSmp e3 = create("c", "1", "a");
|
||||
|
||||
list(e1, e2, e3);
|
||||
}
|
||||
@@ -89,9 +89,9 @@ public class ExplodedSmpTest
|
||||
@Test
|
||||
public void testCompareToTransitiveDependencies()
|
||||
{
|
||||
ExplodedSmp e1 = create("a", "a", "1", "a:b");
|
||||
ExplodedSmp e2 = create("a", "b", "1");
|
||||
ExplodedSmp e3 = create("a", "c", "1", "a:a");
|
||||
ExplodedSmp e1 = create("a", "1", "b");
|
||||
ExplodedSmp e2 = create("b", "1");
|
||||
ExplodedSmp e3 = create("c", "1", "a");
|
||||
|
||||
List<ExplodedSmp> es = list(e1, e2, e3);
|
||||
|
||||
@@ -107,9 +107,9 @@ public class ExplodedSmpTest
|
||||
@Test
|
||||
public void testMultipleDependencies()
|
||||
{
|
||||
ExplodedSmp e1 = create("a", "a", "1", "a:b", "a:c");
|
||||
ExplodedSmp e2 = create("a", "b", "1", "a:c");
|
||||
ExplodedSmp e3 = create("a", "c", "1");
|
||||
ExplodedSmp e1 = create("a", "1", "b", "c");
|
||||
ExplodedSmp e2 = create("b", "1", "c");
|
||||
ExplodedSmp e3 = create("c", "1");
|
||||
List<ExplodedSmp> es = list(e1, e2, e3);
|
||||
|
||||
is(es, 2, "a");
|
||||
@@ -119,20 +119,18 @@ public class ExplodedSmpTest
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param groupId
|
||||
* @param artifactId
|
||||
* @param name
|
||||
* @param version
|
||||
* @param dependencies
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ExplodedSmp create(String groupId, String artifactId, String version,
|
||||
private ExplodedSmp create(String name, String version,
|
||||
String... dependencies)
|
||||
{
|
||||
PluginInformation info = new PluginInformation();
|
||||
|
||||
info.setGroupId(groupId);
|
||||
info.setArtifactId(artifactId);
|
||||
info.setName(name);
|
||||
info.setVersion(version);
|
||||
|
||||
Plugin plugin = new Plugin(2, info, null, null, false,
|
||||
@@ -170,6 +168,6 @@ public class ExplodedSmpTest
|
||||
*/
|
||||
private void is(List<ExplodedSmp> es, int p, String a)
|
||||
{
|
||||
assertEquals(a, es.get(p).getPlugin().getInformation().getArtifactId());
|
||||
assertEquals(a, es.get(p).getPlugin().getInformation().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package sonia.scm.plugin;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static sonia.scm.plugin.PluginCenterDto.Plugin;
|
||||
import static sonia.scm.plugin.PluginCenterDto.*;
|
||||
|
||||
class PluginCenterDtoMapperTest {
|
||||
|
||||
@Test
|
||||
void shouldMapSinglePlugin() {
|
||||
Plugin plugin = new Plugin(
|
||||
"scm-hitchhiker-plugin",
|
||||
"SCM Hitchhiker Plugin",
|
||||
"plugin for hitchhikers",
|
||||
"Travel",
|
||||
"2.0.0",
|
||||
"trillian",
|
||||
"http://avatar.url",
|
||||
"555000444",
|
||||
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
|
||||
new Dependency("scm-review-plugin"),
|
||||
new HashMap<>());
|
||||
|
||||
PluginInformation result = PluginCenterDtoMapper.map(Collections.singletonList(plugin)).iterator().next();
|
||||
|
||||
assertThat(result.getAuthor()).isEqualTo(plugin.getAuthor());
|
||||
assertThat(result.getCategory()).isEqualTo(plugin.getCategory());
|
||||
assertThat(result.getVersion()).isEqualTo(plugin.getVersion());
|
||||
assertThat(result.getCondition().getArch()).isEqualTo(plugin.getConditions().getArch());
|
||||
assertThat(result.getCondition().getMinVersion()).isEqualTo(plugin.getConditions().getMinVersion());
|
||||
assertThat(result.getCondition().getOs().iterator().next()).isEqualTo(plugin.getConditions().getOs().iterator().next());
|
||||
assertThat(result.getDescription()).isEqualTo(plugin.getDescription());
|
||||
assertThat(result.getName()).isEqualTo(plugin.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapMultiplePlugins() {
|
||||
Plugin plugin1 = new Plugin(
|
||||
"scm-review-plugin",
|
||||
"SCM Hitchhiker Plugin",
|
||||
"plugin for hitchhikers",
|
||||
"Travel",
|
||||
"2.1.0",
|
||||
"trillian",
|
||||
"https://avatar.url",
|
||||
"12345678aa",
|
||||
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
|
||||
new Dependency("scm-review-plugin"),
|
||||
new HashMap<>());
|
||||
|
||||
Plugin plugin2 = new Plugin(
|
||||
"scm-hitchhiker-plugin",
|
||||
"SCM Hitchhiker Plugin",
|
||||
"plugin for hitchhikers",
|
||||
"Travel",
|
||||
"2.0.0",
|
||||
"dent",
|
||||
"http://avatar.url",
|
||||
"555000444",
|
||||
new Condition(Collections.singletonList("linux"), "amd64","2.0.0"),
|
||||
new Dependency("scm-review-plugin"),
|
||||
new HashMap<>());
|
||||
|
||||
Set<PluginInformation> resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2));
|
||||
|
||||
List<PluginInformation> pluginsList = new ArrayList<>(resultSet);
|
||||
|
||||
PluginInformation pluginInformation1 = pluginsList.get(1);
|
||||
PluginInformation pluginInformation2 = pluginsList.get(0);
|
||||
|
||||
assertThat(pluginInformation1.getAuthor()).isEqualTo(plugin1.getAuthor());
|
||||
assertThat(pluginInformation1.getVersion()).isEqualTo(plugin1.getVersion());
|
||||
assertThat(pluginInformation2.getAuthor()).isEqualTo(plugin2.getAuthor());
|
||||
assertThat(pluginInformation2.getVersion()).isEqualTo(plugin2.getVersion());
|
||||
assertThat(resultSet.size()).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
@@ -71,37 +71,37 @@ public class PluginProcessorTest
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_A =
|
||||
new PluginResource("sonia/scm/plugin/scm-a-plugin.smp", "scm-a-plugin.smp",
|
||||
"sonia.scm.plugins:scm-a-plugin:1.0.0-SNAPSHOT");
|
||||
"scm-a-plugin:1.0.0-SNAPSHOT");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_B =
|
||||
new PluginResource("sonia/scm/plugin/scm-b-plugin.smp", "scm-b-plugin.smp",
|
||||
"sonia.scm.plugins:scm-b-plugin:1.0.0-SNAPSHOT");
|
||||
"scm-b-plugin:1.0.0-SNAPSHOT");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_C =
|
||||
new PluginResource("sonia/scm/plugin/scm-c-plugin.smp", "scm-c-plugin.smp",
|
||||
"sonia.scm.plugins:scm-c-plugin:1.0.0-SNAPSHOT");
|
||||
"scm-c-plugin:1.0.0-SNAPSHOT");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_D =
|
||||
new PluginResource("sonia/scm/plugin/scm-d-plugin.smp", "scm-d-plugin.smp",
|
||||
"sonia.scm.plugins:scm-d-plugin:1.0.0-SNAPSHOT");
|
||||
"scm-d-plugin:1.0.0-SNAPSHOT");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_E =
|
||||
new PluginResource("sonia/scm/plugin/scm-e-plugin.smp", "scm-e-plugin.smp",
|
||||
"sonia.scm.plugins:scm-e-plugin:1.0.0-SNAPSHOT");
|
||||
"scm-e-plugin:1.0.0-SNAPSHOT");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_F_1_0_0 =
|
||||
new PluginResource("sonia/scm/plugin/scm-f-plugin-1.0.0.smp",
|
||||
"scm-f-plugin.smp", "sonia.scm.plugins:scm-f-plugin:1.0.0");
|
||||
"scm-f-plugin.smp", "scm-f-plugin:1.0.0");
|
||||
|
||||
/** Field description */
|
||||
private static final PluginResource PLUGIN_F_1_0_1 =
|
||||
new PluginResource("sonia/scm/plugin/scm-f-plugin-1.0.1.smp",
|
||||
"scm-f-plugin.smp", "sonia.scm.plugins:scm-f-plugin:1.0.1");
|
||||
"scm-f-plugin.smp", "scm-f-plugin:1.0.1");
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ public class PluginTreeTest
|
||||
{
|
||||
PluginCondition condition = new PluginCondition("999",
|
||||
new ArrayList<String>(), "hit");
|
||||
Plugin plugin = new Plugin(2, createInfo("a", "b", "1"), null, condition,
|
||||
Plugin plugin = new Plugin(2, createInfo("a", "1"), null, condition,
|
||||
false, null);
|
||||
ExplodedSmp smp = createSmp(plugin);
|
||||
|
||||
@@ -102,7 +102,7 @@ public class PluginTreeTest
|
||||
List<ExplodedSmp> smps = createSmps("a", "b", "c");
|
||||
List<String> nodes = unwrapIds(new PluginTree(smps).getRootNodes());
|
||||
|
||||
assertThat(nodes, containsInAnyOrder("a:a", "b:b", "c:c"));
|
||||
assertThat(nodes, containsInAnyOrder("a", "b", "c"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +114,7 @@ public class PluginTreeTest
|
||||
@Test(expected = PluginException.class)
|
||||
public void testScmVersion() throws IOException
|
||||
{
|
||||
Plugin plugin = new Plugin(1, createInfo("a", "b", "1"), null, null, false,
|
||||
Plugin plugin = new Plugin(1, createInfo("a", "1"), null, null, false,
|
||||
null);
|
||||
ExplodedSmp smp = createSmp(plugin);
|
||||
|
||||
@@ -141,34 +141,32 @@ public class PluginTreeTest
|
||||
PluginTree tree = new PluginTree(smps);
|
||||
List<PluginNode> rootNodes = tree.getRootNodes();
|
||||
|
||||
assertThat(unwrapIds(rootNodes), containsInAnyOrder("a:a"));
|
||||
assertThat(unwrapIds(rootNodes), containsInAnyOrder("a"));
|
||||
|
||||
PluginNode a = rootNodes.get(0);
|
||||
|
||||
assertThat(unwrapIds(a.getChildren()), containsInAnyOrder("b:b", "c:c"));
|
||||
assertThat(unwrapIds(a.getChildren()), containsInAnyOrder("b", "c"));
|
||||
|
||||
PluginNode b = a.getChild("b:b");
|
||||
PluginNode b = a.getChild("b");
|
||||
|
||||
assertThat(unwrapIds(b.getChildren()), containsInAnyOrder("c:c"));
|
||||
assertThat(unwrapIds(b.getChildren()), containsInAnyOrder("c"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param groupId
|
||||
* @param artifactId
|
||||
* @param name
|
||||
* @param version
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private PluginInformation createInfo(String groupId, String artifactId,
|
||||
private PluginInformation createInfo(String name,
|
||||
String version)
|
||||
{
|
||||
PluginInformation info = new PluginInformation();
|
||||
|
||||
info.setGroupId(groupId);
|
||||
info.setArtifactId(artifactId);
|
||||
info.setName(name);
|
||||
info.setVersion(version);
|
||||
|
||||
return info;
|
||||
@@ -201,7 +199,7 @@ public class PluginTreeTest
|
||||
*/
|
||||
private ExplodedSmp createSmp(String name) throws IOException
|
||||
{
|
||||
return createSmp(new Plugin(2, createInfo(name, name, "1.0.0"), null, null,
|
||||
return createSmp(new Plugin(2, createInfo(name, "1.0.0"), null, null,
|
||||
false, null));
|
||||
}
|
||||
|
||||
@@ -224,10 +222,10 @@ public class PluginTreeTest
|
||||
|
||||
for (String d : dependencies)
|
||||
{
|
||||
dependencySet.add(d.concat(":").concat(d));
|
||||
dependencySet.add(d);
|
||||
}
|
||||
|
||||
Plugin plugin = new Plugin(2, createInfo(name, name, "1"), null, null,
|
||||
Plugin plugin = new Plugin(2, createInfo(name, "1"), null, null,
|
||||
false, dependencySet);
|
||||
|
||||
return createSmp(plugin);
|
||||
|
||||
@@ -5,8 +5,8 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.update.repository.MigrationStrategy;
|
||||
import sonia.scm.update.repository.DefaultMigrationStrategyDAO;
|
||||
import sonia.scm.update.repository.MigrationStrategy;
|
||||
import sonia.scm.update.repository.V1Repository;
|
||||
import sonia.scm.update.repository.XmlRepositoryV1UpdateStep;
|
||||
|
||||
|
||||
@@ -11,10 +11,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.security.AssignedPermission;
|
||||
import sonia.scm.store.ConfigurationEntryStore;
|
||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||
import sonia.scm.store.InMemoryConfigurationEntryStore;
|
||||
import sonia.scm.store.InMemoryConfigurationEntryStoreFactory;
|
||||
import sonia.scm.update.security.XmlSecurityV1UpdateStep;
|
||||
|
||||
import javax.xml.bind.JAXBException;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -6,14 +6,12 @@ import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user