From 1ea2bdfedff9ad540de50dc302aa2264d1b63fee Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 25 Jul 2019 11:34:16 +0200 Subject: [PATCH 01/30] change type to category --- scm-ui/src/admin/plugins/components/groupByCategory.js | 2 +- .../src/main/java/sonia/scm/api/v2/resources/PluginDto.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scm-ui/src/admin/plugins/components/groupByCategory.js b/scm-ui/src/admin/plugins/components/groupByCategory.js index 1c542d45e3..49b6590d9a 100644 --- a/scm-ui/src/admin/plugins/components/groupByCategory.js +++ b/scm-ui/src/admin/plugins/components/groupByCategory.js @@ -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) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java index d119eca711..a35c3e848d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java @@ -13,7 +13,7 @@ import lombok.Setter; public class PluginDto extends HalRepresentation { private String name; - private String type; + private String category; private String version; private String author; private String description; From 27dc47a590b33b2aa66ce1745586216e306acff6 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Fri, 26 Jul 2019 13:04:54 +0200 Subject: [PATCH 02/30] parse pluginBackendResponse to pluginCenterDto / add Endpoint / remove groupId + artefactId from plugins --- .../sonia/scm/config/ScmConfiguration.java | 2 +- .../sonia/scm/plugin/PluginInformation.java | 71 ++------------- .../plugin/PluginInformationComparator.java | 31 +++---- .../java/sonia/scm/plugin/SmpArchive.java | 10 +-- .../java/sonia/scm/plugin/SmpArchiveTest.java | 43 +++------ .../resources/PluginDtoCollectionMapper.java | 8 +- .../scm/api/v2/resources/PluginDtoMapper.java | 17 ++-- .../scm/api/v2/resources/PluginResource.java | 29 ++++++- .../scm/plugin/DefaultPluginManager.java | 57 +++++++----- .../java/sonia/scm/plugin/ExplodedSmp.java | 4 +- .../sonia/scm/plugin/PluginCenterDto.java | 87 +++++++++++++++++++ .../scm/plugin/PluginCenterDtoMapper.java | 5 ++ .../java/sonia/scm/plugin/PluginNode.java | 2 +- .../sonia/scm/plugin/PluginProcessor.java | 2 +- .../sonia/scm/plugin/PluginsInternal.java | 6 +- 15 files changed, 217 insertions(+), 157 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index 8d3db8b348..6868182f10 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -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"; /** * Default plugin url from version 1.0 diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 6de52c3cca..50d5c0d81f 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -88,7 +88,6 @@ public class PluginInformation { PluginInformation clone = new PluginInformation(); - clone.setArtifactId(artifactId); clone.setAuthor(author); clone.setCategory(category); clone.setTags(tags); @@ -99,7 +98,6 @@ public class PluginInformation } clone.setDescription(description); - clone.setGroupId(groupId); clone.setName(name); if (Util.isNotEmpty(screenshots)) @@ -139,13 +137,12 @@ public class PluginInformation final PluginInformation other = (PluginInformation) obj; //J- - return Objects.equal(artifactId, other.artifactId) - && Objects.equal(author, other.author) + return + 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) @@ -164,8 +161,8 @@ public class PluginInformation @Override public int hashCode() { - return Objects.hashCode(artifactId, author, category, tags, condition, - description, groupId, name, screenshots, state, url, version, wiki); + return Objects.hashCode(author, category, tags, condition, + description, name, screenshots, state, url, version, wiki); } /** @@ -179,13 +176,11 @@ public class PluginInformation { //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) @@ -198,17 +193,6 @@ public class PluginInformation //~--- get methods ---------------------------------------------------------- - /** - * Method description - * - * - * @return - */ - public String getArtifactId() - { - return artifactId; - } - /** * Method description * @@ -253,16 +237,6 @@ public class PluginInformation return description; } - /** - * Method description - * - * - * @return - */ - public String getGroupId() - { - return groupId; - } /** * Method description @@ -273,7 +247,7 @@ public class PluginInformation @Override public String getId() { - return getId(true); + return getName(true); } /** @@ -285,11 +259,9 @@ public class PluginInformation * @return * @since 1.21 */ - public String getId(boolean withVersion) + public String getName(boolean withVersion) { - StringBuilder id = new StringBuilder(groupId); - - id.append(":").append(artifactId); + StringBuilder id = new StringBuilder(name); if (withVersion) { @@ -385,22 +357,11 @@ public class PluginInformation @Override public boolean isValid() { - return Util.isNotEmpty(groupId) && Util.isNotEmpty(artifactId) - && Util.isNotEmpty(name) && Util.isNotEmpty(version); + return Util.isNotEmpty(name) && Util.isNotEmpty(version); } //~--- set methods ---------------------------------------------------------- - /** - * Method description - * - * - * @param artifactId - */ - public void setArtifactId(String artifactId) - { - this.artifactId = artifactId; - } /** * Method description @@ -446,16 +407,6 @@ public class PluginInformation this.description = description; } - /** - * Method description - * - * - * @param groupId - */ - public void setGroupId(String groupId) - { - this.groupId = groupId; - } /** * Method description @@ -536,9 +487,6 @@ public class PluginInformation //~--- fields --------------------------------------------------------------- - /** Field description */ - private String artifactId; - /** Field description */ private String author; @@ -551,9 +499,6 @@ public class PluginInformation /** Field description */ private String description; - /** Field description */ - private String groupId; - /** Field description */ private String name; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java index f44de35e8a..5443b2328d 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformationComparator.java @@ -75,29 +75,24 @@ public class PluginInformationComparator { int result = 0; - result = Util.compare(plugin.getGroupId(), other.getGroupId()); + result = Util.compare(plugin.getName(), other.getName()); if (result == 0) { - result = Util.compare(plugin.getArtifactId(), other.getArtifactId()); + PluginState state = plugin.getState(); + PluginState otherState = other.getState(); - if (result == 0) + if ((state != null) && (otherState != null)) { - PluginState state = plugin.getState(); - PluginState otherState = other.getState(); - - if ((state != null) && (otherState != null)) - { - result = state.getCompareValue() - otherState.getCompareValue(); - } - else if ((state == null) && (otherState != null)) - { - result = 1; - } - else if ((state != null) && (otherState == null)) - { - result = -1; - } + result = state.getCompareValue() - otherState.getCompareValue(); + } + else if ((state == null) && (otherState != null)) + { + result = 1; + } + else if ((state != null) && (otherState == null)) + { + result = -1; } } diff --git a/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java b/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java index 63d5e8fb8f..f674bdd2ba 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java +++ b/scm-core/src/main/java/sonia/scm/plugin/SmpArchive.java @@ -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())) diff --git a/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java b/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java index 95addf388f..d7f4ecf515 100644 --- a/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java +++ b/scm-core/src/test/java/sonia/scm/plugin/SmpArchiveTest.java @@ -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(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java index 72178e94f3..0bb5bd3610 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java @@ -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,7 +25,12 @@ public class PluginDtoCollectionMapper { this.mapper = mapper; } - public HalRepresentation map(Collection plugins) { + public HalRepresentation map(List plugins) { + List dtos = plugins.stream().map(mapper::map).collect(toList()); + return new HalRepresentation(createLinks(), embedDtos(dtos)); + } + + public HalRepresentation map(Collection plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); return new HalRepresentation(createLinks(), embedDtos(dtos)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index d17ecdae70..7b2108cd0e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; +import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginWrapper; import javax.inject.Inject; @@ -16,16 +17,20 @@ public class PluginDtoMapper { } public PluginDto map(PluginWrapper plugin) { + return map(plugin.getPlugin().getInformation()); + } + + public PluginDto map(PluginInformation pluginInformation) { Links.Builder linksBuilder = linkingTo() .self(resourceLinks.plugin() - .self(plugin.getPlugin().getInformation().getId(false))); + .self(pluginInformation.getName())); 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()); + pluginDto.setName(pluginInformation.getName()); + pluginDto.setCategory(pluginInformation.getCategory() != null ? pluginInformation.getCategory() : "Miscellaneous"); + pluginDto.setVersion(pluginInformation.getVersion()); + pluginDto.setAuthor(pluginInformation.getAuthor()); + pluginDto.setDescription(pluginInformation.getDescription()); return pluginDto; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java index c3b6ea6020..99c61191fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java @@ -4,7 +4,10 @@ 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.PluginCenter; +import sonia.scm.plugin.PluginInformation; 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; @@ -16,6 +19,7 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; @@ -27,12 +31,14 @@ public class PluginResource { 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 PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginCenter pluginCenter1, PluginManager pluginManager) { this.pluginLoader = pluginLoader; this.collectionMapper = collectionMapper; this.mapper = mapper; + this.pluginManager = pluginManager; } /** @@ -74,7 +80,7 @@ public class PluginResource { PluginPermissions.read().check(); Optional pluginDto = pluginLoader.getInstalledPlugins() .stream() - .filter(plugin -> id.equals(plugin.getPlugin().getInformation().getId(false))) + .filter(plugin -> id.equals(plugin.getPlugin().getInformation().getName(false))) .map(mapper::map) .findFirst(); if (pluginDto.isPresent()) { @@ -84,4 +90,23 @@ public class PluginResource { } } + /** + * Returns a collection of available plugins. + * + * @return collection of available plugins. + */ + @GET + @Path("/available") + @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 plugins = pluginManager.getAvailable(); + return Response.ok(collectionMapper.map(plugins)).build(); + } + } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index ed1f691988..4bcfc3a06e 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -67,10 +67,12 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -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 FILTER_UPDATES = @@ -309,14 +311,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 +326,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 +423,7 @@ public class DefaultPluginManager implements PluginManager for (PluginInformation info : centerPlugins) { - if (!installedPlugins.containsKey(info.getId())) + if (!installedPlugins.containsKey(info.getName())) { availablePlugins.add(info); } @@ -590,7 +590,7 @@ public class DefaultPluginManager implements PluginManager */ private PluginCenter getPluginCenter() { - PluginCenter center = cache.get(PluginCenter.class.getName()); + PluginCenter center = null; // cache.get(PluginCenter.class.getName()); if (center == null) { @@ -605,15 +605,13 @@ public class DefaultPluginManager implements PluginManager 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); + center.setPlugins(mapPluginsFromPluginCenter(pluginCenterDto.getEmbedded().getPlugins())); preparePlugins(center); cache.put(PluginCenter.class.getName(), center); @@ -633,17 +631,35 @@ public class DefaultPluginManager implements PluginManager logger.error("could not load plugins from plugin center", ex); } } - - if (center == null) - { - center = new PluginCenter(); - } } } return center; } + private Set mapPluginsFromPluginCenter(List plugins) { + HashSet pluginInformationSet = new HashSet<>(); + + for (PluginCenterDto.Plugin plugin : plugins) { + + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setName(plugin.getName()); + pluginInformation.setAuthor(plugin.getAuthor()); + pluginInformation.setCategory(plugin.getCategory()); + pluginInformation.setVersion(plugin.getVersion()); + pluginInformation.setDescription(plugin.getDescription()); + pluginInformation.setUrl(plugin.getLinks().getDownload()); + + if (plugin.getConditions() != null) { + PluginCenterDto.Condition condition = plugin.getConditions(); + pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); + } + + pluginInformationSet.add(pluginInformation); + } + return pluginInformationSet; + } + /** * Method description * @@ -719,8 +735,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 --------------------------------------------------------------- diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java b/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java index d1fe214f50..372470df14 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/ExplodedSmp.java @@ -115,8 +115,8 @@ public final class ExplodedSmp implements Comparable } 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)) { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java new file mode 100644 index 0000000000..468249157a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -0,0 +1,87 @@ +package sonia.scm.plugin; + +import com.google.common.collect.ImmutableList; +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; + +@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) + static class Embedded { + + @XmlElement(name = "plugins") + private List plugins; + + public List getPlugins() { + if (plugins == null) { + plugins = ImmutableList.of(); + } + return plugins; + } + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "plugins") + @Getter + static class Plugin { + + private String name; + private String displayName; + private String description; + private String category; + private String version; + private String author; + private String sha256; + + @XmlElement(name = "conditions") + private Condition conditions; + + @XmlElement(name = "dependecies") + private Dependency dependencies; + + @XmlElement(name = "_links") + private Links links; + + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "conditions") + @Getter + static class Condition { + + private String os; + private String arch; + private String minVersion; + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "dependencies") + @Getter + static class Dependency { + private String name; + } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "_links") + @Getter + static class Links { + private String download; + } + +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java new file mode 100644 index 0000000000..77593fe56a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -0,0 +1,5 @@ +package sonia.scm.plugin; + +public class PluginCenterDtoMapper { + +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java index e28ccff2ff..8bc4f47658 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginNode.java @@ -126,7 +126,7 @@ public final class PluginNode */ public String getId() { - return plugin.getPlugin().getInformation().getId(false); + return plugin.getPlugin().getInformation().getName(false); } /** diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java index c7d669ee63..e55254d1d1 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -377,7 +377,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()) { diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java index 52d192da32..0354ded11a 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginsInternal.java @@ -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); From 785e5e114282b79c3404958d597d3f4091be0d04 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 29 Jul 2019 11:52:53 +0200 Subject: [PATCH 03/30] refactor plugin backend + fix tests --- .../sonia/scm/plugin/PluginInformation.java | 134 +++--------------- .../scm/plugin/DefaultPluginManager.java | 1 - .../sonia/scm/plugin/PluginProcessor.java | 8 +- .../api/v2/resources/ResourceLinksMock.java | 2 + .../sonia/scm/plugin/ExplodedSmpTest.java | 32 ++--- .../sonia/scm/plugin/PluginProcessorTest.java | 14 +- .../java/sonia/scm/plugin/PluginTreeTest.java | 28 ++-- .../sonia/scm/plugin/scm-b-plugin.smp | Bin 5787 -> 6872 bytes .../sonia/scm/plugin/scm-c-plugin.smp | Bin 4899 -> 5727 bytes .../sonia/scm/plugin/scm-d-plugin.smp | Bin 4899 -> 5727 bytes .../sonia/scm/plugin/scm-e-plugin.smp | Bin 4899 -> 5727 bytes .../sonia/scm/plugin/scm-f-plugin-1.0.0.smp | Bin 4822 -> 5654 bytes .../sonia/scm/plugin/scm-f-plugin-1.0.1.smp | Bin 4826 -> 5658 bytes 13 files changed, 54 insertions(+), 165 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 50d5c0d81f..663fd81f9f 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -44,11 +44,8 @@ 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 ------------------------------------------------------------ @@ -87,29 +84,18 @@ public class PluginInformation public PluginInformation clone() { PluginInformation clone = new PluginInformation(); - + clone.setName(name); clone.setAuthor(author); clone.setCategory(category); - clone.setTags(tags); + clone.setDescription(description); + clone.setState(state); + clone.setVersion(version); if (condition != null) { clone.setCondition(condition.clone()); } - clone.setDescription(description); - clone.setName(name); - - if (Util.isNotEmpty(screenshots)) - { - clone.setScreenshots(new ArrayList(screenshots)); - } - - clone.setState(state); - clone.setUrl(url); - clone.setVersion(version); - clone.setWiki(wiki); - return clone; } @@ -140,15 +126,11 @@ public class PluginInformation return 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(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); + && Objects.equal(state, other.state) + && Objects.equal(version, other.version); //J+ } @@ -161,8 +143,8 @@ public class PluginInformation @Override public int hashCode() { - return Objects.hashCode(author, category, tags, condition, - description, name, screenshots, state, url, version, wiki); + return Objects.hashCode(author, category, condition, + description, name, state, version); } /** @@ -178,15 +160,11 @@ public class PluginInformation return MoreObjects.toStringHelper(this) .add("author", author) .add("category", category) - .add("tags", tags) .add("condition", condition) .add("description", description) .add("name", name) - .add("screenshots", screenshots) .add("state", state) - .add("url", url) .add("version", version) - .add("wiki", wiki) .toString(); //J+ } @@ -282,17 +260,6 @@ public class PluginInformation return name; } - /** - * Method description - * - * - * @return - */ - public List getScreenshots() - { - return screenshots; - } - /** * Method description * @@ -304,28 +271,6 @@ public class PluginInformation return state; } - /** - * Method description - * - * - * @return - */ - public List getTags() - { - return tags; - } - - /** - * Method description - * - * - * @return - */ - public String getUrl() - { - return url; - } - /** * Method description * @@ -343,9 +288,8 @@ public class PluginInformation * * @return */ - public String getWiki() - { - return wiki; + public List getLinks() { + return links; } /** @@ -362,7 +306,6 @@ public class PluginInformation //~--- set methods ---------------------------------------------------------- - /** * Method description * @@ -419,17 +362,6 @@ public class PluginInformation this.name = name; } - /** - * Method description - * - * - * @param screenshots - */ - public void setScreenshots(List screenshots) - { - this.screenshots = screenshots; - } - /** * Method description * @@ -441,28 +373,6 @@ public class PluginInformation this.state = state; } - /** - * Method description - * - * - * @param tags - */ - public void setTags(List tags) - { - this.tags = tags; - } - - /** - * Method description - * - * - * @param url - */ - public void setUrl(String url) - { - this.url = url; - } - /** * Method description * @@ -474,15 +384,15 @@ public class PluginInformation this.version = version; } + /** * Method description * * - * @param wiki + * @param links */ - public void setWiki(String wiki) - { - this.wiki = wiki; + public void setLinks(List links) { + this.links = links; } //~--- fields --------------------------------------------------------------- @@ -502,25 +412,13 @@ public class PluginInformation /** Field description */ private String name; - /** Field description */ - @XmlElement(name = "screenshot") - @XmlElementWrapper(name = "screenshots") - private List screenshots; - /** Field description */ private PluginState state; - /** Field description */ - @XmlElement(name = "tag") - @XmlElementWrapper(name = "tags") - private List tags; - - /** Field description */ - private String url; - /** Field description */ private String version; /** Field description */ - private String wiki; + private List links; + } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 4bcfc3a06e..a62916ed41 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -648,7 +648,6 @@ public class DefaultPluginManager implements PluginManager pluginInformation.setCategory(plugin.getCategory()); pluginInformation.setVersion(plugin.getVersion()); pluginInformation.setDescription(plugin.getDescription()); - pluginInformation.setUrl(plugin.getLinks().getDownload()); if (plugin.getConditions() != null) { PluginCenterDto.Condition condition = plugin.getConditions(); diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java index e55254d1d1..b91ee9b1ee 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginProcessor.java @@ -318,10 +318,7 @@ public final class PluginProcessor { for (Path parent : parentStream) { - try (DirectoryStream 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 @@ -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 diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java index 6950d882f4..539b5c8d99 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java @@ -36,6 +36,8 @@ 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.pluginCollection()).thenReturn(new ResourceLinks.PluginCollectionLinks(uriInfo)); + when(resourceLinks.plugin()).thenReturn(new ResourceLinks.PluginLinks(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)); diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java index b7bde65677..601725d938 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/ExplodedSmpTest.java @@ -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 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 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 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 es, int p, String a) { - assertEquals(a, es.get(p).getPlugin().getInformation().getArtifactId()); + assertEquals(a, es.get(p).getPlugin().getInformation().getName()); } } diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java index 8b352b8e68..87e9cbf7b7 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginProcessorTest.java @@ -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 -------------------------------------------------------------- diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java index 06d6c1732c..0115f4510e 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginTreeTest.java @@ -71,7 +71,7 @@ public class PluginTreeTest { PluginCondition condition = new PluginCondition("999", new ArrayList(), "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 smps = createSmps("a", "b", "c"); List 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 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); diff --git a/scm-webapp/src/test/resources/sonia/scm/plugin/scm-b-plugin.smp b/scm-webapp/src/test/resources/sonia/scm/plugin/scm-b-plugin.smp index 4106b9794585e53f4ef50d9ab63eee75b9657e56..a70205e3eb05275a8846e3fbffdce57aa9b73b4c 100644 GIT binary patch delta 3361 zcmbQOd&879z?+#xWU{`HxV{2Ia!z7#acZ%CXb3L@yTsq7kgPp+u^?Pp!Og(P@`9Ox z0ZhR3h*}9UAawbV&~?$O!+L64TMC@`H<`^&dlP9_$#92_THcgsowL`Hw6F>dvp9@izEAg7JcB z)}PakFPv|1!9DKg$3qc`?EW_oX6+Wdd`t9E(r4Aw1D7iQ$)vFq>^*l$-AdVbMqd2| z4+XF5vwQTWS?XM_a5G{{G&+1&<@}2+CxY)twD2gi-_345W^j|?=e~0s$rI8X!e7r& zQb;>-NM(5oZ=+gZ%$bZ$5=>1Flb#7&7Cn1W*T3Ctqw{Z5kxQ0qJj8TA8qT>i^rckMg-s{)k8Mu%Rmu{7Vtp{_P>%7NLdl<- z9n?(@>BO{hACkE9I@o(nTcsC)r$lZ`S}d|W(KcK zNa>jQ>4IRrgT#~`+Z$iEBwXoqpFAfea^tzoS$7il&kvM*B9-*w%*r`xwUh7WEV+4% zDcUzix$VOql^ydVW9JqvoZ7swDX{Ksn`PjIKf!w^OmOuH{M8ZBe?`eQ*e!2E`IJcZ zmaQx2JD-W(@hNxrgxKv%<~BXgl-qTTX|A(H^`dPpb_~xOFV@wU&1t!~Dc|vb&>o56 zKd*jACvIE*irqYU<+puh<<09?r(G#|zvFr3;)^O4a(>kp{S1WmH*Wp@{qhsL59{T1 zD-@!t|Lttfcrv68iD?(Ay+7>22YkN|tz^9&F|NO_d9jXs!>0Y+^P=AT%&BU*q2XiYAddjN(50u^h z&hwZ2L7gt~D>30-GINFIvaZ|4^#9g7)_?2|*QiXlULd|n@U81F|D&4V zer3*_zwy`c`b)oaiVUV3oDVBGa#{MnykObmPe0xr(W$>ypZa0lyj>dqZT#z*14O*p zIX=C+9T~>LzyKDUMyu5OtUy6uxt}hQU#Z7M5q^K z)&rG>0{XT@=>V6fO!;lDu%h_~8v}zW*aTzcirgHqAy57^g@EL-ICFC)UoI1SHBiz9 ztjU&OO*C0cu$vLwsIXT6843n(9YGZFO$o@_k`BUW7?Y8edGVW>QiIhbBlH$O-~30X0Qn;H~32 zRz&a++c1Iq${W-)fq}PH&f>*&QcS&~Tx5%IX%Ejl7{d;p_uW1bEvy;E02_bdZY_U`q#PG{cfc1Fp$$I3yu~ho{*C zi#cH6d4NhH7Y5ZyiVaR!Wcm~#_}ii#hIMnw-T1hI%Bhnm91N<&B?dP#90wrMQ1k}b6n z(p%&wXk#I0BWk5TK(Ms&V`eY6x3O?S2$_8|Z|2Q=vd<@@1*a{N;Ac7;BV;|lwLH1D zu`-3p6w%?6tsduz5-CFXhi>uV_8XeuKT*rn;k9&7^t`es>O{83RI)8?#WS^drN^a}~|L^sL#!A@i00df8ctRoc<%;if3_G;1M zyaU~)_A+t-mpID{0;XFQLjPTFU1+PY%qKX`mIbK5ND|%17kc_aQ6YWLUo$p(vb*3! Pg-uOjl1C8nO4#NXYH!Zn diff --git a/scm-webapp/src/test/resources/sonia/scm/plugin/scm-c-plugin.smp b/scm-webapp/src/test/resources/sonia/scm/plugin/scm-c-plugin.smp index aee452fac4a5056e2439f140faa9f361407791c5..b80169b9b594a9560803fbdde39124e020dd4834 100644 GIT binary patch delta 2953 zcmZ3ic3+1zz?+#xWO5?Es38Y~0z-06VsUY5v3_U>F9W;G-=>fWd+cICxU_E}kAb_Ni567|;)%zzT2*o-Sq&V?BHcNfGp2p6fLI*EY; z2$9Vc;$&cuXHb~@l2M@^>?x2DFk{d>wKXg-|B;12-ShA>AGb_x*s^!}WwTV#CPr_? zhmV%T$~h;k*s9{TEpz+-f5-eHG-6-Adn&M~Fa6o<{CzWTeBJ+d&G!$driyKQ%Ua2)>!;Zuiq3+)zXjz1;6 zWB=aWvJ8)BJ4pwc9MD=B@=&JvuiTu)Y)4JQ*YAb5WH zvgaf>slZQ-&WHOe1*N`mWEARMHoGdSekST2cVMw;(ZTQYS0t1%F@N2`D^KREU6TfJhUdk6-dTGWlS+hIm1jLjI=M-`j&NGl%dF4_;M(82W zH4~KilyWXUV6kM(+G(0!CbNr^x%Ra*DRAYTpO#XXBHj6rZY43yrUPx z_0=Yoi-L_>PfFGIvK&g;>i4o{;z6a(Wh$uxM}qJ4Zl2=zN#|g1-2sD5_xG7)@H{e| z@P73a12#{tlAT=|Oi67f9mZZa7&2tseoi^|nNfk`h$ow1>?NMNn^F>lt+$+CIN?l! z=cR(i{(~)UCegd+7|%LtcArO>Z3EY(p0-MnB^yp%4n4$?P{~*1RUawATH?7(`AEN^ z=>&D5^aEm!B0DDq8y)98{?~b~aFa^2r^_v!kGq(CJZIdTbZ)hPs7syTJY8*}pxp}h zImLvRymo9qed4~Q)k48#2aRS3KYOt6Pq)d9`3C+cF9o^jM%X1vp8eEwz{Yb~pNaBI zVP4Maf`-6DbFwUzna@mN?PI7vv>@fp!FB4Ei~Ve*(p7vvhq_Jv!S?%a&nboL49?5_ zo~pc?^(IZ(^SaoLtVdFY4bNE*{=M?*)fDbEGCZO@o4w7?bT*}he|7SCCg}RoImuS2 z`-_j{&V%;?(_{s%Evw@*$bWZg`&*~7x0AMU&0ALaK#IGB?ckTrTSB`S=CHf%zFA)= z)YiR@^{)5Fq%#j!KhJHsDRWx>PS28CmACHl&eMCHcz569y>a;#GQZpZ7OKxObbEAt z`?q&>Qulw~d%BqQyKC}!`*MNaSfg_DovwvP%ENnW*GKP&J(F;_%u9}ci&XZ_j--|q zi_O-4+dFyXbMaFhxq(uz+bd5ezHV+vcRp`_vHsxc;u7nW!VO8?Dxdepo77gP#47Gs zJFh<7=%a-E%kSzVumpowq8VU*|O@@#uZ!neTtzV}H0t<+=3&;Z0&^BY&-a zq?c=UV|ULs>Gnl`8n^gI+|l!i&z-{)y3t7{RrGA1|^Q@Pe06RRAt`k)2}( z^qn}c+;tF?P=H%M5n6nsmAji)vlX&HtheAtwmvtpEH#g~$^=w{F)IQsT*$4Y3bv4t zlfgRlz&i9HPQ;^Q@&#@&_MdDF45knb>IM0^dKI}jV4Lp!!5Olfm3eZR*sFoTZUfOc z`M(e!!ZsZ7G5HE#4)saW66Xr;W@j<{Q0X!+eDqk$=Fu)sP2dolD&MLr49A*W>lExR4pED|{ zgWZg$LWi0MjA?O8gzmSF>sT2O`Gk;d{TX+_((r3}{A%J7`JcH-4z0f|F12ahbsr b6G#abM+JDZ0t*oa23{Zx2BxEL0w5j$i0jsj delta 1114 zcmcbwvsleJz?+$civa`_^4na&3@E_~qM@LP!>&@2*c!mLSmEM z*^V%BP5vt&UeCagoRe5woLUUlgxzdGpxH20D0X01E{aWgadIv;8;qgO=S6ltOf7aV z$WOKuQrf(ny_jWk1iJt`$N+H$hRONt5;h(7ZYx zC0X3Ttp`yt`9BXYSQW&nfAdfy#T226y&ykVuOc^R^L*Z1Ca|Mz5R#Ln#rP%{2=v5a zcRMI{yg?dZaSu@e zGF1|&0T#zlHDF2fbOuTb5CGC;kE9FDP1wBxN*gfkVQ(Gtu<3_HKiHXIrRYuor4F9W;W-=>gld+cICxU_v1S&!8~* zC8L4@+y@{zEI!!0nyrw9QDkx=zj(a@1Ke`3HU$D6$FNv{k%2)3=oh##zK(vLZmz*0 zdcJOi9Fp2UG5S!72-pj6qyG9q{Q{_?eGerZ#NZJN>d*s%R6Vx8lP`OJe1mlU8h1aod); z{r|sXei0h6FW)^CSk#yPYnK!=f|GVbY3K5+4Q?d#$af3~#V;KyOXjQ!K|BUGns zTG_&TNpHooXW6G;=asnnHs6_NvC!p3^%b_&hO9+f7aVol7j@ZllABcEr$*<){gr}J z-#9V~^)8!T6;(eI^^QBR*tF>2_xURmPI3GCg>1O+s^iN)b+ZeiQ71n|d_HEe(Z50B zMWVlomBtG0!&5cB{aUj(dscn-K8+=7-=7ye60^hM^#u{r?Q0@-$mn%1yHWVH;mdiq z>zs*SG)*t%3ogAhU&uZ zrEK+kSu^pVQs*+2RDmPGcX~HZ@%yB6u($4j!KVBB%rbZ$nNE1WdWr#?Cs)bNE)Axn zHj@rxuNw>*GHySo9Q(|uz;VQrO)&Nn&)rQa3BuM}&M%yBCc*PkL1X{H7B`dV-E)j* z9W}epBh0pe>rzi!rO1*Er!I#c;z+3EEApz3lwd9KT&8@a-_Ue|x={K7F-MV|lY)(o z^B(`}JXg3$rP*CwouS+h5MXh!b@H|wx2$6-_mNK z;Ie~8GlZW#*!QQ~mh|C5)3+;k)C5+%=m>N#NJxvbAb`K2%~XLUhC;GsELmdea$ zrm*%g)E`=q^5)<=b<4$mHd5&-zMn(gCjVgj{kP|o!gU7c<$h09-pzWGrtEoL>_*lj zDZ_^6tOx&IdG%@v_Zk@`+zwbR=%=+Cm z`MiC(KyR#3x%p1l!XxG3y|wG3cf_7aI9%o>$G=4?`({T{%ZkNjYrpNCyz;sDsgB%0 zsn_k5s}o;0H>5kCx4&3_@N{vBbxPreq;8eZd*e-Nt5aeXcdVUPAM*QSeaP{()|2kJ z8+2d#ti8@#mCvv98k2bRzVgiXKkubdV%mJv9poCRzK3qHM_C9XPb2UqCbsW z{3Gr@e*eX8rpy`3HR;+`4_@~E@8{TESXWb?^eO&pyo6ol)cxN-t=q8o@)hyF%mLo) z9Io@W`$n=bFn~%XNL`Z1FKWS$T-@a*mZj!FDgr{~F|ZI9WmW_ha0|JWREa9V!8-K7 zI`kn`1s*#lU*HyF|H;O{U<%QoUXY)wSCN|ow&~WNrVvnBjioT&tjv?k#9j@Qw1H@x z{9lNVV8t}~3SSSfUINz0j-VPD2HrY?Sj5-Qj7%cTAXCIW6ktX$ymfR#G6P;&f&GEE zvWFQa>JF;0Vc@OfRBXn<>nS`&PCh0e$BQs&N#l#j&l#1};VuDLfh9x(ydf?DR@#;b z-ESQ?u`(d)UP1|A@n5S*hPno95OQ@35&+fQF!0uqn;pr0pz!el zI}%m}y4%UR^&3WEa26P5Ax56E?r12X+)KJ06r}((c2q#fiU`YW=0K5#rz=ZWp H0K@|T@UPCW delta 1128 zcmcbwvsleJz?+$civa|b^4na&3@E_~qM@LP!Ua;2*c!mLSmD> z*^V%BP5vt&UeCagoRe5woLUUl1Th<3lOWJ+m?{)IuqzkEro1>g7n=>nQ0Ma^J0GSN zyBFjqTM8*{c4aJPk-+9daiB?)`5484DhgTLum=`DvTeDEWvO{cZbJ`QMWA_gJW8^- z!(I=fV)B0;Ua%^NyMO1Q#*isO6?;K`u3klM&gS{NxlHV!Ah1D5PL>wqn_M8!6N}yL zpooS50fx7ZAQn=TFfxfSBSP)|q3LHf02K%@GBAih^nl4Fjq2cJz#iZY(f~^c5EUR( zC4m}X5e`)Ymc*7Y5W4JF9W;6-=>f|d+cICxU_v1S&!8~* zC8L4@+y@{zEI!!0nyrw9QDkx=zj(a@1Ke`3HYEZc$FNv{k%2)3=oh##zK(vLZmz*0 zdcJOi9Fp2UG5S!72-pkXqW=0p{Q{F5_?eGerZ#NZJN>d*s%R6Vx8lP`OJe1mlU8h1aod); z{r|sXei0h6FW)^CSk#yPYnK!=f|GVbY3K5+4Q?d#$af3~#V;KyOXjQ!K|BUGns zTG_&TNpHooXW6G;=asnnHs6_NvC!p3^%b_&hO9+f7aVol7j@ZllABcEr$*<){gr}J z-#9V~^)8!T6;(eI^^QBR*tF>2_xURmPI3GCg>1O+s^iN)b+ZeiQ71n|d_HEe(Z50B zMWVlomBtG0!&5cB{aUj(dscn-K8+=7-=7ye60^hM^#u{r?Q0@-$mn%1yHWVH;mdiq z>zs*SG)*t%3ogAhU&uZ zrEK+kSu^pVQs*+2RDmPGcX~HZ@%yB6u($4j!KVBB%rbZ$nNE1WdWr#?Cs)bNE)Axn zHj@rxuNw>*GHySo9Q(|uz;VQrO)&Nn&)rQa3BuM}&M%yBCc*PkL1X{H7B`dV-E)j* z9W}epBh0pe>rzi!rO1*Er!I#c;z+3EEApz3lwd9KT&8@a-_Ue|x={K7F-MV|lY)(o z^B(`}JXg3$rP*CwouS+h5MXh!b@H|wx2$6-_mNK z;Ie~8GlZW#*!QQ~mh|C5)3+;k)C5+%=m>N#NJxvbAb`K2%~XLUhC;GsELmdea$ zrm*%g)E`=q^5)<=b<4$mHd5&-zMn(gCjVgj{kP|o!gU7c<$h09-pzWGrtEoL>_*lj zDZ_^6tOx&IdG%@v_Zk@`+zwbR=%=+Cm z`MiC(KyR#3x%p1l!XxG3y|wG3cf_7aI9%o>$G=4?`({T{%ZkNjYrpNCyz;sDsgB%0 zsn_k5KR0}JJg|A;^Z768A3U|(CA-NYVx#M%pZD}mhUz1n+w7weU`nz8QJ|z=Wl4?bTjEUv2M3}odf?l z+)`ij?qhk?d>_7NGS@bX%6|BA?7w@WEgns2x1XmKQl6kFoR4H_fUWt!SL464ap36Wd*4c z@YQ)R!$jRdH8u>qb)1ULI7B^#+sMhs1mt)TCM{`vG5I;8k~-WaASSlq#ykZbomnNtRI1~j+A9kitJ8$Z-g!O5rixXcJAQC46{0ZIV848g#J J^-Tc80|1vN+8qD@ delta 1128 zcmcbwvsleJz?+$civa|b^V?j(3@E_~qM@LPzg<$2*c!mLSmD> z*^V%BP5vt&UeCagoRe5woLUUl1Th<3lOWJ+m?{)IuqzkEro1>g7n=>nQ0Ma^J0GSN zyBFjqTM8*{c4aJPk-+9daiB?)`5484DhgTLum=`DvTeDEWvO{cZbJ`QMWA_gJW8^- z!(I=fV)B0;Ua%^NyMN@N#*isO6?;K`u3klM&gS{NxlHV!Ah1D5PL>wqn_M8!6N}yL zpooS50fx7ZAQn=TFfxfSBSOvV=;5FZKm`Jf3=AR=Jz#Q4qdGVlum^a9G{6!9LZ5GbX>x?HeeJ0(@hq#htbmlDB(Z=$TkUXB->!g z2;o$8&5AI+3?R*}+~8!Ti_bb>!h!;jjuwaxP%@irBPxe*3(#8^5OT1@HF-apnkqi9 cjbQ!2G$t0{%?k800|PG*Zf0g+hyt4k0Aq-+5C8xG diff --git a/scm-webapp/src/test/resources/sonia/scm/plugin/scm-f-plugin-1.0.0.smp b/scm-webapp/src/test/resources/sonia/scm/plugin/scm-f-plugin-1.0.0.smp index cfcaae8427b446944574f877a2904199c48a28ed..8f2758f9624cbd31cb9e00921945e304d637262b 100644 GIT binary patch delta 1698 zcmb7E&ui0g6n|+NQ;liTlC~%^i#mp&9Rmj{I?Z82?PddZg2G^9R2{6YHY)QN9SGja zSH0>XcvBBMDE|02cJ(%T$u6?9zHid+HyI>d_CiZb`FuX_ectoB$*-)_>h zzWA$%xGVq|DE>q%1;P)|uLR-Vy&r<_azl*@+so~AUd2vcK`Qc9uY_Zt{W-NTZ0w6# zRK30Qy!AMpJ|0L#z)_V`qugU^RE!uu4Y5YeQO3je5)kC0QkKUK0Zaw}(kRLSsG02O z=3nK0M((2DR{~V6v(~P4x@(N1gGkuOVrP*m8~Lg_URS;Fa*$rgY~-kp_j8 z>s*q-k4<(YjY(ni`CF|RB6DAnXqGNGIfd)fa|bj%fN1ux~TPeA@C z>TJq$^B1*_|75$)oOmGvFvuP!bsbP?S4e8?8$@!qI1-u2b-rl>Y~gzPI9hcHR|}xx Ma|e&_vrfPL2aCCNr~m)} delta 865 zcmZ8fze~eF6uvaI^;gq!#RA2T`NVH@FCPaQD4Cd%gDEaJhHi``&l&%j5j#^gyMghKFg~wAP0RA#S0XUnrKH z9Hvb&22F5KFG{WUP78$Tm+;n)TDO!@5To^-JC{iSy*4 zlkYr!iLue!vDDoEC2WB|Bi~9xt(26Isj~THaH>3zFRsWl7AW25fC!?N71}n&qY{m7 z^#BeKV9S6K`oIGjo}iD=&0WD}{NKRAza?w!jW8iEpfBC2ggx*-kUK-2@s!@p2mW@1 zyCXN9MSFN))30HvX~(S28*PC_s5QdOlgSLQPa>-_L~el(feXI?;lIACE`gR3v-Z85tNvkd5(m^z(Fc4Gz)sbpyMj3m9}b z+%Yk7HzO#xMeBKyO(;&zB`(;o27)-cQ3W}r>6v+Y6}dU^a53GD8kOwOxP%3a1wXPO zxrt?|dBnL0w$IXlMwZJNKv57#=u|-Ho*)M^$xg$eDgQ% zC?@tIV1(F!br?_X;^QKiU?xxDYX--ZxV-`>F2UffBZwkCn=mqoFoR>8ACysG;H{(E z{)=fgzL2{2hDD%L;TgMN;)Bw-ZM23n?04UeM zz>-F5V1__3gNQr?FAm`W#O5iuVFJKh1v3j6=@vYb_j4;Fq77#j zLl^+eWJm@~2N__1NclJnnEYK(jt`-4Nh32aP@g%$d^R~s2vy%MexN=RP;N7WCv}i> Wu%q_q@B(2lFnw$k0Pz6Uw}q4d delta 842 zcmbQGb4!&qz?+$cYw`zfQF~4X1_oc(5Jz24KR11lum}SO5Kf)g?h2HF0}&t#uFTib z&(qB{I7H9aZL%fXZbq<4f*e34$vKI|#i_+$gTSJUf|Dl-iXiM`6b14n5PTh8WP^*7 zbCKLI4b2VmAgz;mgt#_OU{7R`067y~i8#=-$^Qk#fhyuz!0r+kLvna-Vp(b)!g0)s zK-CJoN(kpL>w!h|Wf38fhvo`XunM!yWxP>L>_F=nY`_A>leq-ACYuN}LqbCu6s`~; z!0^@)#DWLkt4a4WOAvtcm@cZi@OOfA@}65QaJL^4cN5Ez$G z05UEdtU`bCcM&;$m^jFJi^1Y%ldD8g#jW^2CW50?EWn!;=voE_ULf4e%)lTBHUR)L C>7GOY From 598a4e6f321425ccd5a8ac2d5608031c3fc93774 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 30 Jul 2019 16:49:24 +0200 Subject: [PATCH 04/30] fix Mapping / implement endpoint --- .../sonia/scm/plugin/PluginInformation.java | 8 ++--- .../scm/api/v2/resources/PluginDtoMapper.java | 7 ++++ .../scm/api/v2/resources/PluginResource.java | 9 +++-- .../scm/plugin/DefaultPluginManager.java | 34 +++++-------------- .../sonia/scm/plugin/PluginCenterDto.java | 13 +++++-- .../scm/plugin/PluginCenterDtoMapper.java | 32 ++++++++++++++++- 6 files changed, 67 insertions(+), 36 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 663fd81f9f..74cca02d92 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -46,7 +46,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; -import java.util.List; +import java.util.Map; //~--- JDK imports ------------------------------------------------------------ @@ -288,7 +288,7 @@ public class PluginInformation * * @return */ - public List getLinks() { + public Map getLinks() { return links; } @@ -391,7 +391,7 @@ public class PluginInformation * * @param links */ - public void setLinks(List links) { + public void setLinks(Map links) { this.links = links; } @@ -419,6 +419,6 @@ public class PluginInformation private String version; /** Field description */ - private List links; + private Map links; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 7b2108cd0e..4886138a6b 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -5,6 +5,8 @@ import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginWrapper; import javax.inject.Inject; +import java.util.Map; + import static de.otto.edison.hal.Links.linkingTo; public class PluginDtoMapper { @@ -25,6 +27,11 @@ public class PluginDtoMapper { .self(resourceLinks.plugin() .self(pluginInformation.getName())); + for (Object link : pluginInformation.getLinks().values()) { + System.out.println("Link is = " + link.toString()); + linksBuilder.item(((Map) link).values().iterator().next().toString()); + } + PluginDto pluginDto = new PluginDto(linksBuilder.build()); pluginDto.setName(pluginInformation.getName()); pluginDto.setCategory(pluginInformation.getCategory() != null ? pluginInformation.getCategory() : "Miscellaneous"); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java index 99c61191fa..be57e3f674 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java @@ -9,6 +9,7 @@ import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; +import sonia.scm.plugin.PluginState; import sonia.scm.plugin.PluginWrapper; import sonia.scm.web.VndMediaType; @@ -22,6 +23,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; @@ -34,7 +36,7 @@ public class PluginResource { private final PluginManager pluginManager; @Inject - public PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginCenter pluginCenter1, PluginManager pluginManager) { + public PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) { this.pluginLoader = pluginLoader; this.collectionMapper = collectionMapper; this.mapper = mapper; @@ -105,7 +107,10 @@ public class PluginResource { @Produces(VndMediaType.PLUGIN_COLLECTION) public Response getAvailablePlugins() { PluginPermissions.read().check(); - Collection plugins = pluginManager.getAvailable(); + Collection plugins = pluginManager.getAvailable() + .stream() + .filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE)) + .collect(Collectors.toList()); return Response.ok(collectionMapper.map(plugins)).build(); } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index a62916ed41..93e2e9aaa1 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -75,11 +75,14 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; 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 @@ -592,8 +595,8 @@ public class DefaultPluginManager implements PluginManager { PluginCenter center = null; // cache.get(PluginCenter.class.getName()); - if (center == null) - { +// if (center == null) +// { synchronized (DefaultPluginManager.class) { String pluginUrl = configuration.getPluginUrl(); @@ -611,7 +614,8 @@ public class DefaultPluginManager implements PluginManager { center = new PluginCenter(); PluginCenterDto pluginCenterDto = httpClient.get(pluginUrl).request().contentFromJson(PluginCenterDto.class); - center.setPlugins(mapPluginsFromPluginCenter(pluginCenterDto.getEmbedded().getPlugins())); + Set pluginInformationSet = map(pluginCenterDto.getEmbedded().getPlugins()); + center.setPlugins(pluginInformationSet); preparePlugins(center); cache.put(PluginCenter.class.getName(), center); @@ -632,33 +636,11 @@ public class DefaultPluginManager implements PluginManager } } } - } +// } return center; } - private Set mapPluginsFromPluginCenter(List plugins) { - HashSet pluginInformationSet = new HashSet<>(); - - for (PluginCenterDto.Plugin plugin : plugins) { - - PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setName(plugin.getName()); - pluginInformation.setAuthor(plugin.getAuthor()); - pluginInformation.setCategory(plugin.getCategory()); - pluginInformation.setVersion(plugin.getVersion()); - pluginInformation.setDescription(plugin.getDescription()); - - if (plugin.getConditions() != null) { - PluginCenterDto.Condition condition = plugin.getConditions(); - pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); - } - - pluginInformationSet.add(pluginInformation); - } - return pluginInformationSet; - } - /** * Method description * diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java index 468249157a..f15c646c03 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -9,6 +9,7 @@ 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) @@ -56,8 +57,7 @@ public final class PluginCenterDto implements Serializable { private Dependency dependencies; @XmlElement(name = "_links") - private Links links; - + private Map links; } @XmlAccessorType(XmlAccessType.FIELD) @@ -81,7 +81,14 @@ public final class PluginCenterDto implements Serializable { @XmlRootElement(name = "_links") @Getter static class Links { - private String download; + private Link link; + private boolean templated; + } + + @XmlAccessorType(XmlAccessType.FIELD) + @Getter + static class Link { + private String url; } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java index 77593fe56a..cca5da6520 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -1,5 +1,35 @@ package sonia.scm.plugin; -public class PluginCenterDtoMapper { +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +public class PluginCenterDtoMapper { + + public static Set map(List plugins) { + HashSet pluginInformationSet = new HashSet<>(); + + for (PluginCenterDto.Plugin plugin : plugins) { + + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setName(plugin.getName()); + pluginInformation.setAuthor(plugin.getAuthor()); + pluginInformation.setCategory(plugin.getCategory()); + pluginInformation.setVersion(plugin.getVersion()); + pluginInformation.setDescription(plugin.getDescription()); + + if (plugin.getConditions() != null) { + PluginCenterDto.Condition condition = plugin.getConditions(); + pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); + } + + if (plugin.getLinks() != null) { + pluginInformation.setLinks(plugin.getLinks()); + } + + pluginInformationSet.add(pluginInformation); + } + return pluginInformationSet; + } } From a9744d8df1b08da4d81010bae2aa6fbc7067625d Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 31 Jul 2019 10:27:52 +0200 Subject: [PATCH 05/30] refactor plugin endpoints --- .../sonia/scm/plugin/PluginInformation.java | 24 ---- .../v2/resources/AvailablePluginResource.java | 106 ++++++++++++++++++ .../api/v2/resources/IndexDtoGenerator.java | 3 +- ...urce.java => InstalledPluginResource.java} | 30 +---- .../resources/PluginDtoCollectionMapper.java | 16 ++- .../scm/api/v2/resources/PluginDtoMapper.java | 21 ++-- .../api/v2/resources/PluginRootResource.java | 19 ++-- .../scm/api/v2/resources/ResourceLinks.java | 64 ++++++++--- .../scm/plugin/DefaultPluginManager.java | 10 +- .../sonia/scm/plugin/PluginCenterDto.java | 14 +-- .../scm/plugin/PluginCenterDtoMapper.java | 4 - .../api/v2/resources/ResourceLinksMock.java | 6 +- 12 files changed, 210 insertions(+), 107 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java rename scm-webapp/src/main/java/sonia/scm/api/v2/resources/{PluginResource.java => InstalledPluginResource.java} (75%) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 74cca02d92..de0a3ca1e9 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -282,16 +282,6 @@ public class PluginInformation return version; } - /** - * Method description - * - * - * @return - */ - public Map getLinks() { - return links; - } - /** * Method description * @@ -384,17 +374,6 @@ public class PluginInformation this.version = version; } - - /** - * Method description - * - * - * @param links - */ - public void setLinks(Map links) { - this.links = links; - } - //~--- fields --------------------------------------------------------------- /** Field description */ @@ -418,7 +397,4 @@ public class PluginInformation /** Field description */ private String version; - /** Field description */ - private Map links; - } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java new file mode 100644 index 0000000000..0f4864d2c0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -0,0 +1,106 @@ +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.PluginLoader; +import sonia.scm.plugin.PluginManager; +import sonia.scm.plugin.PluginPermissions; +import sonia.scm.plugin.PluginState; +import sonia.scm.plugin.PluginWrapper; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +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.ArrayList; +import java.util.Collection; +import java.util.List; +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 PluginDtoMapper dtoMapper; + private final PluginManager pluginManager; + + @Inject + public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginDtoMapper dtoMapper, PluginManager pluginManager) { + this.collectionMapper = collectionMapper; + this.dtoMapper = dtoMapper; + this.pluginManager = pluginManager; + } + + /** + * 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 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 = 500, condition = "internal server error") + }) + @TypeHint(CollectionDto.class) + @Produces(VndMediaType.PLUGIN) + public Response getAvailablePlugin(@PathParam("name") String name, @PathParam("version") String version) { + PluginPermissions.read().check(); + Optional plugin = pluginManager.getAvailable() + .stream() + .filter(p -> p.getId().equals(name + ":" + version)) + .findFirst(); + return Response.ok(dtoMapper.map(plugin.get())).build(); + } + + /** + * Returns 200 when plugin installation is successful triggered. + * + * @return HTTP Status. + */ + @POST + @Path("/{name}/{version}/install") + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @TypeHint(CollectionDto.class) + @Produces(VndMediaType.PLUGIN) + public Response installPlugin(@PathParam("name") String name, @PathParam("version") String version) { + PluginPermissions.manage().check(); + pluginManager.install(name + ":" + version); + return Response.ok().build(); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java index c7b52861dc..906ff66759 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java @@ -42,7 +42,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())); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java similarity index 75% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index be57e3f674..246bbf379f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -15,6 +15,7 @@ import sonia.scm.web.VndMediaType; import javax.inject.Inject; 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; @@ -28,7 +29,7 @@ import java.util.stream.Collectors; 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; @@ -36,7 +37,7 @@ public class PluginResource { private final PluginManager pluginManager; @Inject - public PluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) { + public InstalledPluginResource(PluginLoader pluginLoader, PluginDtoCollectionMapper collectionMapper, PluginDtoMapper mapper, PluginManager pluginManager) { this.pluginLoader = pluginLoader; this.collectionMapper = collectionMapper; this.mapper = mapper; @@ -70,7 +71,7 @@ public class PluginResource { * @return installed plugin with specified id */ @GET - @Path("{id}") + @Path("/{id}") @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 404, condition = "not found"), @@ -91,27 +92,4 @@ public class PluginResource { throw notFound(entity(Plugin.class, id)); } } - - /** - * Returns a collection of available plugins. - * - * @return collection of available plugins. - */ - @GET - @Path("/available") - @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 plugins = pluginManager.getAvailable() - .stream() - .filter(plugin -> plugin.getState().equals(PluginState.AVAILABLE)) - .collect(Collectors.toList()); - return Response.ok(collectionMapper.map(plugins)).build(); - } - } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java index 0bb5bd3610..5d8746c211 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoCollectionMapper.java @@ -27,16 +27,24 @@ public class PluginDtoCollectionMapper { public HalRepresentation map(List plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); - return new HalRepresentation(createLinks(), embedDtos(dtos)); + return new HalRepresentation(createInstalledPluginsLinks(), embedDtos(dtos)); } public HalRepresentation map(Collection plugins) { List dtos = plugins.stream().map(mapper::map).collect(toList()); - return new HalRepresentation(createLinks(), embedDtos(dtos)); + return new HalRepresentation(createAvailablePluginsLinks(), embedDtos(dtos)); } - private Links createLinks() { - String baseUrl = resourceLinks.pluginCollection().self(); + 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()); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 4886138a6b..9604ccbcc0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -2,11 +2,11 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; import sonia.scm.plugin.PluginInformation; +import sonia.scm.plugin.PluginState; import sonia.scm.plugin.PluginWrapper; import javax.inject.Inject; -import java.util.Map; - +import static de.otto.edison.hal.Link.*; import static de.otto.edison.hal.Links.linkingTo; public class PluginDtoMapper { @@ -23,13 +23,18 @@ public class PluginDtoMapper { } public PluginDto map(PluginInformation pluginInformation) { - Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.plugin() - .self(pluginInformation.getName())); + Links.Builder linksBuilder; + if (pluginInformation.getState() != null && pluginInformation.getState().equals(PluginState.AVAILABLE)) { + linksBuilder = linkingTo() + .self(resourceLinks.availablePlugin() + .self(pluginInformation.getName(), pluginInformation.getVersion())); - for (Object link : pluginInformation.getLinks().values()) { - System.out.println("Link is = " + link.toString()); - linksBuilder.item(((Map) link).values().iterator().next().toString()); + linksBuilder.single(link("install", resourceLinks.availablePlugin().install(pluginInformation.getName(), pluginInformation.getVersion()))); + } + else { + linksBuilder = linkingTo() + .self(resourceLinks.installedPlugin() + .self(pluginInformation.getName())); } PluginDto pluginDto = new PluginDto(linksBuilder.build()); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginRootResource.java index e9b0f0a997..79c46369a3 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginRootResource.java @@ -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 pluginResourceProvider; + private Provider installedPluginResourceProvider; + private Provider availablePluginResourceProvider; @Inject - public PluginRootResource(Provider pluginResourceProvider) { - this.pluginResourceProvider = pluginResourceProvider; + public PluginRootResource(Provider installedPluginResourceProvider, Provider 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(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java index 1d06659649..268f5f8619 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java @@ -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(); } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 93e2e9aaa1..b48113e66d 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -186,8 +186,6 @@ public class DefaultPluginManager implements PluginManager PluginCenter center = getPluginCenter(); - // pluginHandler.install(id); - for (PluginInformation plugin : center.getPlugins()) { String pluginId = plugin.getId(); @@ -593,10 +591,10 @@ public class DefaultPluginManager implements PluginManager */ private PluginCenter getPluginCenter() { - PluginCenter center = null; // cache.get(PluginCenter.class.getName()); + PluginCenter center = cache.get(PluginCenter.class.getName()); -// if (center == null) -// { + if (center == null) + { synchronized (DefaultPluginManager.class) { String pluginUrl = configuration.getPluginUrl(); @@ -636,7 +634,7 @@ public class DefaultPluginManager implements PluginManager } } } -// } + } return center; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java index f15c646c03..c7ed6ec3c4 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -57,7 +57,7 @@ public final class PluginCenterDto implements Serializable { private Dependency dependencies; @XmlElement(name = "_links") - private Map links; + private Map links; } @XmlAccessorType(XmlAccessType.FIELD) @@ -77,18 +77,10 @@ public final class PluginCenterDto implements Serializable { private String name; } - @XmlAccessorType(XmlAccessType.FIELD) - @XmlRootElement(name = "_links") - @Getter - static class Links { - private Link link; - private boolean templated; - } - @XmlAccessorType(XmlAccessType.FIELD) @Getter static class Link { - private String url; + private String href; + private boolean templated; } - } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java index cca5da6520..8e8520f50e 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -24,10 +24,6 @@ public class PluginCenterDtoMapper { pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); } - if (plugin.getLinks() != null) { - pluginInformation.setLinks(plugin.getLinks()); - } - pluginInformationSet.add(pluginInformation); } return pluginInformationSet; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java index 539b5c8d99..1aef4e57cb 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java @@ -36,8 +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.pluginCollection()).thenReturn(new ResourceLinks.PluginCollectionLinks(uriInfo)); - when(resourceLinks.plugin()).thenReturn(new ResourceLinks.PluginLinks(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)); From 8a8942cbc4731c7828b42bf5a67e87056f124c72 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 31 Jul 2019 10:43:39 +0200 Subject: [PATCH 06/30] cleanup / fix endpoints --- .../v2/resources/AvailablePluginResource.java | 19 +++++++++---------- .../v2/resources/InstalledPluginResource.java | 16 +++++----------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index 0f4864d2c0..7899c9a9a7 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -5,11 +5,9 @@ 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.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; import sonia.scm.plugin.PluginState; -import sonia.scm.plugin.PluginWrapper; import sonia.scm.web.VndMediaType; import javax.inject.Inject; @@ -19,9 +17,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -74,7 +70,7 @@ public class AvailablePluginResource { @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") }) - @TypeHint(CollectionDto.class) + @TypeHint(PluginDto.class) @Produces(VndMediaType.PLUGIN) public Response getAvailablePlugin(@PathParam("name") String name, @PathParam("version") String version) { PluginPermissions.read().check(); @@ -82,12 +78,17 @@ public class AvailablePluginResource { .stream() .filter(p -> p.getId().equals(name + ":" + version)) .findFirst(); - return Response.ok(dtoMapper.map(plugin.get())).build(); + if (plugin.isPresent()) { + return Response.ok(plugin.get()).build(); + } else { + throw notFound(entity(Plugin.class, name)); + } } /** - * Returns 200 when plugin installation is successful triggered. - * + * Triggers plugin installation. + * @param name plugin artefact name + * @param version plugin version * @return HTTP Status. */ @POST @@ -96,8 +97,6 @@ public class AvailablePluginResource { @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") }) - @TypeHint(CollectionDto.class) - @Produces(VndMediaType.PLUGIN) public Response installPlugin(@PathParam("name") String name, @PathParam("version") String version) { PluginPermissions.manage().check(); pluginManager.install(name + ":" + version); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java index 246bbf379f..f10912e5ac 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InstalledPluginResource.java @@ -4,27 +4,21 @@ 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.PluginCenter; -import sonia.scm.plugin.PluginInformation; import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginManager; import sonia.scm.plugin.PluginPermissions; -import sonia.scm.plugin.PluginState; import sonia.scm.plugin.PluginWrapper; import sonia.scm.web.VndMediaType; import javax.inject.Inject; 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.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; @@ -66,12 +60,12 @@ public class InstalledPluginResource { /** * 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"), @@ -79,17 +73,17 @@ public class InstalledPluginResource { }) @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 = pluginLoader.getInstalledPlugins() .stream() - .filter(plugin -> id.equals(plugin.getPlugin().getInformation().getName(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)); } } } From 508e5bbf3bba80a519719335e36b4327f0fa8ccb Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 31 Jul 2019 15:40:35 +0200 Subject: [PATCH 07/30] implemented ui for available plugins page --- .../packages/ui-components/src/CardColumn.js | 38 ++++++- scm-ui/src/admin/containers/Admin.js | 106 ++++++++++++------ .../admin/plugins/components/PluginEntry.js | 27 ++++- .../plugins/containers/PluginsOverview.js | 37 +++++- scm-ui/src/modules/indexResource.js | 8 +- 5 files changed, 165 insertions(+), 51 deletions(-) diff --git a/scm-ui-components/packages/ui-components/src/CardColumn.js b/scm-ui-components/packages/ui-components/src/CardColumn.js index e1eb65255a..65710c7be2 100644 --- a/scm-ui-components/packages/ui-components/src/CardColumn.js +++ b/scm-ui-components/packages/ui-components/src/CardColumn.js @@ -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 { }; 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 {
{avatar}
-
+
-
+

