use mapstruct for dto mapping and fix missing fields

This commit is contained in:
Sebastian Sdorra
2019-08-15 17:01:15 +02:00
parent 924efc6187
commit 55e4568ee5
20 changed files with 181 additions and 136 deletions

View File

@@ -46,11 +46,11 @@
<!--- the current version of the plugin -->
<!ELEMENT version (#PCDATA)>
<!--- plugin displayName -->
<!ELEMENT displayName (#PCDATA)>
<!--- plugin displayName -->
<!ELEMENT displayName (#PCDATA)>
<!--- url of the plugin avatar -->
<!ELEMENT avatarUrl (#PCDATA)>
<!--- url of the plugin avatar -->
<!ELEMENT avatarUrl (#PCDATA)>
<!--- true if the plugin should load child classes first, the default is false -->
<!ELEMENT child-first-classloader (#PCDATA)>

View File

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

View File

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

View File

@@ -10,7 +10,7 @@
</parent>
<artifactId>scm-git-plugin</artifactId>
<name>scm-git-plugin</name>
<name>Git</name>
<packaging>smp</packaging>
<url>https://bitbucket.org/sdorra/scm-manager</url>
<description>Plugin for the version control system Git</description>

View File

@@ -47,13 +47,7 @@
<information>
<author>Sebastian Sdorra</author>
<category>Git</category>
<tags>
<tag>git</tag>
<tag>scm</tag>
<tag>vcs</tag>
<tag>dvcs</tag>
</tags>
<category>Source Code Management</category>
</information>
<conditions>

View File

@@ -10,7 +10,7 @@
</parent>
<artifactId>scm-hg-plugin</artifactId>
<name>scm-hg-plugin</name>
<name>Mercurial</name>
<packaging>smp</packaging>
<url>https://bitbucket.org/sdorra/scm-manager</url>
<description>Plugin for the version control system Mercurial</description>

View File

@@ -47,14 +47,7 @@ jo
<information>
<author>Sebastian Sdorra</author>
<category>Mercurial</category>
<tags>
<tag>mercurial</tag>
<tag>hg</tag>
<tag>scm</tag>
<tag>vcs</tag>
<tag>dvcs</tag>
</tags>
<category>Source Code Management</category>
</information>
<conditions>

View File

@@ -6,8 +6,10 @@
<artifactId>scm-plugins</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<groupId>sonia.scm.plugins</groupId>
<artifactId>scm-legacy-plugin</artifactId>
<name>Legacy</name>
<description>Support migrated repository urls and v1 passwords</description>
<version>2.0.0-SNAPSHOT</version>
<packaging>smp</packaging>
@@ -21,6 +23,7 @@
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>

View File

@@ -10,7 +10,7 @@
</parent>
<artifactId>scm-svn-plugin</artifactId>
<name>scm-svn-plugin</name>
<name>Subversion</name>
<packaging>smp</packaging>
<url>https://bitbucket.org/sdorra/scm-manager</url>
<description>Plugin for the version control system Subversion</description>

View File

@@ -47,13 +47,7 @@
<information>
<author>Sebastian Sdorra</author>
<category>Subversion</category>
<tags>
<tag>subversion</tag>
<tag>scm</tag>
<tag>vcs</tag>
<tag>svn</tag>
</tags>
<category>Source Code Management</category>
</information>
<conditions>

View File

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

View File

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

View File

@@ -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<PluginInformation> map(List<PluginCenterDto.Plugin> plugins) {
HashSet<PluginInformation> 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
package sonia.scm.plugin;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Mapper
public interface PluginCenterDtoMapper {
@Mapping(source = "conditions", target = "condition")
PluginInformation map(PluginCenterDto.Plugin plugin);
PluginCondition map(PluginCenterDto.Condition condition);
static Set<PluginInformation> map(List<PluginCenterDto.Plugin> dtos) {
PluginCenterDtoMapper mapper = Mappers.getMapper(PluginCenterDtoMapper.class);
Set<PluginInformation> plugins = new HashSet<>();
for (PluginCenterDto.Plugin plugin : dtos) {
plugins.add(mapper.map(plugin));
}
return plugins;
}
}

View File

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

View File

@@ -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<PluginInformation> resultSet = PluginCenterDtoMapper.map(Arrays.asList(plugin1, plugin2));
List pluginsList = new ArrayList(resultSet);
List<PluginInformation> 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());