{title}

{description}

+ {contentRight && contentRight}
-
+
{footerLeft}
{footerRight}
diff --git a/scm-ui/src/admin/containers/Admin.js b/scm-ui/src/admin/containers/Admin.js index 58e37a0a7c..22c6184548 100644 --- a/scm-ui/src/admin/containers/Admin.js +++ b/scm-ui/src/admin/containers/Admin.js @@ -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 { 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 { }; 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 { - - + + ( - + )} /> ( - + )} /> ( - + )} /> ( - + )} /> { ( - + )} /> ( - - )} + render={() => } /> { icon="fas fa-info-circle" label={t("admin.menu.informationNavLink")} /> - { - links.plugins && - - - {/* Activate this again after available plugins page is created */} - {/**/} - - } + {(availablePluginsLink || installedPluginsLink) && ( + + {installedPluginsLink && ( + + )} + {availablePluginsLink && ( + + )} + + )} { const mapStateToProps = (state: any) => { const links = getLinks(state); + const availablePluginsLink = getAvailablePluginsLink(state); + const installedPluginsLink = getInstalledPluginsLink(state); return { - links + links, + availablePluginsLink, + installedPluginsLink }; }; diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index a8cdaad915..3693c2e284 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -1,11 +1,21 @@ //@flow import React from "react"; +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: "all" + } }; class PluginEntry extends React.Component { @@ -13,6 +23,17 @@ class PluginEntry extends React.Component { return ; }; + createContentRight = (plugin: Plugin) => { + const { classes } = this.props; + if (plugin._links && plugin._links.install && plugin._links.install.href) { + return ( + + + + ); + } + }; + createFooterLeft = (plugin: Plugin) => { return {plugin.author}; }; @@ -24,6 +45,7 @@ class PluginEntry extends React.Component { 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); @@ -34,6 +56,7 @@ class PluginEntry extends React.Component { avatar={avatar} title={plugin.name} description={plugin.description} + contentRight={contentRight} footerLeft={footerLeft} footerRight={footerRight} /> @@ -41,4 +64,4 @@ class PluginEntry extends React.Component { } } -export default PluginEntry; +export default injectSheet(styles)(PluginEntry); diff --git a/scm-ui/src/admin/plugins/containers/PluginsOverview.js b/scm-ui/src/admin/plugins/containers/PluginsOverview.js index 7a3fc7ec4a..5a8a46f884 100644 --- a/scm-ui/src/admin/plugins/containers/PluginsOverview.js +++ b/scm-ui/src/admin/plugins/containers/PluginsOverview.js @@ -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 { 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 }; }; diff --git a/scm-ui/src/modules/indexResource.js b/scm-ui/src/modules/indexResource.js index 9bfa620674..aa8ebad5a2 100644 --- a/scm-ui/src/modules/indexResource.js +++ b/scm-ui/src/modules/indexResource.js @@ -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) { From c2e7ecd164a6d82a5ca4bdfc5e0cf8b79e894f31 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 31 Jul 2019 16:59:13 +0200 Subject: [PATCH 08/30] add tests --- .../v2/resources/AvailablePluginResource.java | 4 +- .../v2/resources}/PluginCenterDto.java | 13 +- .../v2/resources}/PluginCenterDtoMapper.java | 5 +- .../scm/plugin/DefaultPluginManager.java | 6 +- .../InstalledPluginResourceTest.java | 124 +++++++++++ .../resources/PluginCenterDtoMapperTest.java | 91 ++++++++ .../scm/api/v2/installedPlugins-001.json | 201 ++++++++++++++++++ .../sonia/scm/plugin/plugincenter-001.json | 72 +++++++ 8 files changed, 504 insertions(+), 12 deletions(-) rename scm-webapp/src/main/java/sonia/scm/{plugin => api/v2/resources}/PluginCenterDto.java (87%) rename scm-webapp/src/main/java/sonia/scm/{plugin => api/v2/resources}/PluginCenterDtoMapper.java (89%) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java create mode 100644 scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json create mode 100644 scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index 7899c9a9a7..ca8f3eba29 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -27,13 +27,11 @@ import static sonia.scm.NotFoundException.notFound; public class AvailablePluginResource { private final PluginDtoCollectionMapper collectionMapper; - private PluginDtoMapper dtoMapper; private final PluginManager pluginManager; @Inject - public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginDtoMapper dtoMapper, PluginManager pluginManager) { + public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginManager pluginManager) { this.collectionMapper = collectionMapper; - this.dtoMapper = dtoMapper; this.pluginManager = pluginManager; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java similarity index 87% rename from scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java index c7ed6ec3c4..2b72e7fddc 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java @@ -1,7 +1,9 @@ -package sonia.scm.plugin; +package sonia.scm.api.v2.resources; import com.google.common.collect.ImmutableList; +import lombok.AllArgsConstructor; import lombok.Getter; +import lombok.Setter; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -24,7 +26,7 @@ public final class PluginCenterDto implements Serializable { @XmlRootElement(name = "_embedded") @XmlAccessorType(XmlAccessType.FIELD) - static class Embedded { + public static class Embedded { @XmlElement(name = "plugins") private List plugins; @@ -40,7 +42,8 @@ public final class PluginCenterDto implements Serializable { @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "plugins") @Getter - static class Plugin { + @AllArgsConstructor + public static class Plugin { private String name; private String displayName; @@ -63,7 +66,8 @@ public final class PluginCenterDto implements Serializable { @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "conditions") @Getter - static class Condition { + @AllArgsConstructor + public static class Condition { private String os; private String arch; @@ -73,6 +77,7 @@ public final class PluginCenterDto implements Serializable { @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "dependencies") @Getter + @AllArgsConstructor static class Dependency { private String name; } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java similarity index 89% rename from scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java index 8e8520f50e..4ec5a48667 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java @@ -1,4 +1,7 @@ -package sonia.scm.plugin; +package sonia.scm.api.v2.resources; + +import sonia.scm.plugin.PluginCondition; +import sonia.scm.plugin.PluginInformation; import java.util.Collections; import java.util.HashSet; diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index b48113e66d..d17a693969 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -46,6 +46,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContextProvider; +import sonia.scm.api.v2.resources.PluginCenterDto; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.config.ScmConfiguration; @@ -67,21 +68,18 @@ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; import javax.xml.bind.JAXB; import sonia.scm.net.ahc.AdvancedHttpClient; -import static sonia.scm.plugin.PluginCenterDtoMapper.*; +import static sonia.scm.api.v2.resources.PluginCenterDtoMapper.*; /** * TODO replace aether stuff. diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java new file mode 100644 index 0000000000..ffd480b530 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -0,0 +1,124 @@ +package sonia.scm.api.v2.resources; + +import com.google.common.io.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.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.PluginManager; +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.net.URL; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +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; + private URL resources = Resources.getResource("sonia/scm/api/v2/installedPlugins-001.json"); + + @Mock + Provider installedPluginResourceProvider; + + @Mock + Provider availablePluginResourceProvider; + + @Mock + private PluginManager pluginManager; + + @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 { + PluginInformation pluginInformation = new PluginInformation(); + pluginInformation.setVersion("2.0.0"); + pluginInformation.setName("plugin-name"); + 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("plugin-name"); + pluginDto.setVersion("2.0.0"); + //TODO How to mock this? + when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new HalRepresentation()); + + MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); + request.accept(VndMediaType.PLUGIN_COLLECTION); + MockHttpResponse response = new MockHttpResponse(); + + dispatcher.invoke(request, response); + + assertEquals(HttpServletResponse.SC_OK, response.getStatus()); + assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/plugins/installed\"}")); + } + + @Test + void getInstalledPlugin() { + } + + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java new file mode 100644 index 0000000000..ecbc44a1ed --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java @@ -0,0 +1,91 @@ +package sonia.scm.api.v2.resources; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import sonia.scm.plugin.PluginInformation; + +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.api.v2.resources.PluginCenterDto.*; + +class PluginCenterDtoMapperTest { + + private PluginCenterDtoMapper pluginCenterDtoMapper; + + @BeforeEach + void initMapper() { + pluginCenterDtoMapper = new PluginCenterDtoMapper(); + } + + @Test + void shouldMapSinglePlugin() { + Plugin plugin = new Plugin( + "scm-hitchhiker-plugin", + "SCM Hitchhiker Plugin", + "plugin for hitchhikers", + "Travel", + "2.0.0", + "trillian", + "555000444", + new Condition("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()); + assertThat(result.getDescription()).isEqualTo(plugin.getDescription()); + assertThat(result.getName()).isEqualTo(plugin.getName()); + } + + @Test + void shouldMapMultiplePlugins() { + Plugin plugin1 = new Plugin( + "scm-hitchhiker-plugin", + "SCM Hitchhiker Plugin", + "plugin for hitchhikers", + "Travel", + "2.0.0", + "dent", + "555000444", + new Condition("linux", "amd64","2.0.0"), + new Dependency("scm-review-plugin"), + new HashMap<>()); + + Plugin plugin2 = new Plugin( + "scm-review-plugin", + "SCM Hitchhiker Plugin", + "plugin for hitchhikers", + "Travel", + "2.1.0", + "trillian", + "12345678aa", + new Condition("linux", "amd64","2.0.0"), + new Dependency("scm-review-plugin"), + new HashMap<>()); + + Set resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2)); + + List pluginsList = new ArrayList(resultSet); + + PluginInformation pluginInformation1 = (PluginInformation) pluginsList.get(1); + PluginInformation pluginInformation2 = (PluginInformation) 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); + } +} diff --git a/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json b/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json new file mode 100644 index 0000000000..077851a9fa --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json @@ -0,0 +1,201 @@ +{ + "_links": { + "self": { "href": "http://localhost:8081/scm/api/v2/plugins/installed" } + }, + "_embedded": { + "plugins": [ + { + "name": "scm-issuetracker-plugin", + "category": "Library Plugin", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Helper classes for issuetracker plugins.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-issuetracker-plugin" + } + } + }, + { + "name": "scm-webhook-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Notify a remote webserver whenever a repository is pushed to.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-webhook-plugin" + } + } + }, + { + "name": "scm-jenkins-plugin", + "category": "Continuous Integration", + "version": "2.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "This plugin will ping your Jenkins CI server when a new commit is pushed to SCM-Manager.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-jenkins-plugin" + } + } + }, + { + "name": "scm-legacy-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "The easiest way to share your Git, Mercurial\n and Subversion repositories over http.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-legacy-plugin" + } + } + }, + { + "name": "scm-jira-plugin", + "category": "Issue-Tracking", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "This plugin integrates Atlassian JIRA to SCM-Manager.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-jira-plugin" + } + } + }, + { + "name": "scm-ci-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Cloudogu GmbH", + "description": "SCM-Manager CI Plugin", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-ci-plugin" + } + } + }, + { + "name": "scm-svn-plugin", + "category": "Subversion", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Plugin for the version control system Subversion", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-svn-plugin" + } + } + }, + { + "name": "scm-hg-plugin", + "category": "Mercurial", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Plugin for the version control system Mercurial", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-hg-plugin" + } + } + }, + { + "name": "scm-review-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Cloudogu GmbH", + "description": "SCM-Manager Review Plugin", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-review-plugin" + } + } + }, + { + "name": "scm-git-plugin", + "category": "Git", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Plugin for the version control system Git", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-git-plugin" + } + } + }, + { + "name": "scm-script-plugin", + "category": "Development", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Script support for scm-manager.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-script-plugin" + } + } + }, + { + "name": "scm-mail-plugin", + "category": "Library Plugin", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "The mail plugin provides an api for sending e-mails.\n This api can be used by other plugins.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-mail-plugin" + } + } + }, + { + "name": "scm-authormapping-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Lookup and transform usernames to the real names stored in the scm-manager user database or in a mapping table.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-authormapping-plugin" + } + } + }, + { + "name": "scm-pushlog-plugin", + "category": "Miscellaneous", + "version": "2.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "Tracks who pushed what to a repository.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-pushlog-plugin" + } + } + }, + { + "name": "scm-branchwp-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "This plugin adds branch write protection for repositories.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-branchwp-plugin" + } + } + }, + { + "name": "scm-notify-plugin", + "category": "Miscellaneous", + "version": "2.0.0-SNAPSHOT", + "author": "Sebastian Sdorra", + "description": "This plugin sends email notifications to a list of subscribed addresses whenever a repo has changes.", + "_links": { + "self": { + "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-notify-plugin" + } + } + } + ] + } +} diff --git a/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json b/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json new file mode 100644 index 0000000000..d66f2f6c6b --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json @@ -0,0 +1,72 @@ +{ + "_embedded": { + "plugins": [ + { + "name": "scm-branchwp-plugin", + "displayName": "SCM Branch WritePermission Plugin", + "description": "This plugin adds branch write protection for repositories.", + "category": "Miscellaneous", + "version": "1.0.0", + "author": "Sebastian Sdorra", + "sha256sum": "f6585d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", + "_links": { + "download": { + "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-branchwp-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-branchwp-plugin-2.0.0-SNAPSHOT.smp" + } + } + }, + { + "name": "scm-pathwp-plugin", + "displayName": "SCM Path WritePermission Plugin", + "description": "This plugin adds path write protection for repositories.", + "category": "Miscellaneous", + "version": "2.1.0", + "author": "Sebastian Sdorra", + "conditions": { + "os": "linux", + "minVersion": "2.1.0" + }, + "sha256sum": "abc85d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", + "_links": { + "download": { + "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-pathwp-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-pathwp-plugin-2.0.0-SNAPSHOT.smp" + } + } + }, + { + "name": "scm-mail-plugin", + "displayName": "SCM Mail Plugin", + "description": "The mail plugin provides an api for sending e-mails. This api can be used by other plugins.", + "category": "Library Plugin", + "version": "2.6.7", + "author": "Sebastian Sdorra", + "conditions": { + "os": "windows" + }, + "sha256sum": "def85d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", + "_links": { + "download": { + "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-mail-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-mail-plugin-2.0.0-SNAPSHOT.smp" + } + } + }, + { + "name": "scm-review-plugin", + "displayName": "SCM Review Plugin", + "description": "SCM-Manager Review Plugin", + "category": "Miscellaneous", + "version": "2.4.0", + "author": "Sebastian Sdorra", + "conditions": { + "arch": "armv7" + }, + "sha256sum": "12385d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", + "_links": { + "download": { + "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-review-plugin/job/develop/lastSuccessfulBuild/artifact/target/scm-review-plugin-2.0.0-SNAPSHOT.smp" + } + } + } + ] + } +} From f699f0ef432244ced6d565f385c4a85bd9f0fd2f Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 31 Jul 2019 17:11:28 +0200 Subject: [PATCH 09/30] fix Test --- .../v2/resources/InstalledPluginResourceTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index ffd480b530..acca905320 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -31,8 +31,7 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -103,8 +102,7 @@ class InstalledPluginResourceTest { PluginDto pluginDto = new PluginDto(); pluginDto.setName("plugin-name"); pluginDto.setVersion("2.0.0"); - //TODO How to mock this? - when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new HalRepresentation()); + when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); request.accept(VndMediaType.PLUGIN_COLLECTION); @@ -112,13 +110,18 @@ class InstalledPluginResourceTest { dispatcher.invoke(request, response); - assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - assertTrue(response.getContentAsString().contains("\"self\":{\"href\":\"/v2/plugins/installed\"}")); + assertThat(HttpServletResponse.SC_OK).isEqualTo(response.getStatus()); + assertThat(response.getContentAsString()).contains("\"marker\":\"x\""); } @Test void getInstalledPlugin() { } + public class MockedResultDto extends HalRepresentation { + public String getMarker() { + return "x"; + } + } } } From 35c5a6951149eb20d0e3714365e9b2bc8eda471b Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 08:38:53 +0200 Subject: [PATCH 10/30] add resource tests --- .../v2/resources/AvailablePluginResource.java | 3 + .../AvailablePluginResourceTest.java | 169 ++++++++++++++++++ .../InstalledPluginResourceTest.java | 72 +++++--- 3 files changed, 222 insertions(+), 22 deletions(-) create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index ca8f3eba29..c3663b82b5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -11,6 +11,7 @@ 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; @@ -66,6 +67,7 @@ public class AvailablePluginResource { @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) @@ -91,6 +93,7 @@ public class AvailablePluginResource { */ @POST @Path("/{name}/{version}/install") + @Consumes(VndMediaType.PLUGIN) @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java new file mode 100644 index 0000000000..1ec1c52e81 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -0,0 +1,169 @@ +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 installedPluginResourceProvider; + + @Mock + Provider availablePluginResourceProvider; + + @Mock + private PluginDtoCollectionMapper collectionMapper; + + @Mock + private PluginManager pluginManager; + + @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)); + + 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 { + + @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 { + 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"; + } + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index acca905320..f6633b4965 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import com.google.common.io.Resources; import de.otto.edison.hal.HalRepresentation; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ThreadContext; @@ -8,6 +7,7 @@ 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; @@ -19,7 +19,6 @@ 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.PluginManager; import sonia.scm.plugin.PluginState; import sonia.scm.plugin.PluginWrapper; import sonia.scm.web.VndMediaType; @@ -28,10 +27,10 @@ import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; -import java.net.URL; 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; @@ -40,7 +39,6 @@ import static org.mockito.Mockito.when; class InstalledPluginResourceTest { private Dispatcher dispatcher; - private URL resources = Resources.getResource("sonia/scm/api/v2/installedPlugins-001.json"); @Mock Provider installedPluginResourceProvider; @@ -48,9 +46,6 @@ class InstalledPluginResourceTest { @Mock Provider availablePluginResourceProvider; - @Mock - private PluginManager pluginManager; - @Mock private PluginLoader pluginLoader; @@ -91,17 +86,8 @@ class InstalledPluginResourceTest { @Test void getInstalledPlugins() throws URISyntaxException, UnsupportedEncodingException { - PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setVersion("2.0.0"); - pluginInformation.setName("plugin-name"); - pluginInformation.setState(PluginState.INSTALLED); - Plugin plugin = new Plugin(2, pluginInformation, null, null, false, null); - PluginWrapper pluginWrapper = new PluginWrapper(plugin, null, null, null); + PluginWrapper pluginWrapper = new PluginWrapper(null, null, null, null); when(pluginLoader.getInstalledPlugins()).thenReturn(Collections.singletonList(pluginWrapper)); - - PluginDto pluginDto = new PluginDto(); - pluginDto.setName("plugin-name"); - pluginDto.setVersion("2.0.0"); when(collectionMapper.map(Collections.singletonList(pluginWrapper))).thenReturn(new MockedResultDto()); MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); @@ -115,13 +101,55 @@ class InstalledPluginResourceTest { } @Test - void getInstalledPlugin() { + 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 { + + @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)); } - public class MockedResultDto extends HalRepresentation { - public String getMarker() { - return "x"; - } + @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"; } } } From 853bf133caa3f3adf43f2e09a40331fa2e42ced0 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 09:14:48 +0200 Subject: [PATCH 11/30] fix tests --- .../scm/api/v2/resources/AvailablePluginResourceTest.java | 6 ++++++ .../scm/api/v2/resources/InstalledPluginResourceTest.java | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java index 1ec1c52e81..da4ad9d29b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -133,6 +133,11 @@ class AvailablePluginResourceTest { @Nested class WithoutAuthorization { + @BeforeEach + void unbindSubject() { + ThreadContext.unbindSubject(); + } + @Test void shouldNotGetAvailablePluginsIfMissingPermission() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/v2/plugins/available"); @@ -153,6 +158,7 @@ class AvailablePluginResourceTest { @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(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index f6633b4965..72aa8d4a5f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -140,7 +140,7 @@ class InstalledPluginResourceTest { @Test void shouldNotGetInstalledPluginIfMissingPermission() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed/pluginName"); -// request.accept(VndMediaType.PLUGIN); + request.accept(VndMediaType.PLUGIN); MockHttpResponse response = new MockHttpResponse(); assertThrows(UnhandledException.class, () -> dispatcher.invoke(request, response)); From 2fe8c154d3593859dbc87c879e97e6273ad9fc58 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 09:57:18 +0200 Subject: [PATCH 12/30] small fixes --- scm-ui/src/admin/plugins/components/PluginEntry.js | 6 +++--- .../scm/api/v2/resources/AvailablePluginResource.java | 6 ++++-- .../scm/api/v2/resources/AvailablePluginResourceTest.java | 7 +++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index 3693c2e284..b901cc0cfb 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -27,9 +27,9 @@ class PluginEntry extends React.Component { const { classes } = this.props; if (plugin._links && plugin._links.install && plugin._links.install.href) { return ( - - - +
console.log(plugin._links.install.href) /*TODO trigger plugin installation*/}> + +
); } }; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java index c3663b82b5..6d5711133f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailablePluginResource.java @@ -29,11 +29,13 @@ public class AvailablePluginResource { private final PluginDtoCollectionMapper collectionMapper; private final PluginManager pluginManager; + private final PluginDtoMapper mapper; @Inject - public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginManager pluginManager) { + public AvailablePluginResource(PluginDtoCollectionMapper collectionMapper, PluginManager pluginManager, PluginDtoMapper mapper) { this.collectionMapper = collectionMapper; this.pluginManager = pluginManager; + this.mapper = mapper; } /** @@ -79,7 +81,7 @@ public class AvailablePluginResource { .filter(p -> p.getId().equals(name + ":" + version)) .findFirst(); if (plugin.isPresent()) { - return Response.ok(plugin.get()).build(); + return Response.ok(mapper.map(plugin.get())).build(); } else { throw notFound(entity(Plugin.class, name)); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java index da4ad9d29b..57564999ef 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AvailablePluginResourceTest.java @@ -52,6 +52,9 @@ class AvailablePluginResourceTest { @Mock private PluginManager pluginManager; + @Mock + private PluginDtoMapper mapper; + @InjectMocks AvailablePluginResource availablePluginResource; @@ -107,6 +110,10 @@ class AvailablePluginResourceTest { 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(); From 1b1d3066648b97c0ab4ff61805a3af9aad0c6db2 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 10:00:49 +0200 Subject: [PATCH 13/30] remove unsed resource --- .../scm/api/v2/installedPlugins-001.json | 201 ------------------ 1 file changed, 201 deletions(-) delete mode 100644 scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json diff --git a/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json b/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json deleted file mode 100644 index 077851a9fa..0000000000 --- a/scm-webapp/src/test/resources/sonia/scm/api/v2/installedPlugins-001.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "_links": { - "self": { "href": "http://localhost:8081/scm/api/v2/plugins/installed" } - }, - "_embedded": { - "plugins": [ - { - "name": "scm-issuetracker-plugin", - "category": "Library Plugin", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Helper classes for issuetracker plugins.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-issuetracker-plugin" - } - } - }, - { - "name": "scm-webhook-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Notify a remote webserver whenever a repository is pushed to.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-webhook-plugin" - } - } - }, - { - "name": "scm-jenkins-plugin", - "category": "Continuous Integration", - "version": "2.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "This plugin will ping your Jenkins CI server when a new commit is pushed to SCM-Manager.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-jenkins-plugin" - } - } - }, - { - "name": "scm-legacy-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "The easiest way to share your Git, Mercurial\n and Subversion repositories over http.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-legacy-plugin" - } - } - }, - { - "name": "scm-jira-plugin", - "category": "Issue-Tracking", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "This plugin integrates Atlassian JIRA to SCM-Manager.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-jira-plugin" - } - } - }, - { - "name": "scm-ci-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Cloudogu GmbH", - "description": "SCM-Manager CI Plugin", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-ci-plugin" - } - } - }, - { - "name": "scm-svn-plugin", - "category": "Subversion", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Plugin for the version control system Subversion", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-svn-plugin" - } - } - }, - { - "name": "scm-hg-plugin", - "category": "Mercurial", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Plugin for the version control system Mercurial", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-hg-plugin" - } - } - }, - { - "name": "scm-review-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Cloudogu GmbH", - "description": "SCM-Manager Review Plugin", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-review-plugin" - } - } - }, - { - "name": "scm-git-plugin", - "category": "Git", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Plugin for the version control system Git", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-git-plugin" - } - } - }, - { - "name": "scm-script-plugin", - "category": "Development", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Script support for scm-manager.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-script-plugin" - } - } - }, - { - "name": "scm-mail-plugin", - "category": "Library Plugin", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "The mail plugin provides an api for sending e-mails.\n This api can be used by other plugins.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-mail-plugin" - } - } - }, - { - "name": "scm-authormapping-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Lookup and transform usernames to the real names stored in the scm-manager user database or in a mapping table.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-authormapping-plugin" - } - } - }, - { - "name": "scm-pushlog-plugin", - "category": "Miscellaneous", - "version": "2.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "Tracks who pushed what to a repository.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-pushlog-plugin" - } - } - }, - { - "name": "scm-branchwp-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "This plugin adds branch write protection for repositories.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-branchwp-plugin" - } - } - }, - { - "name": "scm-notify-plugin", - "category": "Miscellaneous", - "version": "2.0.0-SNAPSHOT", - "author": "Sebastian Sdorra", - "description": "This plugin sends email notifications to a list of subscribed addresses whenever a repo has changes.", - "_links": { - "self": { - "href": "http://localhost:8081/scm/api/v2/plugins/installed/scm-notify-plugin" - } - } - } - ] - } -} From a5681a30619029d559b5e018740f6f12fcc694db Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 10:08:00 +0200 Subject: [PATCH 14/30] remove unsed resource --- .../sonia/scm/plugin/plugincenter-001.json | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json diff --git a/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json b/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json deleted file mode 100644 index d66f2f6c6b..0000000000 --- a/scm-webapp/src/test/resources/sonia/scm/plugin/plugincenter-001.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "_embedded": { - "plugins": [ - { - "name": "scm-branchwp-plugin", - "displayName": "SCM Branch WritePermission Plugin", - "description": "This plugin adds branch write protection for repositories.", - "category": "Miscellaneous", - "version": "1.0.0", - "author": "Sebastian Sdorra", - "sha256sum": "f6585d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", - "_links": { - "download": { - "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-branchwp-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-branchwp-plugin-2.0.0-SNAPSHOT.smp" - } - } - }, - { - "name": "scm-pathwp-plugin", - "displayName": "SCM Path WritePermission Plugin", - "description": "This plugin adds path write protection for repositories.", - "category": "Miscellaneous", - "version": "2.1.0", - "author": "Sebastian Sdorra", - "conditions": { - "os": "linux", - "minVersion": "2.1.0" - }, - "sha256sum": "abc85d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", - "_links": { - "download": { - "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-pathwp-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-pathwp-plugin-2.0.0-SNAPSHOT.smp" - } - } - }, - { - "name": "scm-mail-plugin", - "displayName": "SCM Mail Plugin", - "description": "The mail plugin provides an api for sending e-mails. This api can be used by other plugins.", - "category": "Library Plugin", - "version": "2.6.7", - "author": "Sebastian Sdorra", - "conditions": { - "os": "windows" - }, - "sha256sum": "def85d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", - "_links": { - "download": { - "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-mail-plugin/job/2.0.0/lastSuccessfulBuild/artifact/target/scm-mail-plugin-2.0.0-SNAPSHOT.smp" - } - } - }, - { - "name": "scm-review-plugin", - "displayName": "SCM Review Plugin", - "description": "SCM-Manager Review Plugin", - "category": "Miscellaneous", - "version": "2.4.0", - "author": "Sebastian Sdorra", - "conditions": { - "arch": "armv7" - }, - "sha256sum": "12385d369d3737415f05ea06e99048ac602fadcb76f5056b32b6cc232b9e8331", - "_links": { - "download": { - "href": "https://oss.cloudogu.com/jenkins/job/scm-manager/job/scm-manager-bitbucket/job/scm-review-plugin/job/develop/lastSuccessfulBuild/artifact/target/scm-review-plugin-2.0.0-SNAPSHOT.smp" - } - } - } - ] - } -} From c7f5ae6bc7c61609485cfceb509c1351298c1f9e Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 10:13:18 +0200 Subject: [PATCH 15/30] fix sonar build --- .../sonia/scm/plugin/DefaultPluginManager.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index d17a693969..55b5dd9328 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -614,17 +614,6 @@ public class DefaultPluginManager implements PluginManager 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) { @@ -632,6 +621,9 @@ public class DefaultPluginManager implements PluginManager } } } + if(center == null) { + center = new PluginCenter(); + } } return center; From b102c19f5f2960c8a68a361499dfcf28a1640858 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 1 Aug 2019 11:53:08 +0200 Subject: [PATCH 16/30] fix test --- .../scm/api/v2/resources/InstalledPluginResourceTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java index 72aa8d4a5f..a81eadadb8 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/InstalledPluginResourceTest.java @@ -128,6 +128,11 @@ class InstalledPluginResourceTest { @Nested class WithoutAuthorization { + @BeforeEach + void unbindSubject() { + ThreadContext.unbindSubject(); + } + @Test void shouldNotGetInstalledPluginsIfMissingPermission() throws URISyntaxException { MockHttpRequest request = MockHttpRequest.get("/v2/plugins/installed"); From c58788e1e5572e015c1e80e5889384611cd5f281 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 12 Aug 2019 11:16:47 +0200 Subject: [PATCH 17/30] add avatarUrl / fix conditions_os to list of string --- .../sonia/scm/plugin/PluginInformation.java | 346 +++--------------- .../packages/ui-types/src/Plugin.js | 3 +- .../admin/plugins/components/PluginAvatar.js | 8 +- .../scm/api/v2/resources/PluginCenterDto.java | 4 +- .../v2/resources/PluginCenterDtoMapper.java | 3 +- .../sonia/scm/api/v2/resources/PluginDto.java | 2 +- .../scm/api/v2/resources/PluginDtoMapper.java | 4 +- .../resources/PluginCenterDtoMapperTest.java | 37 +- 8 files changed, 88 insertions(+), 319 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index de0a3ca1e9..99ad1e82e8 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -1,19 +1,19 @@ /** * Copyright (c) 2010, Sebastian Sdorra * All rights reserved. - * + *

* Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + *

* 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. + * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. * 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. - * + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + *

* 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,13 +24,11 @@ * 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. - * + *

* http://bitbucket.org/sdorra/scm-manager - * */ - package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- @@ -39,6 +37,8 @@ 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.Getter; +import lombok.Setter; import sonia.scm.Validateable; import sonia.scm.util.Util; @@ -46,43 +46,38 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; -import java.util.Map; //~--- JDK imports ------------------------------------------------------------ /** - * * @author Sebastian Sdorra */ @StaticPermissions( - value = "plugin", - generatedClass = "PluginPermissions", + 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 -{ +@Getter +@Setter +public class PluginInformation implements PermissionObject, Validateable, Cloneable, Serializable { - /** Field description */ private static final long serialVersionUID = 461382048865977206L; - //~--- methods -------------------------------------------------------------- + private String author; + private String category; + private PluginCondition condition; + private String description; + private String name; + private PluginState state; + private String version; + private String avatarUrl; - /** - * Method description - * - * - * @return - * - * @since 1.11 - */ @Override - public PluginInformation clone() - { + public PluginInformation clone() { PluginInformation clone = new PluginInformation(); clone.setName(name); clone.setAuthor(author); @@ -90,33 +85,22 @@ public class PluginInformation clone.setDescription(description); clone.setState(state); clone.setVersion(version); + clone.setAvatarUrl(avatarUrl); - if (condition != null) - { + if (condition != null) { clone.setCondition(condition.clone()); } return clone; } - /** - * Method description - * - * - * @param obj - * - * @return - */ @Override - public boolean equals(Object obj) - { - if (obj == null) - { + public boolean equals(Object obj) { + if (obj == null) { return false; } - if (getClass() != obj.getClass()) - { + if (getClass() != obj.getClass()) { return false; } @@ -125,276 +109,54 @@ public class PluginInformation //J- return Objects.equal(author, other.author) - && Objects.equal(category, other.category) - && Objects.equal(condition, other.condition) - && Objects.equal(description, other.description) - && Objects.equal(name, other.name) - && Objects.equal(state, other.state) - && Objects.equal(version, other.version); + && Objects.equal(category, other.category) + && Objects.equal(condition, other.condition) + && Objects.equal(description, other.description) + && Objects.equal(name, other.name) + && Objects.equal(state, other.state) + && Objects.equal(version, other.version) + && Objects.equal(avatarUrl, other.avatarUrl); //J+ } - /** - * Method description - * - * - * @return - */ @Override - public int hashCode() - { + public int hashCode() { return Objects.hashCode(author, category, condition, - description, name, state, version); + description, name, state, version, avatarUrl); } - /** - * Method description - * - * - * @return - */ @Override - public String toString() - { + public String toString() { //J- return MoreObjects.toStringHelper(this) - .add("author", author) - .add("category", category) - .add("condition", condition) - .add("description", description) - .add("name", name) - .add("state", state) - .add("version", version) - .toString(); + .add("author", author) + .add("category", category) + .add("condition", condition) + .add("description", description) + .add("name", name) + .add("state", state) + .add("version", version) + .add("avatarUrl", avatarUrl) + .toString(); //J+ } - //~--- get methods ---------------------------------------------------------- - - /** - * 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 - */ @Override - public String getId() - { + public String getId() { return getName(true); } - /** - * Method description - * - * - * @param withVersion - * - * @return - * @since 1.21 - */ - public String getName(boolean withVersion) - { + public String getName(boolean withVersion) { StringBuilder id = new StringBuilder(name); - if (withVersion) - { + if (withVersion) { id.append(":").append(version); } - return id.toString(); } - /** - * Method description - * - * - * @return - */ - public String getName() - { - return name; - } - - /** - * Method description - * - * - * @return - */ - public PluginState getState() - { - return state; - } - - /** - * Method description - * - * - * @return - */ - public String getVersion() - { - return version; - } - - /** - * Method description - * - * - * @return - */ @Override - public boolean isValid() - { + public boolean isValid() { return Util.isNotEmpty(name) && Util.isNotEmpty(version); } - - //~--- set methods ---------------------------------------------------------- - - /** - * 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 name - */ - public void setName(String name) - { - this.name = name; - } - - /** - * Method description - * - * - * @param state - */ - public void setState(PluginState state) - { - this.state = state; - } - - /** - * Method description - * - * - * @param version - */ - public void setVersion(String version) - { - this.version = version; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String author; - - /** Field description */ - private String category; - - /** Field description */ - private PluginCondition condition; - - /** Field description */ - private String description; - - /** Field description */ - private String name; - - /** Field description */ - private PluginState state; - - /** Field description */ - private String version; - } diff --git a/scm-ui-components/packages/ui-types/src/Plugin.js b/scm-ui-components/packages/ui-types/src/Plugin.js index bb9c5e7d88..72e4908a54 100644 --- a/scm-ui-components/packages/ui-types/src/Plugin.js +++ b/scm-ui-components/packages/ui-types/src/Plugin.js @@ -1,11 +1,12 @@ //@flow -import type { Collection, Links } from "./hal"; +import type {Collection, Links} from "./hal"; export type Plugin = { name: string, type: string, version: string, author: string, + avatarUrl: string, description?: string, _links: Links }; diff --git a/scm-ui/src/admin/plugins/components/PluginAvatar.js b/scm-ui/src/admin/plugins/components/PluginAvatar.js index 10408f14bd..42a1fd732b 100644 --- a/scm-ui/src/admin/plugins/components/PluginAvatar.js +++ b/scm-ui/src/admin/plugins/components/PluginAvatar.js @@ -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 { return (

- Logo + Logo

); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java index 2b72e7fddc..423a0ba0d2 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java @@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.Setter; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; @@ -51,6 +50,7 @@ public final class PluginCenterDto implements Serializable { private String category; private String version; private String author; + private String avatarUrl; private String sha256; @XmlElement(name = "conditions") @@ -69,7 +69,7 @@ public final class PluginCenterDto implements Serializable { @AllArgsConstructor public static class Condition { - private String os; + private List os; private String arch; private String minVersion; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java index 4ec5a48667..3a4e8a1947 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java @@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources; import sonia.scm.plugin.PluginCondition; import sonia.scm.plugin.PluginInformation; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -24,7 +23,7 @@ public class PluginCenterDtoMapper { if (plugin.getConditions() != null) { PluginCenterDto.Condition condition = plugin.getConditions(); - pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), Collections.singletonList(condition.getOs()), condition.getArch())); + pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), condition.getOs(), condition.getArch())); } pluginInformationSet.add(pluginInformation); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java index a35c3e848d..75386aed63 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java @@ -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; @@ -16,6 +15,7 @@ public class PluginDto extends HalRepresentation { private String category; private String version; private String author; + private String avatarUrl; private String description; public PluginDto(Links links) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 9604ccbcc0..020e706e43 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -4,9 +4,10 @@ import de.otto.edison.hal.Links; 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.*; +import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; public class PluginDtoMapper { @@ -43,6 +44,7 @@ public class PluginDtoMapper { pluginDto.setVersion(pluginInformation.getVersion()); pluginDto.setAuthor(pluginInformation.getAuthor()); pluginDto.setDescription(pluginInformation.getDescription()); + pluginDto.setAvatarUrl(pluginInformation.getAvatarUrl()); return pluginDto; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java index ecbc44a1ed..1175526f75 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java @@ -12,7 +12,9 @@ import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static sonia.scm.api.v2.resources.PluginCenterDto.*; +import static sonia.scm.api.v2.resources.PluginCenterDto.Condition; +import static sonia.scm.api.v2.resources.PluginCenterDto.Dependency; +import static sonia.scm.api.v2.resources.PluginCenterDto.Plugin; class PluginCenterDtoMapperTest { @@ -32,8 +34,9 @@ class PluginCenterDtoMapperTest { "Travel", "2.0.0", "trillian", + "http://avatar.url", "555000444", - new Condition("linux", "amd64","2.0.0"), + new Condition(Collections.singletonList("linux"), "amd64","2.0.0"), new Dependency("scm-review-plugin"), new HashMap<>()); @@ -44,7 +47,7 @@ class PluginCenterDtoMapperTest { 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()); + assertThat(result.getCondition().getOs().iterator().next()).isEqualTo(plugin.getConditions().getOs().iterator().next()); assertThat(result.getDescription()).isEqualTo(plugin.getDescription()); assertThat(result.getName()).isEqualTo(plugin.getName()); } @@ -52,26 +55,28 @@ class PluginCenterDtoMapperTest { @Test void shouldMapMultiplePlugins() { Plugin plugin1 = new Plugin( - "scm-hitchhiker-plugin", - "SCM Hitchhiker Plugin", - "plugin for hitchhikers", - "Travel", - "2.0.0", - "dent", - "555000444", - new Condition("linux", "amd64","2.0.0"), - new Dependency("scm-review-plugin"), - new HashMap<>()); - - Plugin plugin2 = new Plugin( "scm-review-plugin", "SCM Hitchhiker Plugin", "plugin for hitchhikers", "Travel", "2.1.0", "trillian", + "https://avatar.url", "12345678aa", - new Condition("linux", "amd64","2.0.0"), + 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<>()); From 900e52ad0e95751669608a438f0e5af4aab8d638 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 12 Aug 2019 13:18:30 +0200 Subject: [PATCH 18/30] update dtd --- docs/dtd/plugin/2.0.0-01.dtd | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/docs/dtd/plugin/2.0.0-01.dtd b/docs/dtd/plugin/2.0.0-01.dtd index eec149f3e8..a4f5a2d831 100644 --- a/docs/dtd/plugin/2.0.0-01.dtd +++ b/docs/dtd/plugin/2.0.0-01.dtd @@ -29,47 +29,23 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - @@ -121,4 +97,4 @@ - \ No newline at end of file + From a2bff1fc5450a5cdfe9f2804feac114d3a8aacf6 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Mon, 12 Aug 2019 14:24:08 +0200 Subject: [PATCH 19/30] fix pointer = cursor --- scm-ui/src/admin/plugins/components/PluginEntry.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index b901cc0cfb..047ce5492e 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -1,8 +1,8 @@ //@flow import React from "react"; import injectSheet from "react-jss"; -import type { Plugin } from "@scm-manager/ui-types"; -import { CardColumn } from "@scm-manager/ui-components"; +import type {Plugin} from "@scm-manager/ui-types"; +import {CardColumn} from "@scm-manager/ui-components"; import PluginAvatar from "./PluginAvatar"; type Props = { @@ -14,7 +14,7 @@ type Props = { const styles = { link: { - pointerEvents: "all" + pointerEvents: "cursor" } }; From bbfd5195e169179d43523edec2ad13b10c47c1b6 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 14 Aug 2019 09:12:08 +0200 Subject: [PATCH 20/30] add avatarUrl and DisplayName to dtd validation --- docs/dtd/plugin/2.0.0-01.dtd | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/dtd/plugin/2.0.0-01.dtd b/docs/dtd/plugin/2.0.0-01.dtd index a4f5a2d831..4b330fd652 100644 --- a/docs/dtd/plugin/2.0.0-01.dtd +++ b/docs/dtd/plugin/2.0.0-01.dtd @@ -29,7 +29,7 @@ - + @@ -46,6 +46,12 @@ + + + + + + From 339caaf6909bfbcc7b03378492fc589ec4e6cfbf Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 14 Aug 2019 09:12:38 +0200 Subject: [PATCH 21/30] keep old parameters --- scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index 6868182f10..5c504291d2 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -74,6 +74,8 @@ public class ScmConfiguration implements Configuration { */ public static final String DEFAULT_PLUGINURL = "http://download.scm-manager.org/api/v2/plugins.json"; + // Keep the parameters + // "http://plugins.scm-manager.org/scm-plugin-backend/api/{version}/plugins?os={os}&arch={arch}&snapshot=false"; /** * Default plugin url from version 1.0 From cce45083b67d2d460bf5239bc344230827bfbd14 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Wed, 14 Aug 2019 09:13:29 +0200 Subject: [PATCH 22/30] use lombok for equalsAndHashcode + toString / add displayName --- .../sonia/scm/plugin/PluginInformation.java | 55 ++----------------- .../packages/ui-types/src/Plugin.js | 1 + 2 files changed, 7 insertions(+), 49 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index 99ad1e82e8..bb33069afe 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -35,10 +35,10 @@ package sonia.scm.plugin; 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.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; +import lombok.ToString; import sonia.scm.Validateable; import sonia.scm.util.Util; @@ -63,6 +63,8 @@ import java.io.Serializable; @XmlRootElement(name = "plugin-information") @Getter @Setter +@EqualsAndHashCode +@ToString public class PluginInformation implements PermissionObject, Validateable, Cloneable, Serializable { private static final long serialVersionUID = 461382048865977206L; @@ -74,6 +76,7 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea private String name; private PluginState state; private String version; + private String displayName; private String avatarUrl; @Override @@ -85,6 +88,7 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea clone.setDescription(description); clone.setState(state); clone.setVersion(version); + clone.setDisplayName(displayName); clone.setAvatarUrl(avatarUrl); if (condition != null) { @@ -94,53 +98,6 @@ public class PluginInformation implements PermissionObject, Validateable, Clonea return clone; } - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - - if (getClass() != obj.getClass()) { - return false; - } - - final PluginInformation other = (PluginInformation) obj; - - //J- - return - Objects.equal(author, other.author) - && Objects.equal(category, other.category) - && Objects.equal(condition, other.condition) - && Objects.equal(description, other.description) - && Objects.equal(name, other.name) - && Objects.equal(state, other.state) - && Objects.equal(version, other.version) - && Objects.equal(avatarUrl, other.avatarUrl); - //J+ - } - - @Override - public int hashCode() { - return Objects.hashCode(author, category, condition, - description, name, state, version, avatarUrl); - } - - @Override - public String toString() { - //J- - return MoreObjects.toStringHelper(this) - .add("author", author) - .add("category", category) - .add("condition", condition) - .add("description", description) - .add("name", name) - .add("state", state) - .add("version", version) - .add("avatarUrl", avatarUrl) - .toString(); - //J+ - } - @Override public String getId() { return getName(true); diff --git a/scm-ui-components/packages/ui-types/src/Plugin.js b/scm-ui-components/packages/ui-types/src/Plugin.js index 72e4908a54..5a4b015224 100644 --- a/scm-ui-components/packages/ui-types/src/Plugin.js +++ b/scm-ui-components/packages/ui-types/src/Plugin.js @@ -6,6 +6,7 @@ export type Plugin = { type: string, version: string, author: string, + displayName: string, avatarUrl: string, description?: string, _links: Links From 924efc6187565bc1c7539e1c116d4fa8d8cc8775 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Thu, 15 Aug 2019 12:50:56 +0200 Subject: [PATCH 23/30] fix merge error --- .../test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java | 1 - 1 file changed, 1 deletion(-) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java index c4c885fa71..1aef4e57cb 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java @@ -50,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; } From 55e4568ee51383b38163926fe678f95e2b25241f Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 15 Aug 2019 17:01:15 +0200 Subject: [PATCH 24/30] use mapstruct for dto mapping and fix missing fields --- docs/dtd/plugin/2.0.0-01.dtd | 8 +- .../sonia/scm/config/ScmConfiguration.java | 4 +- .../sonia/scm/plugin/PluginInformation.java | 30 +++---- scm-plugins/scm-git-plugin/pom.xml | 2 +- .../main/resources/META-INF/scm/plugin.xml | 8 +- scm-plugins/scm-hg-plugin/pom.xml | 2 +- .../main/resources/META-INF/scm/plugin.xml | 9 +- scm-plugins/scm-legacy-plugin/pom.xml | 5 +- scm-plugins/scm-svn-plugin/pom.xml | 2 +- .../main/resources/META-INF/scm/plugin.xml | 8 +- .../packages/ui-types/src/Plugin.js | 7 +- .../scm/api/v2/resources/MapperModule.java | 2 + .../v2/resources/PluginCenterDtoMapper.java | 33 ------- .../sonia/scm/api/v2/resources/PluginDto.java | 7 +- .../scm/api/v2/resources/PluginDtoMapper.java | 35 ++++---- .../scm/plugin/DefaultPluginManager.java | 13 +-- .../resources => plugin}/PluginCenterDto.java | 5 +- .../scm/plugin/PluginCenterDtoMapper.java | 27 ++++++ .../api/v2/resources/PluginDtoMapperTest.java | 88 +++++++++++++++++++ .../PluginCenterDtoMapperTest.java | 22 ++--- 20 files changed, 181 insertions(+), 136 deletions(-) delete mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java rename scm-webapp/src/main/java/sonia/scm/{api/v2/resources => plugin}/PluginCenterDto.java (96%) create mode 100644 scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java rename scm-webapp/src/test/java/sonia/scm/{api/v2/resources => plugin}/PluginCenterDtoMapperTest.java (79%) diff --git a/docs/dtd/plugin/2.0.0-01.dtd b/docs/dtd/plugin/2.0.0-01.dtd index 4b330fd652..954a2c2219 100644 --- a/docs/dtd/plugin/2.0.0-01.dtd +++ b/docs/dtd/plugin/2.0.0-01.dtd @@ -46,11 +46,11 @@ - - + + - - + + diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index 5c504291d2..b50c64b321 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -73,9 +73,7 @@ public class ScmConfiguration implements Configuration { * Default plugin url */ public static final String DEFAULT_PLUGINURL = - "http://download.scm-manager.org/api/v2/plugins.json"; - // Keep the parameters - // "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 plugin url from version 1.0 diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java index bb33069afe..22911041d4 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginInformation.java @@ -35,10 +35,7 @@ package sonia.scm.plugin; import com.github.sdorra.ssp.PermissionObject; import com.github.sdorra.ssp.StaticPermissions; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; +import lombok.Data; import sonia.scm.Validateable; import sonia.scm.util.Util; @@ -52,6 +49,7 @@ import java.io.Serializable; /** * @author Sebastian Sdorra */ +@Data @StaticPermissions( value = "plugin", generatedClass = "PluginPermissions", @@ -61,40 +59,34 @@ import java.io.Serializable; ) @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement(name = "plugin-information") -@Getter -@Setter -@EqualsAndHashCode -@ToString public class PluginInformation implements PermissionObject, Validateable, Cloneable, Serializable { private static final long serialVersionUID = 461382048865977206L; - private String author; - private String category; - private PluginCondition condition; - private String description; private String name; - private PluginState state; private String version; private String displayName; + private String description; + private String author; + private String category; private String avatarUrl; + private PluginCondition condition; + private PluginState state; @Override public PluginInformation clone() { PluginInformation clone = new PluginInformation(); clone.setName(name); - clone.setAuthor(author); - clone.setCategory(category); - clone.setDescription(description); - clone.setState(state); clone.setVersion(version); clone.setDisplayName(displayName); + clone.setDescription(description); + clone.setAuthor(author); + clone.setCategory(category); clone.setAvatarUrl(avatarUrl); - + clone.setState(state); if (condition != null) { clone.setCondition(condition.clone()); } - return clone; } diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index a838e2f146..7f5531691f 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -10,7 +10,7 @@ scm-git-plugin - scm-git-plugin + Git smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git diff --git a/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml index ff699441a8..1706603e8a 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -47,13 +47,7 @@ Sebastian Sdorra - Git - - git - scm - vcs - dvcs - + Source Code Management diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index 025f79add3..ace7642e91 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -10,7 +10,7 @@ scm-hg-plugin - scm-hg-plugin + Mercurial smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml index 1d0b05c4a8..90d4270a40 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -47,14 +47,7 @@ jo Sebastian Sdorra - Mercurial - - mercurial - hg - scm - vcs - dvcs - + Source Code Management diff --git a/scm-plugins/scm-legacy-plugin/pom.xml b/scm-plugins/scm-legacy-plugin/pom.xml index 6cfa74ea61..5f876beaa2 100644 --- a/scm-plugins/scm-legacy-plugin/pom.xml +++ b/scm-plugins/scm-legacy-plugin/pom.xml @@ -6,8 +6,10 @@ scm-plugins 2.0.0-SNAPSHOT - sonia.scm.plugins + scm-legacy-plugin + Legacy + Support migrated repository urls and v1 passwords 2.0.0-SNAPSHOT smp @@ -21,6 +23,7 @@ ${servlet.version} provided + javax.ws.rs jsr311-api diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index 4386efde5b..b924997711 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -10,7 +10,7 @@ scm-svn-plugin - scm-svn-plugin + Subversion smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion diff --git a/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml index 302abd2b10..7a36cc1486 100644 --- a/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -47,13 +47,7 @@ Sebastian Sdorra - Subversion - - subversion - scm - vcs - svn - + Source Code Management diff --git a/scm-ui-components/packages/ui-types/src/Plugin.js b/scm-ui-components/packages/ui-types/src/Plugin.js index 5a4b015224..3f4f9858c1 100644 --- a/scm-ui-components/packages/ui-types/src/Plugin.js +++ b/scm-ui-components/packages/ui-types/src/Plugin.js @@ -1,14 +1,15 @@ //@flow import type {Collection, Links} from "./hal"; + export type Plugin = { name: string, - type: string, version: string, - author: string, displayName: string, - avatarUrl: string, description?: string, + author: string, + category: string, + avatarUrl: string, _links: Links }; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java index cf09eeb128..0b419cf542 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MapperModule.java @@ -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()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java deleted file mode 100644 index 3a4e8a1947..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDtoMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package sonia.scm.api.v2.resources; - -import sonia.scm.plugin.PluginCondition; -import sonia.scm.plugin.PluginInformation; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class PluginCenterDtoMapper { - - public static Set map(List plugins) { - HashSet pluginInformationSet = new HashSet<>(); - - for (PluginCenterDto.Plugin plugin : plugins) { - - PluginInformation pluginInformation = new PluginInformation(); - pluginInformation.setName(plugin.getName()); - pluginInformation.setAuthor(plugin.getAuthor()); - pluginInformation.setCategory(plugin.getCategory()); - pluginInformation.setVersion(plugin.getVersion()); - pluginInformation.setDescription(plugin.getDescription()); - - if (plugin.getConditions() != null) { - PluginCenterDto.Condition condition = plugin.getConditions(); - pluginInformation.setCondition(new PluginCondition(condition.getMinVersion(), condition.getOs(), condition.getArch())); - } - - pluginInformationSet.add(pluginInformation); - } - return pluginInformationSet; - } -} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java index 75386aed63..b096266537 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDto.java @@ -12,11 +12,12 @@ import lombok.Setter; public class PluginDto extends HalRepresentation { private String name; - private String category; private String version; - private String author; - private String avatarUrl; + private String displayName; private String description; + private String author; + private String category; + private String avatarUrl; public PluginDto(Links links) { add(links); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java index 020e706e43..ca81edd7ff 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginDtoMapper.java @@ -1,6 +1,10 @@ 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; @@ -10,20 +14,27 @@ 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) { return map(plugin.getPlugin().getInformation()); } - public PluginDto map(PluginInformation pluginInformation) { + public abstract PluginDto map(PluginInformation plugin); + + @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() @@ -38,14 +49,6 @@ public class PluginDtoMapper { .self(pluginInformation.getName())); } - PluginDto pluginDto = new PluginDto(linksBuilder.build()); - pluginDto.setName(pluginInformation.getName()); - pluginDto.setCategory(pluginInformation.getCategory() != null ? pluginInformation.getCategory() : "Miscellaneous"); - pluginDto.setVersion(pluginInformation.getVersion()); - pluginDto.setAuthor(pluginInformation.getAuthor()); - pluginDto.setDescription(pluginInformation.getDescription()); - pluginDto.setAvatarUrl(pluginInformation.getAvatarUrl()); - - return pluginDto; + return new PluginDto(linksBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 55b5dd9328..b718a43a81 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -46,7 +46,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContextProvider; -import sonia.scm.api.v2.resources.PluginCenterDto; import sonia.scm.cache.Cache; import sonia.scm.cache.CacheManager; import sonia.scm.config.ScmConfiguration; @@ -79,7 +78,7 @@ import javax.xml.bind.JAXB; import sonia.scm.net.ahc.AdvancedHttpClient; -import static sonia.scm.api.v2.resources.PluginCenterDtoMapper.*; +import static sonia.scm.plugin.PluginCenterDtoMapper.*; /** * TODO replace aether stuff. @@ -595,14 +594,8 @@ public class DefaultPluginManager implements PluginManager { synchronized (DefaultPluginManager.class) { - String pluginUrl = configuration.getPluginUrl(); - - pluginUrl = buildPluginUrl(pluginUrl); - - if (logger.isInfoEnabled()) - { - logger.info("fetch plugin informations from {}", pluginUrl); - } + String pluginUrl = buildPluginUrl(configuration.getPluginUrl()); + logger.info("fetch plugin information from {}", pluginUrl); if (REMOTE_PLUGINS_ENABLED && Util.isNotEmpty(pluginUrl)) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java similarity index 96% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java rename to scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java index 423a0ba0d2..8bb48c8ceb 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PluginCenterDto.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDto.java @@ -1,4 +1,4 @@ -package sonia.scm.api.v2.resources; +package sonia.scm.plugin; import com.google.common.collect.ImmutableList; import lombok.AllArgsConstructor; @@ -45,10 +45,10 @@ public final class PluginCenterDto implements Serializable { public static class Plugin { private String name; + private String version; private String displayName; private String description; private String category; - private String version; private String author; private String avatarUrl; private String sha256; @@ -86,6 +86,5 @@ public final class PluginCenterDto implements Serializable { @Getter static class Link { private String href; - private boolean templated; } } diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java new file mode 100644 index 0000000000..ea445b3ede --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/plugin/PluginCenterDtoMapper.java @@ -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 map(List dtos) { + PluginCenterDtoMapper mapper = Mappers.getMapper(PluginCenterDtoMapper.class); + Set plugins = new HashSet<>(); + for (PluginCenterDto.Plugin plugin : dtos) { + plugins.add(mapper.map(plugin)); + } + return plugins; + } +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java new file mode 100644 index 0000000000..97b46603d3 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginDtoMapperTest.java @@ -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"); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java similarity index 79% rename from scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java rename to scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java index 1175526f75..66a90255b3 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/PluginCenterDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/PluginCenterDtoMapperTest.java @@ -1,8 +1,6 @@ -package sonia.scm.api.v2.resources; +package sonia.scm.plugin; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import sonia.scm.plugin.PluginInformation; import java.util.ArrayList; import java.util.Arrays; @@ -12,19 +10,11 @@ import java.util.List; import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; -import static sonia.scm.api.v2.resources.PluginCenterDto.Condition; -import static sonia.scm.api.v2.resources.PluginCenterDto.Dependency; -import static sonia.scm.api.v2.resources.PluginCenterDto.Plugin; +import static sonia.scm.plugin.PluginCenterDto.Plugin; +import static sonia.scm.plugin.PluginCenterDto.*; class PluginCenterDtoMapperTest { - private PluginCenterDtoMapper pluginCenterDtoMapper; - - @BeforeEach - void initMapper() { - pluginCenterDtoMapper = new PluginCenterDtoMapper(); - } - @Test void shouldMapSinglePlugin() { Plugin plugin = new Plugin( @@ -82,10 +72,10 @@ class PluginCenterDtoMapperTest { Set resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2)); - List pluginsList = new ArrayList(resultSet); + List pluginsList = new ArrayList<>(resultSet); - PluginInformation pluginInformation1 = (PluginInformation) pluginsList.get(1); - PluginInformation pluginInformation2 = (PluginInformation) pluginsList.get(0); + PluginInformation pluginInformation1 = pluginsList.get(1); + PluginInformation pluginInformation2 = pluginsList.get(0); assertThat(pluginInformation1.getAuthor()).isEqualTo(plugin1.getAuthor()); assertThat(pluginInformation1.getVersion()).isEqualTo(plugin1.getVersion()); From f90060d9a8b4b2d7fb46a4c3cc5ebc2e415bba80 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 16 Aug 2019 14:50:51 +0200 Subject: [PATCH 25/30] update smp-maven-plugin to 1.0.0-alpha-6 --- pom.xml | 2 +- scm-webapp/pom.xml | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index ba06282720..acd1f15303 100644 --- a/pom.xml +++ b/pom.xml @@ -437,7 +437,7 @@ sonia.scm.maven smp-maven-plugin - 1.0.0-alpha-4 + 1.0.0-aplpha-6 diff --git a/scm-webapp/pom.xml b/scm-webapp/pom.xml index a74d8dc429..73431b780e 100644 --- a/scm-webapp/pom.xml +++ b/scm-webapp/pom.xml @@ -464,32 +464,28 @@ sonia.scm.maven smp-maven-plugin - - + + sonia.scm.plugins scm-hg-plugin ${project.version} - smp - - + + sonia.scm.plugins scm-svn-plugin ${project.version} - smp - - + + sonia.scm.plugins scm-git-plugin ${project.version} - smp - - + + sonia.scm.plugins scm-legacy-plugin ${project.version} - smp - - + + From 7d75bd76a19316e9331aab500d3e0562cd800fcd Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 19 Aug 2019 09:31:33 +0200 Subject: [PATCH 26/30] fix typo in smp-maven-plugin version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index acd1f15303..3166c6ec75 100644 --- a/pom.xml +++ b/pom.xml @@ -437,7 +437,7 @@ sonia.scm.maven smp-maven-plugin - 1.0.0-aplpha-6 + 1.0.0-alpha-6 From 07e70794fbed93592c40ec01d019672985008801 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 19 Aug 2019 09:32:18 +0200 Subject: [PATCH 27/30] improve plugin information for core plugins --- scm-plugins/scm-git-plugin/pom.xml | 1 - .../scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml | 4 +++- scm-plugins/scm-hg-plugin/pom.xml | 1 - .../scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml | 4 +++- scm-plugins/scm-legacy-plugin/pom.xml | 1 - .../src/main/resources/META-INF/scm/plugin.xml | 4 +++- scm-plugins/scm-svn-plugin/pom.xml | 1 - .../scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml | 4 +++- 8 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scm-plugins/scm-git-plugin/pom.xml b/scm-plugins/scm-git-plugin/pom.xml index 7f5531691f..ac1f007394 100644 --- a/scm-plugins/scm-git-plugin/pom.xml +++ b/scm-plugins/scm-git-plugin/pom.xml @@ -10,7 +10,6 @@ scm-git-plugin - Git smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Git diff --git a/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml index 1706603e8a..ba1d625fb4 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-git-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -46,8 +46,10 @@ 2 - Sebastian Sdorra + Git + Cloudogu GmbH Source Code Management + /images/git-logo.png diff --git a/scm-plugins/scm-hg-plugin/pom.xml b/scm-plugins/scm-hg-plugin/pom.xml index ace7642e91..e5decb0567 100644 --- a/scm-plugins/scm-hg-plugin/pom.xml +++ b/scm-plugins/scm-hg-plugin/pom.xml @@ -10,7 +10,6 @@ scm-hg-plugin - Mercurial smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Mercurial diff --git a/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml index 90d4270a40..352192121f 100644 --- a/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-hg-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -46,8 +46,10 @@ jo 2 - Sebastian Sdorra + Mercurial + Cloudogu GmbH Source Code Management + /images/hg-logo.png diff --git a/scm-plugins/scm-legacy-plugin/pom.xml b/scm-plugins/scm-legacy-plugin/pom.xml index 5f876beaa2..1a12234014 100644 --- a/scm-plugins/scm-legacy-plugin/pom.xml +++ b/scm-plugins/scm-legacy-plugin/pom.xml @@ -8,7 +8,6 @@ scm-legacy-plugin - Legacy Support migrated repository urls and v1 passwords 2.0.0-SNAPSHOT smp diff --git a/scm-plugins/scm-legacy-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-legacy-plugin/src/main/resources/META-INF/scm/plugin.xml index f8a3c8c7b4..2a6b553cdf 100644 --- a/scm-plugins/scm-legacy-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-legacy-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -46,7 +46,9 @@ 2 - Sebastian Sdorra + Legacy + Cloudogu GmbH + Legacy Support diff --git a/scm-plugins/scm-svn-plugin/pom.xml b/scm-plugins/scm-svn-plugin/pom.xml index b924997711..83da627eb9 100644 --- a/scm-plugins/scm-svn-plugin/pom.xml +++ b/scm-plugins/scm-svn-plugin/pom.xml @@ -10,7 +10,6 @@ scm-svn-plugin - Subversion smp https://bitbucket.org/sdorra/scm-manager Plugin for the version control system Subversion diff --git a/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml b/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml index 7a36cc1486..5e941e98e1 100644 --- a/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml +++ b/scm-plugins/scm-svn-plugin/src/main/resources/META-INF/scm/plugin.xml @@ -46,8 +46,10 @@ 2 - Sebastian Sdorra + Subversion + Cloudogu GmbH Source Code Management + /images/svn-logo.gif From 9ee56f8e38226cf73b067e4e0ff254c38ea7737c Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 19 Aug 2019 09:32:37 +0200 Subject: [PATCH 28/30] use displayName if available --- scm-ui/src/admin/plugins/components/PluginEntry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-ui/src/admin/plugins/components/PluginEntry.js b/scm-ui/src/admin/plugins/components/PluginEntry.js index 047ce5492e..7aaeb3f67f 100644 --- a/scm-ui/src/admin/plugins/components/PluginEntry.js +++ b/scm-ui/src/admin/plugins/components/PluginEntry.js @@ -54,7 +54,7 @@ class PluginEntry extends React.Component { Date: Mon, 19 Aug 2019 12:56:27 +0200 Subject: [PATCH 29/30] reactivate plugin center url configuration --- scm-ui/public/locales/de/config.json | 4 ++-- scm-ui/public/locales/en/config.json | 4 ++-- .../src/admin/components/form/GeneralSettings.js | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/scm-ui/public/locales/de/config.json b/scm-ui/public/locales/de/config.json index a94965fa68..0bc220515a 100644 --- a/scm-ui/public/locales/de/config.json +++ b/scm-ui/public/locales/de/config.json @@ -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.", diff --git a/scm-ui/public/locales/en/config.json b/scm-ui/public/locales/en/config.json index 894d0563ba..ce0f7252df 100644 --- a/scm-ui/public/locales/en/config.json +++ b/scm-ui/public/locales/en/config.json @@ -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.", diff --git a/scm-ui/src/admin/components/form/GeneralSettings.js b/scm-ui/src/admin/components/form/GeneralSettings.js index a2fb1127af..842d0ed7f4 100644 --- a/scm-ui/src/admin/components/form/GeneralSettings.js +++ b/scm-ui/src/admin/components/form/GeneralSettings.js @@ -29,6 +29,7 @@ class GeneralSettings extends React.Component { t, realmDescription, loginInfoUrl, + pluginUrl, enabledXsrfProtection, namespaceStrategy, hasUpdatePermission, @@ -78,6 +79,17 @@ class GeneralSettings extends React.Component { />
+
+
+ +
+
); } @@ -85,7 +97,6 @@ class GeneralSettings extends React.Component { 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 { handleNamespaceStrategyChange = (value: string) => { this.props.onChange(true, value, "namespaceStrategy"); }; + handlePluginCenterUrlChange = (value: string) => { + this.props.onChange(true, value, "pluginUrl"); + }; } export default translate("config")(GeneralSettings); From e1dcd2301f9a47c0d626bceeb6c17e8a71613521 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 19 Aug 2019 12:14:03 +0000 Subject: [PATCH 30/30] Close branch feature/plugin_center