diff --git a/Jenkinsfile b/Jenkinsfile index 8bb52d6030..e817ac2ba1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -50,6 +50,11 @@ node('docker') { def dockerImageTag = "2.0.0-dev-${commitHash.substring(0,7)}-${BUILD_NUMBER}" if (isMainBranch()) { + + stage('Lifecycle') { + nexusPolicyEvaluation iqApplication: selectedApplication('scm'), iqScanPatterns: [[scanPattern: 'scm-server/target/scm-server-app.zip']], iqStage: 'build' + } + stage('Archive') { archiveArtifacts 'scm-webapp/target/scm-webapp.war' archiveArtifacts 'scm-server/target/scm-server-app.*' diff --git a/pom.xml b/pom.xml index 2d8bc13f28..a7108df873 100644 --- a/pom.xml +++ b/pom.xml @@ -351,21 +351,6 @@ test - - - - - commons-beanutils - commons-beanutils - 1.9.3 - - - - commons-collections - commons-collections - 3.2.2 - - @@ -825,11 +810,11 @@ 1.2.3 3.0.1 - 2.0.1 - 3.1.3.Final + 2.1.1 + 3.6.2.Final 1.19.4 2.11.1 - 2.8.6 + 2.9.8 4.0 2.3.0 diff --git a/scm-core/pom.xml b/scm-core/pom.xml index c4bf3a2e6f..f19d50064d 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -93,6 +93,7 @@ javax.ws.rs javax.ws.rs-api + provided @@ -235,7 +236,6 @@ http://download.oracle.com/javase/6/docs/api/ http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/ - http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/ https://google.github.io/guice/api-docs/${guice.version}/javadoc http://www.slf4j.org/api/ http://shiro.apache.org/static/${shiro.version}/apidocs/ diff --git a/scm-core/src/main/java/sonia/scm/HandlerBase.java b/scm-core/src/main/java/sonia/scm/HandlerBase.java index a621f4f697..d960cc6107 100644 --- a/scm-core/src/main/java/sonia/scm/HandlerBase.java +++ b/scm-core/src/main/java/sonia/scm/HandlerBase.java @@ -36,7 +36,6 @@ package sonia.scm; //~--- JDK imports ------------------------------------------------------------ import java.io.Closeable; -import java.io.IOException; /** * The base class of all handlers. diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java index 89cc893131..eba8173de1 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/BaseMapper.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; import org.mapstruct.Mapping; -public abstract class BaseMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class BaseMapper extends HalAppenderMapper implements InstantAttributeMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract D map(T modelObject); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java similarity index 57% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java index 162d5d6699..6759f5cb8c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -7,7 +8,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import java.time.Instant; -import java.util.List; @Getter @Setter @@ -34,16 +34,7 @@ public class ChangesetDto extends HalRepresentation { */ private String description; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + public ChangesetDto(Links links, Embedded embedded) { + super(links, embedded); } - - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation withEmbedded(String rel, List halRepresentations) { - return super.withEmbedded(rel, halRepresentations); - } - - } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java new file mode 100644 index 0000000000..cd7d7ecebe --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java @@ -0,0 +1,14 @@ +package sonia.scm.api.v2.resources; + +import org.mapstruct.Context; +import org.mapstruct.Mapping; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Repository; + +public interface ChangesetToChangesetDtoMapper { + + @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes + ChangesetDto map(Changeset changeset, @Context Repository repository); + + +} diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java similarity index 58% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java index d3864dc798..6afb542646 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppender.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppender.java @@ -1,12 +1,14 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.HalRepresentation; + /** - * The {@link LinkAppender} can be used within an {@link LinkEnricher} to append hateoas links to a json response. + * The {@link HalAppender} can be used within an {@link HalEnricher} to append hateoas links to a json response. * * @author Sebastian Sdorra * @since 2.0.0 */ -public interface LinkAppender { +public interface HalAppender { /** * Appends one link to the json response. @@ -14,7 +16,7 @@ public interface LinkAppender { * @param rel name of relation * @param href link uri */ - void appendOne(String rel, String href); + void appendLink(String rel, String href); /** * Returns a builder which is able to append an array of links to the resource. @@ -22,8 +24,15 @@ public interface LinkAppender { * @param rel name of link relation * @return multi link builder */ - LinkArrayBuilder arrayBuilder(String rel); + LinkArrayBuilder linkArrayBuilder(String rel); + /** + * Appends one embedded object to the json response. + * + * @param rel name of relation + * @param embeddedItem embedded object + */ + void appendEmbedded(String rel, HalRepresentation embeddedItem); /** * Builder for link arrays. @@ -31,7 +40,7 @@ public interface LinkAppender { interface LinkArrayBuilder { /** - * Append an link to the array. + * Append a link to the array. * * @param name name of link * @param href link target diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java similarity index 56% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java index 7843491b71..dd49b765bc 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkAppenderMapper.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalAppenderMapper.java @@ -4,17 +4,17 @@ import com.google.common.annotations.VisibleForTesting; import javax.inject.Inject; -public class LinkAppenderMapper { +public class HalAppenderMapper { @Inject - private LinkEnricherRegistry registry; + private HalEnricherRegistry registry; @VisibleForTesting - void setRegistry(LinkEnricherRegistry registry) { + void setRegistry(HalEnricherRegistry registry) { this.registry = registry; } - protected void appendLinks(LinkAppender appender, Object source, Object... contextEntries) { + protected void applyEnrichers(HalAppender appender, Object source, Object... contextEntries) { // null check is only their to not break existing tests if (registry != null) { @@ -24,10 +24,10 @@ public class LinkAppenderMapper { ctx[i + 1] = contextEntries[i]; } - LinkEnricherContext context = LinkEnricherContext.of(ctx); + HalEnricherContext context = HalEnricherContext.of(ctx); - Iterable enrichers = registry.allByType(source.getClass()); - for (LinkEnricher enricher : enrichers) { + Iterable enrichers = registry.allByType(source.getClass()); + for (HalEnricher enricher : enrichers) { enricher.enrich(context, appender); } } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java similarity index 51% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java index c16d6f6482..647a1cf74e 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricher.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricher.java @@ -3,8 +3,8 @@ package sonia.scm.api.v2.resources; import sonia.scm.plugin.ExtensionPoint; /** - * A {@link LinkEnricher} can be used to append hateoas links to a specific json response. - * To register an enricher use the {@link Enrich} annotation or the {@link LinkEnricherRegistry} which is available + * A {@link HalEnricher} can be used to append hal specific attributes, such as links, to the json response. + * To register an enricher use the {@link Enrich} annotation or the {@link HalEnricherRegistry} which is available * via injection. * * Warning: enrichers are always registered as singletons. @@ -14,13 +14,13 @@ import sonia.scm.plugin.ExtensionPoint; */ @ExtensionPoint @FunctionalInterface -public interface LinkEnricher { +public interface HalEnricher { /** - * Enriches the response with hateoas links. + * Enriches the response with hal specific attributes. * * @param context contains the source for the json mapping and related objects - * @param appender can be used to append links to the json response + * @param appender can be used to append links or embedded objects to the json response */ - void enrich(LinkEnricherContext context, LinkAppender appender); + void enrich(HalEnricherContext context, HalAppender appender); } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java similarity index 80% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java index 2808a923e9..36128087b8 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherContext.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherContext.java @@ -7,17 +7,17 @@ import java.util.NoSuchElementException; import java.util.Optional; /** - * Context object for the {@link LinkEnricher}. The context holds the source object for the json and all related - * objects, which can be useful for the link creation. + * Context object for the {@link HalEnricher}. The context holds the source object for the json and all related + * objects, which can be useful for the enrichment. * * @author Sebastian Sdorra * @since 2.0.0 */ -public final class LinkEnricherContext { +public final class HalEnricherContext { private final Map instanceMap; - private LinkEnricherContext(Map instanceMap) { + private HalEnricherContext(Map instanceMap) { this.instanceMap = instanceMap; } @@ -28,12 +28,12 @@ public final class LinkEnricherContext { * * @return context of given entries */ - public static LinkEnricherContext of(Object... instances) { + public static HalEnricherContext of(Object... instances) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Object instance : instances) { builder.put(instance.getClass(), instance); } - return new LinkEnricherContext(builder.build()); + return new HalEnricherContext(builder.build()); } /** diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java similarity index 53% rename from scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java index cd95a62ec3..3fadbfa388 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/LinkEnricherRegistry.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/HalEnricherRegistry.java @@ -7,34 +7,34 @@ import sonia.scm.plugin.Extension; import javax.inject.Singleton; /** - * The {@link LinkEnricherRegistry} is responsible for binding {@link LinkEnricher} instances to their source types. + * The {@link HalEnricherRegistry} is responsible for binding {@link HalEnricher} instances to their source types. * * @author Sebastian Sdorra * @since 2.0.0 */ @Extension @Singleton -public final class LinkEnricherRegistry { +public final class HalEnricherRegistry { - private final Multimap enrichers = HashMultimap.create(); + private final Multimap enrichers = HashMultimap.create(); /** - * Registers a new {@link LinkEnricher} for the given source type. + * Registers a new {@link HalEnricher} for the given source type. * * @param sourceType type of json mapping source * @param enricher link enricher instance */ - public void register(Class sourceType, LinkEnricher enricher) { + public void register(Class sourceType, HalEnricher enricher) { enrichers.put(sourceType, enricher); } /** - * Returns all registered {@link LinkEnricher} for the given type. + * Returns all registered {@link HalEnricher} for the given type. * * @param sourceType type of json mapping source * @return all registered enrichers */ - public Iterable allByType(Class sourceType) { + public Iterable allByType(Class sourceType) { return enrichers.get(sourceType); } } diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java index bf20f26a7a..346ce83816 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/Index.java @@ -1,7 +1,7 @@ package sonia.scm.api.v2.resources; /** - * The {@link Index} object can be used to register a {@link LinkEnricher} for the index resource. + * The {@link Index} object can be used to register a {@link HalEnricher} for the index resource. * * @author Sebastian Sdorra * @since 2.0.0 diff --git a/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java index f8f82804a6..a027a78d79 100644 --- a/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java +++ b/scm-core/src/main/java/sonia/scm/api/v2/resources/Me.java @@ -1,7 +1,7 @@ package sonia.scm.api.v2.resources; /** - * The {@link Me} object can be used to register a {@link LinkEnricher} for the me resource. + * The {@link Me} object can be used to register a {@link HalEnricher} for the me resource. * * @author Sebastian Sdorra * @since 2.0.0 diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PersonDto.java b/scm-core/src/main/java/sonia/scm/api/v2/resources/PersonDto.java similarity index 100% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/PersonDto.java rename to scm-core/src/main/java/sonia/scm/api/v2/resources/PersonDto.java diff --git a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java index 1fa525d4fd..ef0ec8a5ef 100644 --- a/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java +++ b/scm-core/src/main/java/sonia/scm/filter/GZipResponseFilter.java @@ -1,24 +1,59 @@ package sonia.scm.filter; -import lombok.extern.slf4j.Slf4j; -import sonia.scm.util.WebUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.ext.Provider; +import javax.inject.Inject; +import javax.inject.Provider; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.ext.WriterInterceptor; +import javax.ws.rs.ext.WriterInterceptorContext; import java.io.IOException; +import java.io.OutputStream; +import java.util.Locale; import java.util.zip.GZIPOutputStream; -@Provider -@Slf4j -public class GZipResponseFilter implements ContainerResponseFilter { - public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException { - if (WebUtil.isGzipSupported(requestContext::getHeaderString)) { - log.trace("compress output with gzip"); - GZIPOutputStream wrappedResponse = new GZIPOutputStream(responseContext.getEntityStream()); - responseContext.getHeaders().add("Content-Encoding", "gzip"); - responseContext.setEntityStream(wrappedResponse); +@javax.ws.rs.ext.Provider +public class GZipResponseFilter implements WriterInterceptor { + + private static final Logger LOG = LoggerFactory.getLogger(GZipResponseFilter.class); + + private final Provider requestProvider; + + @Inject + public GZipResponseFilter(Provider requestProvider) { + this.requestProvider = requestProvider; + } + + @Override + public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException { + if (isGZipSupported()) { + LOG.trace("compress output with gzip"); + encodeWithGZip(context); + } else { + context.proceed(); } } + + private void encodeWithGZip(WriterInterceptorContext context) throws IOException { + context.getHeaders().remove(HttpHeaders.CONTENT_LENGTH); + context.getHeaders().add(HttpHeaders.CONTENT_ENCODING, "gzip"); + + OutputStream outputStream = context.getOutputStream(); + GZIPOutputStream compressedOutputStream = new GZIPOutputStream(outputStream); + context.setOutputStream(compressedOutputStream); + try { + context.proceed(); + } finally { + compressedOutputStream.finish(); + context.setOutputStream(outputStream); + } + } + + private boolean isGZipSupported() { + Object encoding = requestProvider.get().getHeader(HttpHeaders.ACCEPT_ENCODING); + return encoding != null && encoding.toString().toLowerCase(Locale.ENGLISH).contains("gzip"); + } } diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java index c80ae9b1c4..0723f44b6c 100644 --- a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java +++ b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java @@ -35,7 +35,6 @@ package sonia.scm.net.ahc; import com.google.common.base.Charsets; import com.google.common.base.Strings; -import com.google.common.collect.HashMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; diff --git a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java index 0bf37054a8..2d65d1cc98 100644 --- a/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java +++ b/scm-core/src/main/java/sonia/scm/plugin/PluginLoader.java @@ -35,8 +35,6 @@ package sonia.scm.plugin; //~--- non-JDK imports -------------------------------------------------------- -import com.google.inject.Module; - //~--- JDK imports ------------------------------------------------------------ import java.util.Collection; diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java index 9941a4253b..663d053ca5 100644 --- a/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java +++ b/scm-core/src/main/java/sonia/scm/repository/AbstractSimpleRepositoryHandler.java @@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory; import sonia.scm.ConfigurationException; import sonia.scm.io.CommandResult; import sonia.scm.io.ExtendedCommand; +import sonia.scm.plugin.PluginLoader; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; @@ -67,11 +68,14 @@ public abstract class AbstractSimpleRepositoryHandler verbs; + private Set verbs; /** * Constructs a new {@link RepositoryPermission}. @@ -79,7 +79,7 @@ public class RepositoryPermission implements PermissionObject, Serializable public RepositoryPermission(String name, Collection verbs, boolean groupPermission) { this.name = name; - this.verbs = unmodifiableCollection(new LinkedHashSet<>(verbs)); + this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs)); this.groupPermission = groupPermission; } @@ -109,7 +109,8 @@ public class RepositoryPermission implements PermissionObject, Serializable final RepositoryPermission other = (RepositoryPermission) obj; return Objects.equal(name, other.name) - && CollectionUtils.isEqualCollection(verbs, other.verbs) + && verbs.containsAll(other.verbs) + && verbs.size() == other.verbs.size() && Objects.equal(groupPermission, other.groupPermission); } @@ -209,6 +210,6 @@ public class RepositoryPermission implements PermissionObject, Serializable */ public void setVerbs(Collection verbs) { - this.verbs = verbs; + this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs)); } } diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java index baf03a0aef..45dfc0b2b7 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/MergeCommandRequest.java @@ -5,7 +5,6 @@ import com.google.common.base.Objects; import com.google.common.base.Strings; import sonia.scm.Validateable; import sonia.scm.repository.Person; -import sonia.scm.util.Util; import java.io.Serializable; diff --git a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java index a005583256..8d95131ee6 100644 --- a/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java +++ b/scm-core/src/main/java/sonia/scm/security/PermissionDescriptor.java @@ -39,7 +39,6 @@ import com.google.common.base.Objects; 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; diff --git a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java index 4b2e46b665..903f86df90 100644 --- a/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java +++ b/scm-core/src/main/java/sonia/scm/security/StoredAssignedPermission.java @@ -34,8 +34,6 @@ package sonia.scm.security; //~--- JDK imports ------------------------------------------------------------ -import com.google.common.base.Objects; - import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; diff --git a/scm-core/src/main/java/sonia/scm/util/Comparables.java b/scm-core/src/main/java/sonia/scm/util/Comparables.java new file mode 100644 index 0000000000..1fb0c5e358 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/util/Comparables.java @@ -0,0 +1,88 @@ +package sonia.scm.util; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Optional; + +import static com.google.common.base.Preconditions.checkArgument; + +public final class Comparables { + + private static final CacheLoader beanInfoCacheLoader = new CacheLoader() { + @Override + public BeanInfo load(Class type) throws IntrospectionException { + return Introspector.getBeanInfo(type); + } + }; + + private static final LoadingCache beanInfoCache = CacheBuilder.newBuilder() + .maximumSize(50) // limit the cache to avoid consuming to much memory on miss usage + .build(beanInfoCacheLoader); + + private Comparables() { + } + + public static Comparator comparator(Class type, String sortBy) { + BeanInfo info = createBeanInfo(type); + PropertyDescriptor propertyDescriptor = findPropertyDescriptor(sortBy, info); + + Method readMethod = propertyDescriptor.getReadMethod(); + checkIfPropertyIsComparable(readMethod, sortBy); + + return new MethodComparator<>(readMethod); + } + + private static void checkIfPropertyIsComparable(Method readMethod, String sortBy) { + checkArgument(isReturnTypeComparable(readMethod), "property %s is not comparable", sortBy); + } + + private static boolean isReturnTypeComparable(Method readMethod) { + return Comparable.class.isAssignableFrom(readMethod.getReturnType()); + } + + private static PropertyDescriptor findPropertyDescriptor(String sortBy, BeanInfo info) { + PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors(); + + Optional sortByPropertyDescriptor = Arrays.stream(propertyDescriptors) + .filter(p -> p.getName().equals(sortBy)) + .findFirst(); + + return sortByPropertyDescriptor.orElseThrow(() -> new IllegalArgumentException("could not find property " + sortBy)); + } + + private static BeanInfo createBeanInfo(Class type) { + return beanInfoCache.getUnchecked(type); + } + + private static class MethodComparator implements Comparator { + + private final Method readMethod; + + private MethodComparator(Method readMethod) { + this.readMethod = readMethod; + } + + @Override + @SuppressWarnings("unchecked") + public int compare(T left, T right) { + try { + Comparable leftResult = (Comparable) readMethod.invoke(left); + Comparable rightResult = (Comparable) readMethod.invoke(right); + return leftResult.compareTo(rightResult); + } catch (IllegalAccessException | InvocationTargetException ex) { + throw new IllegalArgumentException("failed to invoke read method", ex); + } + } + } + +} diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java similarity index 53% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java index 557eac2020..ff658cc26a 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkAppenderMapperTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalAppenderMapperTest.java @@ -11,51 +11,51 @@ import java.util.Optional; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) -class LinkAppenderMapperTest { +class HalAppenderMapperTest { @Mock - private LinkAppender appender; + private HalAppender appender; - private LinkEnricherRegistry registry; - private LinkAppenderMapper mapper; + private HalEnricherRegistry registry; + private HalAppenderMapper mapper; @BeforeEach void beforeEach() { - registry = new LinkEnricherRegistry(); - mapper = new LinkAppenderMapper(); + registry = new HalEnricherRegistry(); + mapper = new HalAppenderMapper(); mapper.setRegistry(registry); } @Test void shouldAppendSimpleLink() { - registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); - mapper.appendLinks(appender, "hello"); + mapper.applyEnrichers(appender, "hello"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } @Test void shouldCallMultipleEnrichers() { - registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com")); - registry.register(String.class, (ctx, appender) -> appender.appendOne("21", "https://scm.hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("42", "https://hitchhiker.com")); + registry.register(String.class, (ctx, appender) -> appender.appendLink("21", "https://scm.hitchhiker.com")); - mapper.appendLinks(appender, "hello"); + mapper.applyEnrichers(appender, "hello"); - verify(appender).appendOne("42", "https://hitchhiker.com"); - verify(appender).appendOne("21", "https://scm.hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); + verify(appender).appendLink("21", "https://scm.hitchhiker.com"); } @Test void shouldAppendLinkByUsingSourceFromContext() { registry.register(String.class, (ctx, appender) -> { Optional rel = ctx.oneByType(String.class); - appender.appendOne(rel.get(), "https://hitchhiker.com"); + appender.appendLink(rel.get(), "https://hitchhiker.com"); }); - mapper.appendLinks(appender, "42"); + mapper.applyEnrichers(appender, "42"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } @Test @@ -63,12 +63,12 @@ class LinkAppenderMapperTest { registry.register(Integer.class, (ctx, appender) -> { Optional rel = ctx.oneByType(Integer.class); Optional href = ctx.oneByType(String.class); - appender.appendOne(String.valueOf(rel.get()), href.get()); + appender.appendLink(String.valueOf(rel.get()), href.get()); }); - mapper.appendLinks(appender, Integer.valueOf(42), "https://hitchhiker.com"); + mapper.applyEnrichers(appender, Integer.valueOf(42), "https://hitchhiker.com"); - verify(appender).appendOne("42", "https://hitchhiker.com"); + verify(appender).appendLink("42", "https://hitchhiker.com"); } } diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java similarity index 72% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java index 6eb7bb4c84..1aecb5ad46 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherContextTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherContextTest.java @@ -7,17 +7,17 @@ import org.junit.jupiter.api.Test; import java.util.NoSuchElementException; -class LinkEnricherContextTest { +class HalEnricherContextTest { @Test void shouldCreateContextFromSingleObject() { - LinkEnricherContext context = LinkEnricherContext.of("hello"); + HalEnricherContext context = HalEnricherContext.of("hello"); assertThat(context.oneByType(String.class)).contains("hello"); } @Test void shouldCreateContextFromMultipleObjects() { - LinkEnricherContext context = LinkEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L)); + HalEnricherContext context = HalEnricherContext.of("hello", Integer.valueOf(42), Long.valueOf(21L)); assertThat(context.oneByType(String.class)).contains("hello"); assertThat(context.oneByType(Integer.class)).contains(42); assertThat(context.oneByType(Long.class)).contains(21L); @@ -25,19 +25,19 @@ class LinkEnricherContextTest { @Test void shouldReturnEmptyOptionalForUnknownTypes() { - LinkEnricherContext context = LinkEnricherContext.of(); + HalEnricherContext context = HalEnricherContext.of(); assertThat(context.oneByType(String.class)).isNotPresent(); } @Test void shouldReturnRequiredObject() { - LinkEnricherContext context = LinkEnricherContext.of("hello"); + HalEnricherContext context = HalEnricherContext.of("hello"); assertThat(context.oneRequireByType(String.class)).isEqualTo("hello"); } @Test void shouldThrowAnNoSuchElementExceptionForUnknownTypes() { - LinkEnricherContext context = LinkEnricherContext.of(); + HalEnricherContext context = HalEnricherContext.of(); assertThrows(NoSuchElementException.class, () -> context.oneRequireByType(String.class)); } diff --git a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java similarity index 53% rename from scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java rename to scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java index 07441003d7..6a863d2f04 100644 --- a/scm-core/src/test/java/sonia/scm/api/v2/resources/LinkEnricherRegistryTest.java +++ b/scm-core/src/test/java/sonia/scm/api/v2/resources/HalEnricherRegistryTest.java @@ -5,54 +5,54 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class LinkEnricherRegistryTest { +class HalEnricherRegistryTest { - private LinkEnricherRegistry registry; + private HalEnricherRegistry registry; @BeforeEach void setUpObjectUnderTest() { - registry = new LinkEnricherRegistry(); + registry = new HalEnricherRegistry(); } @Test void shouldRegisterTheEnricher() { - SampleLinkEnricher enricher = new SampleLinkEnricher(); + SampleHalEnricher enricher = new SampleHalEnricher(); registry.register(String.class, enricher); - Iterable enrichers = registry.allByType(String.class); + Iterable enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(enricher); } @Test void shouldRegisterMultipleEnrichers() { - SampleLinkEnricher one = new SampleLinkEnricher(); + SampleHalEnricher one = new SampleHalEnricher(); registry.register(String.class, one); - SampleLinkEnricher two = new SampleLinkEnricher(); + SampleHalEnricher two = new SampleHalEnricher(); registry.register(String.class, two); - Iterable enrichers = registry.allByType(String.class); + Iterable enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(one, two); } @Test void shouldRegisterEnrichersForDifferentTypes() { - SampleLinkEnricher one = new SampleLinkEnricher(); + SampleHalEnricher one = new SampleHalEnricher(); registry.register(String.class, one); - SampleLinkEnricher two = new SampleLinkEnricher(); + SampleHalEnricher two = new SampleHalEnricher(); registry.register(Integer.class, two); - Iterable enrichers = registry.allByType(String.class); + Iterable enrichers = registry.allByType(String.class); assertThat(enrichers).containsOnly(one); enrichers = registry.allByType(Integer.class); assertThat(enrichers).containsOnly(two); } - private static class SampleLinkEnricher implements LinkEnricher { + private static class SampleHalEnricher implements HalEnricher { @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { + public void enrich(HalEnricherContext context, HalAppender appender) { } } diff --git a/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java new file mode 100644 index 0000000000..c3648fd4b8 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/filter/GZipResponseFilterTest.java @@ -0,0 +1,87 @@ +package sonia.scm.filter; + +import com.google.inject.util.Providers; +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.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.WriterInterceptorContext; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class GZipResponseFilterTest { + + @Mock + private HttpServletRequest request; + + @Mock + private WriterInterceptorContext context; + + @Mock + private MultivaluedMap headers; + + private GZipResponseFilter filter; + + @BeforeEach + void setupObjectUnderTest() { + filter = new GZipResponseFilter(Providers.of(request)); + } + + @Test + void shouldSkipGZipCompression() throws IOException { + when(request.getHeader(HttpHeaders.ACCEPT_ENCODING)).thenReturn("deflate, br"); + + filter.aroundWriteTo(context); + + verifySkipped(); + } + + @Test + void shouldSkipGZipCompressionWithoutAcceptEncodingHeader() throws IOException { + filter.aroundWriteTo(context); + + verifySkipped(); + } + + private void verifySkipped() throws IOException { + verify(context, never()).getOutputStream(); + verify(context).proceed(); + } + + + @Nested + class AcceptGZipEncoding { + + @BeforeEach + void setUpContext() { + when(request.getHeader(HttpHeaders.ACCEPT_ENCODING)).thenReturn("gzip, deflate, br"); + when(context.getHeaders()).thenReturn(headers); + when(context.getOutputStream()).thenReturn(new ByteArrayOutputStream()); + } + + @Test + void shouldEncode() throws IOException { + filter.aroundWriteTo(context); + + verify(headers).remove(HttpHeaders.CONTENT_LENGTH); + verify(headers).add(HttpHeaders.CONTENT_ENCODING, "gzip"); + + verify(context).setOutputStream(any(GZIPOutputStream.class)); + verify(context, times(2)).setOutputStream(any(OutputStream.class)); + } + + } + + +} diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java index b7fa9ae84a..92ca488ddf 100644 --- a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java +++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java @@ -36,7 +36,7 @@ import com.google.common.io.ByteSource; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; + import org.junit.Test; import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; diff --git a/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java index 2e9383b2e2..d65358a66e 100644 --- a/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java +++ b/scm-core/src/test/java/sonia/scm/repository/RepositoryPermissionTest.java @@ -46,4 +46,13 @@ class RepositoryPermissionTest { assertThat(permission1).isNotEqualTo(permission2); } + + @Test + void shouldBeEqualWithRedundantVerbs() { + RepositoryPermission permission1 = new RepositoryPermission("name1", asList("one", "two"), false); + RepositoryPermission permission2 = new RepositoryPermission("name1", asList("one", "two"), false); + permission2.setVerbs(asList("one", "two", "two")); + + assertThat(permission1).isEqualTo(permission2); + } } diff --git a/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java b/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java index af4bf37915..7ddeabd8ac 100644 --- a/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java +++ b/scm-core/src/test/java/sonia/scm/security/DAORealmHelperTest.java @@ -1,7 +1,6 @@ package sonia.scm.security; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.DisabledAccountException; import org.apache.shiro.authc.UnknownAccountException; diff --git a/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java b/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java new file mode 100644 index 0000000000..50ae2254e6 --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/util/ComparablesTest.java @@ -0,0 +1,57 @@ +package sonia.scm.util; + +import org.junit.jupiter.api.Test; + +import java.util.Comparator; + +import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ComparablesTest { + + @Test + void shouldCompare() { + One a = new One("a"); + One b = new One("b"); + + Comparator comparable = Comparables.comparator(One.class, "value"); + assertThat(comparable.compare(a, b)).isEqualTo(-1); + } + + @Test + void shouldThrowAnExceptionForNonExistingField() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "awesome")); + } + + @Test + void shouldThrowAnExceptionForNonComparableField() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "nonComparable")); + } + + @Test + void shouldThrowAnExceptionIfTheFieldHasNoGetter() { + assertThrows(IllegalArgumentException.class, () -> Comparables.comparator(One.class, "incredible")); + } + + private static class One { + + private String value; + private String incredible; + private NonComparable nonComparable; + + One(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public NonComparable getNonComparable() { + return nonComparable; + } + } + + private static class NonComparable {} + +} diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java index 5b5c00a298..579ef75b71 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java +++ b/scm-dao-xml/src/main/java/sonia/scm/store/JAXBDataStoreFactory.java @@ -37,9 +37,6 @@ package sonia.scm.store; import com.google.inject.Inject; import com.google.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sonia.scm.SCMContextProvider; import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.security.KeyGenerator; diff --git a/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java b/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java index 1ce0508616..5b24096eb5 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java +++ b/scm-dao-xml/src/main/java/sonia/scm/xml/AbstractXmlDAO.java @@ -38,7 +38,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.GenericDAO; import sonia.scm.ModelObject; -import sonia.scm.group.xml.XmlGroupDAO; import sonia.scm.store.ConfigurationStore; import java.util.Collection; diff --git a/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java b/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java index d812eedc35..4b3d9b0f28 100644 --- a/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java +++ b/scm-dao-xml/src/main/java/sonia/scm/xml/XmlStreams.java @@ -1,6 +1,5 @@ package sonia.scm.xml; -import com.google.common.base.Charsets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java b/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java index 3c67ca3dc3..c49a65bea2 100644 --- a/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/RepositoriesITCase.java @@ -36,7 +36,6 @@ package sonia.scm.it; import org.apache.http.HttpStatus; import org.assertj.core.api.Assertions; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; diff --git a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java index 9386f1d9c5..0a5693ad2e 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java @@ -5,10 +5,8 @@ import io.restassured.response.Response; import org.junit.Assert; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sonia.scm.user.User; import sonia.scm.web.VndMediaType; -import java.net.ConnectException; import java.util.List; import java.util.Map; import java.util.function.Consumer; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java index e078b04b08..7cda4bc9d3 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigResource.java @@ -18,8 +18,6 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.Response; -import static sonia.scm.ContextEntry.ContextBuilder.entity; - /** * RESTful Web Service Resource to manage the configuration of the git plugin. */ diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java index 95225c9e30..63800e8a02 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitRepositoryHandler.java @@ -44,6 +44,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.SCMContextProvider; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.GitRepositoryServiceProvider; import sonia.scm.schedule.Scheduler; import sonia.scm.schedule.Task; @@ -103,9 +104,10 @@ public class GitRepositoryHandler public GitRepositoryHandler(ConfigurationStoreFactory storeFactory, Scheduler scheduler, RepositoryLocationResolver repositoryLocationResolver, - GitWorkdirFactory workdirFactory) + GitWorkdirFactory workdirFactory, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); this.scheduler = scheduler; this.workdirFactory = workdirFactory; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java index a2114a1b6a..6db7d694d5 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/web/GitRepositoryResolver.java @@ -35,7 +35,6 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.inject.Inject; import org.eclipse.jgit.errors.RepositoryNotFoundException; diff --git a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js index e34a0ef96f..aadb58eed6 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js +++ b/scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js @@ -2,7 +2,7 @@ import React from "react"; -import {apiClient, BranchSelector, ErrorPage, Loading, SubmitButton} from "@scm-manager/ui-components"; +import {apiClient, BranchSelector, ErrorPage, Loading, Subtitle, SubmitButton} from "@scm-manager/ui-components"; import type {Branch, Repository} from "@scm-manager/ui-types"; import {translate} from "react-i18next"; @@ -113,6 +113,7 @@ class RepositoryConfig extends React.Component { if (!(loadingBranches || loadingDefaultBranch)) { return ( <> + {this.renderBranchChangedNotification()}
{ disabled={!this.state.selectedBranchName} /> +
); } else { diff --git a/scm-plugins/scm-git-plugin/src/main/js/index.js b/scm-plugins/scm-git-plugin/src/main/js/index.js index a066247dde..43e3950beb 100644 --- a/scm-plugins/scm-git-plugin/src/main/js/index.js +++ b/scm-plugins/scm-git-plugin/src/main/js/index.js @@ -27,14 +27,9 @@ binder.bind( ); binder.bind("repos.repository-avatar", GitAvatar, gitPredicate); -cfgBinder.bindRepository( - "/configuration", - "scm-git-plugin.repo-config.link", - "configuration", - RepositoryConfig -); -// global config +binder.bind("repo-config.route", RepositoryConfig, gitPredicate); +// global config cfgBinder.bindGlobal( "/git", "scm-git-plugin.config.link", diff --git a/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json b/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json index 8902a57f32..cd88897e74 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json +++ b/scm-plugins/scm-git-plugin/src/main/resources/locales/de/plugins.json @@ -27,6 +27,7 @@ }, "repo-config": { "link": "Konfiguration", + "title": "Git Einstellungen", "default-branch": "Standard Branch", "submit": "Speichern", "error": { diff --git a/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json b/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json index 2b579801dd..551573fb72 100644 --- a/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json +++ b/scm-plugins/scm-git-plugin/src/main/resources/locales/en/plugins.json @@ -27,6 +27,7 @@ }, "repo-config": { "link": "Configuration", + "title": "Git Settings", "default-branch": "Default branch", "submit": "Submit", "error": { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java index ed34db3008..6a05875aa9 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigDtoToGitConfigMapperTest.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import static org.junit.Assert.*; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java index 0c28a28e59..8e657b1050 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigResourceTest.java @@ -17,7 +17,7 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.GitRepositoryHandler; @@ -29,6 +29,7 @@ import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.web.GitVndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -100,7 +101,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetGitConfig() throws URISyntaxException { + public void shouldGetGitConfig() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); @@ -115,7 +116,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException { + public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException { when(repositoryHandler.getConfig()).thenReturn(null); MockHttpResponse response = get(); @@ -126,7 +127,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); @@ -159,7 +160,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldReadDefaultRepositoryConfig() throws URISyntaxException { + public void shouldReadDefaultRepositoryConfig() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X"); @@ -176,7 +177,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldNotHaveUpdateLinkForReadOnlyUser() throws URISyntaxException { + public void shouldNotHaveUpdateLinkForReadOnlyUser() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X"); @@ -193,7 +194,7 @@ public class GitConfigResourceTest { @Test @SubjectAware(username = "writeOnly") - public void shouldReadStoredRepositoryConfig() throws URISyntaxException { + public void shouldReadStoredRepositoryConfig() throws URISyntaxException, UnsupportedEncodingException { when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X")); GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig(); gitRepositoryConfig.setDefaultBranch("test"); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java index 40cf36e8dd..62fa8d33b4 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigToGitConfigDtoMapperTest.java @@ -11,10 +11,9 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; -import java.io.File; import java.net.URI; import static org.junit.Assert.assertEquals; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java index d2942d08a3..a1e349dd57 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitRepositoryConfigEnricherTest.java @@ -14,9 +14,6 @@ import org.mockito.junit.jupiter.MockitoExtension; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryManager; -import sonia.scm.repository.api.Command; -import sonia.scm.repository.api.RepositoryService; -import sonia.scm.repository.api.RepositoryServiceFactory; import sonia.scm.web.JsonEnricherContext; import sonia.scm.web.VndMediaType; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java index 23b3110567..3362c8a22b 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitHeadModifierTest.java @@ -40,7 +40,7 @@ import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.io.File; import java.io.IOException; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java index cb10e15271..66ec320067 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/GitRepositoryHandlerTest.java @@ -94,7 +94,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { RepositoryLocationResolver locationResolver, File directory) { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - scheduler, locationResolver, gitWorkdirFactory); + scheduler, locationResolver, gitWorkdirFactory, null); repositoryHandler.init(contextProvider); GitConfig config = new GitConfig(); @@ -108,7 +108,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory, - scheduler, locationResolver, gitWorkdirFactory); + scheduler, locationResolver, gitWorkdirFactory, null); GitConfig config = new GitConfig(); config.setDisabled(false); config.setGcExpression("gc exp"); diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java index 630236b20b..f2a4ed4954 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/AbstractGitCommandTestBase.java @@ -35,10 +35,8 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import org.junit.After; -import org.junit.Before; import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStore; import sonia.scm.store.InMemoryConfigurationStoreFactory; /** diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java index 817e4641dd..c8d260d503 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBlameCommandTest.java @@ -35,11 +35,9 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import org.junit.Test; -import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.BlameLine; import sonia.scm.repository.BlameResult; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStoreFactory; import java.io.IOException; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java index 2ff3c73420..1feceba652 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitBrowseCommandTest.java @@ -32,11 +32,9 @@ package sonia.scm.repository.spi; import org.junit.Test; -import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider; import sonia.scm.repository.BrowserResult; import sonia.scm.repository.FileObject; import sonia.scm.repository.GitRepositoryConfig; -import sonia.scm.store.InMemoryConfigurationStoreFactory; import java.io.IOException; import java.util.Collection; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index e2ab85d9a7..06e9b17fe7 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -36,10 +36,8 @@ package sonia.scm.repository.spi; import com.google.common.io.Files; import org.junit.Test; -import sonia.scm.event.ScmEventBus; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; -import sonia.scm.repository.ClearRepositoryCacheEvent; import sonia.scm.repository.GitRepositoryConfig; import sonia.scm.repository.Modifications; diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java index dc0822deba..4ed9d5a46a 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/web/GitReceivePackFactoryTest.java @@ -42,7 +42,7 @@ import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.GitConfig; import sonia.scm.repository.GitRepositoryHandler; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java index 785aa399b1..27fdc7a296 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/installer/AbstractHgInstaller.java @@ -35,14 +35,12 @@ package sonia.scm.installer; //~--- non-JDK imports -------------------------------------------------------- -import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; -import sonia.scm.util.IOUtil; //~--- JDK imports ------------------------------------------------------------ import java.io.File; -import java.io.IOException; + import sonia.scm.net.ahc.AdvancedHttpClient; /** diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java index 4b6998f09a..b1d431c742 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgImportHandler.java @@ -39,7 +39,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.io.INIConfiguration; import sonia.scm.io.INIConfigurationReader; -import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.util.ValidationUtil; diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java index c2c0439fc1..7db9a8becb 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/HgRepositoryHandler.java @@ -51,6 +51,7 @@ import sonia.scm.io.INIConfigurationReader; import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HgRepositoryServiceProvider; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.IOUtil; @@ -111,9 +112,10 @@ public class HgRepositoryHandler @Inject public HgRepositoryHandler(ConfigurationStoreFactory storeFactory, Provider hgContextProvider, - RepositoryLocationResolver repositoryLocationResolver) + RepositoryLocationResolver repositoryLocationResolver, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); this.hgContextProvider = hgContextProvider; try diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java index f351ffa572..0897a191a1 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java @@ -41,7 +41,6 @@ import com.aragost.javahg.internals.AbstractCommand; import com.aragost.javahg.internals.HgInputStream; import com.google.common.base.Strings; -import com.google.common.collect.Lists; import sonia.scm.repository.FileObject; import sonia.scm.repository.SubRepository; @@ -52,7 +51,6 @@ import java.io.IOException; import java.util.Deque; import java.util.LinkedList; -import java.util.List; /** * Mercurial command to list files of a repository. diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java index 93b9699fc9..18b716b665 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgPermissionFilter.java @@ -44,7 +44,6 @@ import sonia.scm.web.filter.PermissionFilter; import sonia.scm.repository.HgRepositoryHandler; -import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import java.util.Set; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java index 4b66444bbe..1f88bfe665 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigAutoConfigurationResourceTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java index 524e33e265..6e181f4886 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigDtoToHgConfigMapperTest.java @@ -3,11 +3,9 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; -import java.io.File; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java index 65b9c262cb..bcd9543d28 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsResourceTest.java @@ -14,7 +14,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java index 7cae1d9f7e..80f8ec32b1 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInstallationsToDtoMapperTest.java @@ -6,7 +6,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import java.net.URI; import java.util.Arrays; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java index f1558b6efb..473ddfe4b4 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackageResourceTest.java @@ -17,7 +17,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.installer.HgPackage; import sonia.scm.installer.HgPackageReader; import sonia.scm.net.ahc.AdvancedHttpClient; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java index c4431da6d5..0b5d7b14d0 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigPackagesToDtoMapperTest.java @@ -6,7 +6,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.installer.HgPackage; import sonia.scm.installer.HgPackages; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java index df59954971..e0253ad86a 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigResourceTest.java @@ -16,15 +16,15 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.web.HgVndMediaType; import javax.inject.Provider; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -99,7 +99,7 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "readWrite") - public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException { + public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException { when(repositoryHandler.getConfig()).thenReturn(null); MockHttpResponse response = get(); @@ -110,7 +110,7 @@ public class HgConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java index 84343cdf72..a3430aac43 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigTests.java @@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources; import sonia.scm.installer.HgPackage; import sonia.scm.repository.HgConfig; -import java.io.File; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java index 81c50f3d58..d4bc8be549 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigToHgConfigDtoMapperTest.java @@ -11,7 +11,7 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.HgConfig; import java.net.URI; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java index c45d9ab358..ed222f5119 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgRepositoryHandlerTest.java @@ -77,7 +77,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Override protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) { - HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver); + HgRepositoryHandler handler = new HgRepositoryHandler(factory, new HgContextProvider(), locationResolver, null); handler.init(contextProvider); HgTestUtil.checkForSkip(handler); @@ -87,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { @Test public void getDirectory() { - HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver); + HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null); HgConfig hgConfig = new HgConfig(); hgConfig.setHgBinary("hg"); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java index 68f7e18a76..131dad0837 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/HgTestUtil.java @@ -105,7 +105,7 @@ public final class HgTestUtil RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver()); HgRepositoryHandler handler = - new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver); + new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null); Path repoDir = directory.toPath(); when(repoDao.getPath(any())).thenReturn(repoDir); handler.init(context); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java index 7d74024630..efe9983951 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgHookCallbackServletTest.java @@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java index b3a4a0c2a4..f9bc77bbda 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/HgPermissionFilterTest.java @@ -48,8 +48,6 @@ import javax.servlet.http.HttpServletRequest; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static sonia.scm.web.WireProtocolRequestMockFactory.CMDS_HEADS_KNOWN_NODES; import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.BOOKMARKS; import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.PHASES; diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java index 519dadfd6c..9237127c88 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/web/WireProtocolTest.java @@ -37,7 +37,7 @@ import com.google.common.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java index 86f99cd517..639a16968c 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/SvnRepositoryHandler.java @@ -53,9 +53,9 @@ import sonia.scm.io.INIConfigurationWriter; import sonia.scm.io.INISection; import sonia.scm.logging.SVNKitLogger; import sonia.scm.plugin.Extension; +import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.spi.HookEventFacade; import sonia.scm.repository.spi.SvnRepositoryServiceProvider; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import sonia.scm.util.Util; @@ -97,9 +97,10 @@ public class SvnRepositoryHandler @Inject public SvnRepositoryHandler(ConfigurationStoreFactory storeFactory, HookEventFacade eventFacade, - RepositoryLocationResolver repositoryLocationResolver) + RepositoryLocationResolver repositoryLocationResolver, + PluginLoader pluginLoader) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, pluginLoader); // register logger SVNDebugLog.setDefaultLog(new SVNKitLogger()); diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java index e2f58b593b..df266a11af 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnBrowseCommand.java @@ -36,7 +36,6 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- import com.google.common.base.Strings; -import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tmatesoft.svn.core.SVNDirEntry; @@ -53,7 +52,6 @@ import sonia.scm.repository.SvnUtil; import sonia.scm.util.Util; import java.util.Collection; -import java.util.List; //~--- JDK imports ------------------------------------------------------------ diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java index 4b4f655b12..580bc0b77d 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnModificationsCommand.java @@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.wc.SVNClientManager; +import org.tmatesoft.svn.core.wc.admin.SVNLookClient; import sonia.scm.repository.InternalRepositoryException; import sonia.scm.repository.Modifications; import sonia.scm.repository.Repository; @@ -19,23 +21,45 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif super(context, repository); } - @Override - @SuppressWarnings("unchecked") - public Modifications getModifications(String revision) { - Modifications modifications = null; - log.debug("get modifications {}", revision); + public Modifications getModifications(String revisionOrTransactionId) { + Modifications modifications; try { - long revisionNumber = SvnUtil.parseRevision(revision, repository); - SVNRepository repo = open(); - Collection entries = repo.log(null, null, revisionNumber, - revisionNumber, true, true); - if (Util.isNotEmpty(entries)) { - modifications = SvnUtil.createModifications(entries.iterator().next(), revision); + if (SvnUtil.isTransactionEntryId(revisionOrTransactionId)) { + modifications = getModificationsFromTransaction(SvnUtil.getTransactionId(revisionOrTransactionId)); + } else { + modifications = getModificationFromRevision(revisionOrTransactionId); } + return modifications; } catch (SVNException ex) { - throw new InternalRepositoryException(repository, "could not open repository", ex); + throw new InternalRepositoryException( + repository, + "failed to get svn modifications for " + revisionOrTransactionId, + ex + ); } + } + + @SuppressWarnings("unchecked") + private Modifications getModificationFromRevision(String revision) throws SVNException { + log.debug("get svn modifications from revision: {}", revision); + long revisionNumber = SvnUtil.getRevisionNumber(revision, repository); + SVNRepository repo = open(); + Collection entries = repo.log(null, null, revisionNumber, + revisionNumber, true, true); + if (Util.isNotEmpty(entries)) { + return SvnUtil.createModifications(entries.iterator().next(), revision); + } + return null; + } + + private Modifications getModificationsFromTransaction(String transaction) throws SVNException { + log.debug("get svn modifications from transaction: {}", transaction); + final Modifications modifications = new Modifications(); + SVNLookClient client = SVNClientManager.newInstance().getLookClient(); + client.doGetChanged(context.getDirectory(), transaction, + e -> SvnUtil.appendModification(modifications, e.getType(), e.getPath()), true); + return modifications; } @@ -44,5 +68,4 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif return getModifications(request.getRevision()); } - } diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java index 8ab947fbaf..27ca6d5635 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigDtoToSvnConfigMapperTest.java @@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.Compatibility; import sonia.scm.repository.SvnConfig; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java index 3077bb34f3..f7ccf039b2 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigResourceTest.java @@ -16,14 +16,14 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.SvnConfig; import sonia.scm.repository.SvnRepositoryHandler; import sonia.scm.web.SvnVndMediaType; import javax.servlet.http.HttpServletResponse; -import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -98,7 +98,7 @@ public class SvnConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException { + public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpResponse response = get(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java index 6bbff499e1..07ead15322 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigToSvnConfigDtoMapperTest.java @@ -11,11 +11,10 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.Compatibility; import sonia.scm.repository.SvnConfig; -import java.io.File; import java.net.URI; import static org.junit.Assert.assertEquals; diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java index 7b22e15c94..c81c6311e1 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/SvnRepositoryHandlerTest.java @@ -32,14 +32,10 @@ package sonia.scm.repository; -import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.repository.api.HookContextFactory; import sonia.scm.repository.spi.HookEventFacade; -import sonia.scm.store.ConfigurationStore; import sonia.scm.store.ConfigurationStoreFactory; import java.io.File; @@ -47,7 +43,7 @@ import java.io.IOException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -93,7 +89,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) { - SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver); + SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver, null); handler.init(contextProvider); @@ -109,7 +105,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase { public void getDirectory() { when(factory.withType(any())).thenCallRealMethod(); SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory, - facade, locationResolver); + facade, locationResolver, null); SvnConfig svnConfig = new SvnConfig(); repositoryHandler.setConfig(svnConfig); diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java index f2511a9ad9..0cfeaa3a1c 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SvnLogCommandTest.java @@ -39,8 +39,6 @@ import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.Modifications; -import java.io.IOException; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; diff --git a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java index 3efe78c820..ccaeee8631 100644 --- a/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java +++ b/scm-test/src/main/java/sonia/scm/repository/DummyRepositoryHandler.java @@ -59,7 +59,7 @@ public class DummyRepositoryHandler private final Set existingRepoNames = new HashSet<>(); public DummyRepositoryHandler(ConfigurationStoreFactory storeFactory, RepositoryLocationResolver repositoryLocationResolver) { - super(storeFactory, repositoryLocationResolver); + super(storeFactory, repositoryLocationResolver, null); } @Override diff --git a/scm-ui-components/packages/ui-components/src/BranchSelector.js b/scm-ui-components/packages/ui-components/src/BranchSelector.js index 4b8da5171b..c473527472 100644 --- a/scm-ui-components/packages/ui-components/src/BranchSelector.js +++ b/scm-ui-components/packages/ui-components/src/BranchSelector.js @@ -13,10 +13,11 @@ const styles = { minWidthOfLabel: { minWidth: "4.5rem" }, - wrapper: { - padding: "1rem 1.5rem", - border: "1px solid #eee", - borderRadius: "5px 5px 0 0" + labelSizing: { + fontSize: "1rem !important" + }, + noBottomMargin: { + marginBottom: "0 !important" } }; @@ -52,9 +53,9 @@ class BranchSelector extends React.Component { return (
{ classes.minWidthOfLabel )} > - +
-
+
{ - static format(locale: string, date?: string) { - let fromNow = ""; - if (date) { - fromNow = moment(date) - .locale(locale) - .fromNow(); - } - return fromNow; - } render() { - const { i18n, date } = this.props; + const { i18n, date, classes } = this.props; - const fromNow = DateFromNow.format(i18n.language, date); - return {fromNow}; + if (date) { + const dateWithLocale = moment(date).locale(i18n.language); + + return ( + + ); + } + + return null; } } -export default translate()(DateFromNow); +export default injectSheet(styles)(translate()(DateFromNow)); diff --git a/scm-ui-components/packages/ui-components/src/Tooltip.js b/scm-ui-components/packages/ui-components/src/Tooltip.js index d935b323c7..cecb8d349b 100644 --- a/scm-ui-components/packages/ui-components/src/Tooltip.js +++ b/scm-ui-components/packages/ui-components/src/Tooltip.js @@ -4,21 +4,27 @@ import classNames from "classnames"; type Props = { message: string, - className: string, + className?: string, + location: string, children: React.Node }; class Tooltip extends React.Component { + + static defaultProps = { + location: "right" + }; + render() { - const { className, message, children } = this.props; + const { className, message, location, children } = this.props; const multiline = message.length > 60 ? "is-tooltip-multiline" : ""; return ( -
{children} -
+ ); } } diff --git a/scm-ui-components/packages/ui-components/src/buttons/Button.js b/scm-ui-components/packages/ui-components/src/buttons/Button.js index 4ad88f4eac..2102bb540a 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/Button.js +++ b/scm-ui-components/packages/ui-components/src/buttons/Button.js @@ -1,16 +1,17 @@ //@flow -import React from "react"; +import * as React from "react"; import classNames from "classnames"; import { withRouter } from "react-router-dom"; export type ButtonProps = { - label: string, + label?: string, loading?: boolean, disabled?: boolean, action?: (event: Event) => void, link?: string, fullWidth?: boolean, className?: string, + children?: React.Node, classes: any }; @@ -45,6 +46,7 @@ class Button extends React.Component { type, color, fullWidth, + children, className } = this.props; const loadingClass = loading ? "is-loading" : ""; @@ -62,7 +64,7 @@ class Button extends React.Component { className )} > - {label} + {label} {children} ); }; diff --git a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js index 56ef66522a..fb48a3ba4f 100644 --- a/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js +++ b/scm-ui-components/packages/ui-components/src/buttons/ButtonGroup.js @@ -1,41 +1,30 @@ // @flow -import React from "react"; -import Button from "./Button"; +import * as React from "react"; type Props = { - firstlabel: string, - secondlabel: string, - firstAction?: (event: Event) => void, - secondAction?: (event: Event) => void, - firstIsSelected: boolean + addons?: boolean, + className?: string, + children: React.Node }; class ButtonGroup extends React.Component { + static defaultProps = { + addons: true + }; + render() { - const { firstlabel, secondlabel, firstAction, secondAction, firstIsSelected } = this.props; - - let showFirstColor = ""; - let showSecondColor = ""; - - if (firstIsSelected) { - showFirstColor += "link is-selected"; - } else { - showSecondColor += "link is-selected"; + const { addons, className, children } = this.props; + let styleClasses = "buttons"; + if (addons) { + styleClasses += " has-addons"; + } + if (className) { + styleClasses += " " + className; } - return ( -
-
); diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css deleted file mode 100644 index 96246a44a5..0000000000 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.css +++ /dev/null @@ -1,102 +0,0 @@ -/*modified from https://github.com/GA-MO/react-confirm-alert*/ -.react-confirm-alert-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: 99; - background: rgba(255, 255, 255, 0.9); - display: -webkit-flex; - display: -moz-flex; - display: -ms-flex; - display: -o-flex; - display: flex; - justify-content: center; - -ms-align-items: center; - align-items: center; - opacity: 0; - -webkit-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - -moz-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - -o-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; - animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards; -} - -.react-confirm-alert-body { - font-family: Arial, Helvetica, sans-serif; - width: 400px; - padding: 30px; - text-align: left; - background: #fff; - border-radius: 10px; - box-shadow: 0 20px 75px rgba(0, 0, 0, 0.13); - color: #666; -} - -.react-confirm-alert-body > h1 { - margin-top: 0; -} - -.react-confirm-alert-body > h3 { - margin: 0; - font-size: 16px; -} - -.react-confirm-alert-button-group { - display: -webkit-flex; - display: -moz-flex; - display: -ms-flex; - display: -o-flex; - display: flex; - justify-content: flex-start; - margin-top: 20px; -} - -.react-confirm-alert-button-group > button { - outline: none; - background: #333; - border: none; - display: inline-block; - padding: 6px 18px; - color: #eee; - margin-right: 10px; - border-radius: 5px; - font-size: 12px; - cursor: pointer; -} - -@-webkit-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@-moz-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@-o-keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes react-confirm-alert-fadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} diff --git a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js index 1f93024865..6fe08c8ce8 100644 --- a/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js +++ b/scm-ui-components/packages/ui-components/src/modals/ConfirmAlert.js @@ -1,9 +1,7 @@ // @flow -//modified from https://github.com/GA-MO/react-confirm-alert - import * as React from "react"; -import { render, unmountComponentAtNode } from "react-dom"; -import "./ConfirmAlert.css"; +import ReactDOM from "react-dom"; +import Modal from "./Modal"; type Button = { label: string, @@ -25,59 +23,47 @@ class ConfirmAlert extends React.Component { }; close = () => { - removeElementReconfirm(); + ReactDOM.unmountComponentAtNode(document.getElementById("modalRoot")); }; render() { const { title, message, buttons } = this.props; - return ( -
-
- { -
- {title &&

{title}

} - {message} -
- {buttons.map((button, i) => ( - - ))} -
-
- } -
+ const body = <>{message}; + + const footer = ( +
); + + return ( + this.close()} + body={body} + active={true} + footer={footer} + /> + ); } } -function createElementReconfirm(properties: Props) { - const divTarget = document.createElement("div"); - divTarget.id = "react-confirm-alert"; - if (document.body) { - document.body.appendChild(divTarget); - render(, divTarget); - } -} - -function removeElementReconfirm() { - const target = document.getElementById("react-confirm-alert"); - if (target) { - unmountComponentAtNode(target); - if (target.parentNode) { - target.parentNode.removeChild(target); - } - } -} - export function confirmAlert(properties: Props) { - createElementReconfirm(properties); + const root = document.getElementById("modalRoot"); + if (root) { + ReactDOM.render(, root); + } } export default ConfirmAlert; diff --git a/scm-ui-components/packages/ui-components/src/modals/Modal.js b/scm-ui-components/packages/ui-components/src/modals/Modal.js new file mode 100644 index 0000000000..58da89381d --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/modals/Modal.js @@ -0,0 +1,54 @@ +// @flow +import * as React from "react"; +import classNames from "classnames"; +import injectSheet from "react-jss"; + +type Props = { + title: string, + closeFunction: () => void, + body: any, + footer?: any, + active: boolean, + classes: any +}; + +const styles = { + resize: { + maxWidth: "100%", + width: "auto !important", + display: "inline-block" + } +}; + +class Modal extends React.Component { + render() { + const { title, closeFunction, body, footer, active, classes } = this.props; + + const isActive = active ? "is-active" : null; + + let showFooter = null; + if (footer) { + showFooter =
{footer}
; + } + + return ( +
+
+
+
+

{title}

+
+
{body}
+ {showFooter} +
+
+ ); + } +} + +export default injectSheet(styles)(Modal); diff --git a/scm-ui-components/packages/ui-components/src/modals/index.js b/scm-ui-components/packages/ui-components/src/modals/index.js index e75d082c1c..13b205dd1c 100644 --- a/scm-ui-components/packages/ui-components/src/modals/index.js +++ b/scm-ui-components/packages/ui-components/src/modals/index.js @@ -1,4 +1,5 @@ // @create-index export { default as ConfirmAlert, confirmAlert } from "./ConfirmAlert.js"; +export { default as Modal } from "./Modal.js"; diff --git a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js index 53b124ef31..98c3138a8f 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/NavLink.js +++ b/scm-ui-components/packages/ui-components/src/navigation/NavLink.js @@ -28,7 +28,7 @@ class NavLink extends React.Component { let showIcon = null; if (icon) { - showIcon = (<>{" "}); + showIcon = (<>{" "}); } return ( diff --git a/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js b/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js index f2401729d6..897c63138e 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js +++ b/scm-ui-components/packages/ui-components/src/navigation/PrimaryNavigation.js @@ -50,8 +50,19 @@ class PrimaryNavigation extends React.Component { createNavigationItems = () => { const navigationItems = []; + const { t, links } = this.props; + + const props = { + links, + label: t("primary-navigation.first-menu") + }; const append = this.createNavigationAppender(navigationItems); + if (binder.hasExtension("primary-navigation.first-menu", props)) { + navigationItems.push( + + ); + } append("/repos", "/(repo|repos)", "primary-navigation.repositories", "repositories"); append("/users", "/(user|users)", "primary-navigation.users", "users"); append("/groups", "/(group|groups)", "primary-navigation.groups", "groups"); diff --git a/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js new file mode 100644 index 0000000000..0a6612a173 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/navigation/SubNavigation.js @@ -0,0 +1,65 @@ +//@flow +import * as React from "react"; +import { Link, Route } from "react-router-dom"; + +type Props = { + to: string, + icon?: string, + label: string, + activeOnlyWhenExact?: boolean, + activeWhenMatch?: (route: any) => boolean, + children?: React.Node +}; + +class SubNavigation extends React.Component { + static defaultProps = { + activeOnlyWhenExact: false + }; + + isActive(route: any) { + const { activeWhenMatch } = this.props; + return route.match || (activeWhenMatch && activeWhenMatch(route)); + } + + renderLink = (route: any) => { + const { to, icon, label } = this.props; + + let defaultIcon = "fas fa-cog"; + if (icon) { + defaultIcon = icon; + } + + let children = null; + if (this.isActive(route)) { + children =
    {this.props.children}
; + } + + return ( +
  • + + {label} + + {children} +
  • + ); + }; + + render() { + const { to, activeOnlyWhenExact } = this.props; + + // removes last part of url + let parents = to.split("/"); + parents.splice(-1, 1); + let parent = parents.join("/"); + + return ( + + ); + } +} + +export default SubNavigation; diff --git a/scm-ui-components/packages/ui-components/src/navigation/index.js b/scm-ui-components/packages/ui-components/src/navigation/index.js index ca82073b56..b696f98328 100644 --- a/scm-ui-components/packages/ui-components/src/navigation/index.js +++ b/scm-ui-components/packages/ui-components/src/navigation/index.js @@ -3,6 +3,7 @@ export { default as NavAction } from "./NavAction.js"; export { default as NavLink } from "./NavLink.js"; export { default as Navigation } from "./Navigation.js"; +export { default as SubNavigation } from "./SubNavigation.js"; export { default as PrimaryNavigation } from "./PrimaryNavigation.js"; export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js"; export { default as Section } from "./Section.js"; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js index 5bb6437575..4035cbd899 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetAuthor.js @@ -1,9 +1,14 @@ //@flow import React from "react"; -import type {Changeset} from "@scm-manager/ui-types"; +import type { Changeset } from "@scm-manager/ui-types"; +import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import {translate} from "react-i18next"; type Props = { - changeset: Changeset + changeset: Changeset, + + // context props + t: (string) => string }; class ChangesetAuthor extends React.Component { @@ -13,26 +18,35 @@ class ChangesetAuthor extends React.Component { return null; } - const { name } = changeset.author; + const { name, mail } = changeset.author; + if (mail) { + return this.withExtensionPoint(this.renderWithMail(name, mail)); + } + return this.withExtensionPoint(<>{name}); + } + + renderWithMail(name: string, mail: string) { + const { t } = this.props; return ( - <> - {name} {this.renderMail()} - + + {name} + ); } - renderMail() { - const { mail } = this.props.changeset.author; - if (mail) { - return ( - - < - {mail} - > - - ); - } + withExtensionPoint(child: any) { + const { t } = this.props; + return ( + <> + {t("changeset.author.prefix")} {child} + + + ); } } -export default ChangesetAuthor; +export default translate("repos")(ChangesetAuthor); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js new file mode 100644 index 0000000000..de05efb46e --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetButtonGroup.js @@ -0,0 +1,49 @@ +//@flow +import React from "react"; +import type { Changeset, Repository } from "@scm-manager/ui-types"; +import ButtonGroup from "../../buttons/ButtonGroup"; +import Button from "../../buttons/Button"; +import { createChangesetLink, createSourcesLink } from "./changesets"; +import { translate } from "react-i18next"; + +type Props = { + repository: Repository, + changeset: Changeset, + + // context props + t: (string) => string +} + +class ChangesetButtonGroup extends React.Component { + + render() { + const { repository, changeset, t } = this.props; + + const changesetLink = createChangesetLink(repository, changeset); + const sourcesLink = createSourcesLink(repository, changeset); + + return ( + + + + + ); + } + +} + +export default translate("repos")(ChangesetButtonGroup); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js index 857ff8c827..d5d3c4e665 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js @@ -25,7 +25,7 @@ class ChangesetDiff extends React.Component { render() { const { changeset, t } = this.props; if (!this.isDiffSupported(changeset)) { - return {t("changesets.diff.not-supported")}; + return {t("changeset.diffNotSupported")}; } else { const url = this.createUrl(changeset); return ; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js index aec1029427..a3cc3c73b7 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetId.js @@ -3,6 +3,7 @@ import {Link} from "react-router-dom"; import React from "react"; import type {Changeset, Repository} from "@scm-manager/ui-types"; +import { createChangesetLink } from "./changesets"; type Props = { repository: Repository, @@ -20,13 +21,11 @@ export default class ChangesetId extends React.Component { }; renderLink = () => { - const { changeset, repository } = this.props; + const { repository, changeset } = this.props; + const link = createChangesetLink(repository, changeset); + return ( - + {this.shortId(changeset)} ); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js index 74ec816369..4cd8ec319c 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetList.js @@ -21,7 +21,7 @@ class ChangesetList extends React.Component { /> ); }); - return
    {content}
    ; + return <>{content}; } } diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js index 7609bc2171..90a568f615 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetRow.js @@ -8,21 +8,39 @@ import ChangesetId from "./ChangesetId"; import injectSheet from "react-jss"; import { DateFromNow } from "../.."; import ChangesetAuthor from "./ChangesetAuthor"; -import ChangesetTag from "./ChangesetTag"; - import { parseDescription } from "./changesets"; import { AvatarWrapper, AvatarImage } from "../../avatar"; import { ExtensionPoint } from "@scm-manager/ui-extensions"; +import ChangesetTags from "./ChangesetTags"; +import ChangesetButtonGroup from "./ChangesetButtonGroup"; const styles = { - pointer: { - cursor: "pointer" + changeset: { + // & references parent rule + // have a look at https://cssinjs.org/jss-plugin-nested?v=v10.0.0-alpha.9 + "& + &": { + borderTop: "1px solid rgba(219, 219, 219, 0.5)", + marginTop: "1rem", + paddingTop: "1rem" + } }, - changesetGroup: { - marginBottom: "1em" + avatarFigure: { + marginTop: ".25rem", + marginRight: ".5rem", }, - withOverflow: { - overflow: "auto" + avatarImage: { + height: "35px", + width: "35px" + }, + isVcentered: { + marginTop: "auto", + marginBottom: "auto" + }, + metadata: { + marginLeft: 0 + }, + tag: { + marginTop: ".5rem" } }; @@ -34,74 +52,70 @@ type Props = { }; class ChangesetRow extends React.Component { - createLink = (changeset: Changeset) => { + createChangesetId = (changeset: Changeset) => { const { repository } = this.props; return ; }; - getTags = () => { - const { changeset } = this.props; - return changeset._embedded.tags || []; - }; - render() { - const { changeset, classes } = this.props; - const changesetLink = this.createLink(changeset); - const dateFromNow = ; - const authorLine = ; + const { repository, changeset, classes } = this.props; const description = parseDescription(changeset.description); + const changesetId = this.createChangesetId(changeset); + const dateFromNow = ; return ( -
    - -
    -
    -

    - -

    -
    +
    +
    +
    + +

    + + {description.title} + +

    + +
    + +
    +
    + +
    +
    +
    +
    +

    + +

    +

    + +

    +

    + +

    +
    +
    +
    - -
    -
    -

    - - - {description.title} - - -
    - -

    {" "} -
    {authorLine}
    +
    + +
    - {this.renderTags()} -
    +
    ); } - - renderTags = () => { - const tags = this.getTags(); - if (tags.length > 0) { - return ( -
    - {tags.map((tag: Tag) => { - return ; - })} -
    - ); - } - return null; - }; } export default injectSheet(styles)(translate("repos")(ChangesetRow)); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js index 6a87400d2e..03c73a8e9f 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTag.js @@ -1,32 +1,17 @@ //@flow import React from "react"; import type { Tag } from "@scm-manager/ui-types"; -import injectSheet from "react-jss"; -import classNames from "classnames"; - -const styles = { - spacing: { - marginRight: "4px" - } -}; +import ChangesetTagBase from "./ChangesetTagBase"; type Props = { - tag: Tag, - - // context props - classes: Object + tag: Tag }; class ChangesetTag extends React.Component { render() { - const { tag, classes } = this.props; - return ( - - {" "} - {tag.name} - - ); + const { tag } = this.props; + return ; } } -export default injectSheet(styles)(ChangesetTag); +export default ChangesetTag; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js new file mode 100644 index 0000000000..7fc8dabcd2 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagBase.js @@ -0,0 +1,34 @@ +//@flow +import React from "react"; +import injectSheet from "react-jss"; +import classNames from "classnames"; + +const styles = { + tag: { + marginTop: ".5rem" + }, + spacing: { + marginRight: ".25rem" + } +}; + +type Props = { + icon: string, + label: string, + + // context props + classes: Object +}; + +class ChangesetTagBase extends React.Component { + render() { + const { icon, label, classes } = this.props; + return ( + + {label} + + ); + } +} + +export default injectSheet(styles)(ChangesetTagBase); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js new file mode 100644 index 0000000000..b8bff8ddd2 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTags.js @@ -0,0 +1,31 @@ +//@flow +import React from "react"; +import type { Changeset} from "@scm-manager/ui-types"; +import ChangesetTag from "./ChangesetTag"; +import ChangesetTagsCollapsed from "./ChangesetTagsCollapsed"; + +type Props = { + changeset: Changeset +}; + +class ChangesetTags extends React.Component { + + getTags = () => { + const { changeset } = this.props; + return changeset._embedded.tags || []; + }; + + render() { + const tags = this.getTags(); + + if (tags.length === 1) { + return ; + } else if (tags.length > 1) { + return ; + } else { + return null; + } + } +} + +export default ChangesetTags; diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js new file mode 100644 index 0000000000..50b09d4d65 --- /dev/null +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetTagsCollapsed.js @@ -0,0 +1,27 @@ +//@flow +import React from "react"; +import type { Tag } from "@scm-manager/ui-types"; +import ChangesetTagBase from "./ChangesetTagBase"; +import { translate } from "react-i18next"; +import Tooltip from "../../Tooltip"; + +type Props = { + tags: Tag[], + + // context props + t: (string) => string +}; + +class ChangesetTagsCollapsed extends React.Component { + render() { + const { tags, t } = this.props; + const message = tags.map((tag) => tag.name).join(", "); + return ( + + + + ); + } +} + +export default translate("repos")(ChangesetTagsCollapsed); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js b/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js index f61a89c74b..69227ad75d 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/changesets.js @@ -1,9 +1,19 @@ // @flow +import type { Changeset, Repository } from "@scm-manager/ui-types"; + export type Description = { title: string, message: string }; +export function createChangesetLink(repository: Repository, changeset: Changeset) { + return `/repo/${repository.namespace}/${repository.name}/changeset/${changeset.id}`; +} + +export function createSourcesLink(repository: Repository, changeset: Changeset) { + return `/repo/${repository.namespace}/${repository.name}/sources/${changeset.id}`; +} + export function parseDescription(description?: string): Description { const desc = description ? description : ""; const lineBreak = desc.indexOf("\n"); diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js index 0e7a5e533d..7a07ad8f42 100644 --- a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js +++ b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js @@ -3,8 +3,11 @@ import * as changesets from "./changesets"; export { changesets }; export { default as ChangesetAuthor } from "./ChangesetAuthor"; +export { default as ChangesetButtonGroup } from "./ChangesetButtonGroup"; +export { default as ChangesetDiff } from "./ChangesetDiff"; export { default as ChangesetId } from "./ChangesetId"; export { default as ChangesetList } from "./ChangesetList"; export { default as ChangesetRow } from "./ChangesetRow"; export { default as ChangesetTag } from "./ChangesetTag"; -export { default as ChangesetDiff } from "./ChangesetDiff"; +export { default as ChangesetTags } from "./ChangesetTags"; +export { default as ChangesetTagsCollapsed } from "./ChangesetTagsCollapsed"; diff --git a/scm-ui/package.json b/scm-ui/package.json index bf4e272fb0..2144e50e6c 100644 --- a/scm-ui/package.json +++ b/scm-ui/package.json @@ -17,6 +17,7 @@ "i18next": "^11.4.0", "i18next-browser-languagedetector": "^2.2.2", "i18next-fetch-backend": "^0.1.0", + "jss-nested": "^6.0.1", "moment": "^2.22.2", "node-sass": "^4.9.3", "postcss-easy-import": "^3.0.0", diff --git a/scm-ui/public/index.mustache b/scm-ui/public/index.mustache index 590b5e3cdb..75efc78088 100644 --- a/scm-ui/public/index.mustache +++ b/scm-ui/public/index.mustache @@ -21,6 +21,8 @@ You need to enable JavaScript to run this app.
    +
    + @@ -158,6 +158,24 @@ ${resteasy.version} + + org.hibernate + hibernate-validator + 5.3.6.Final + + + + javax.el + javax.el-api + 2.2.4 + + + + org.glassfish.web + javax.el + 2.2.4 + + @@ -203,18 +221,6 @@ 1.4.01 - - - - commons-beanutils - commons-beanutils - - - - commons-collections - commons-collections - - @@ -561,8 +567,6 @@ 2.53.1 1.0 0.8.17 - 3.1.4.Final - 2.8.9 Tomcat e1 javascript:S3827 diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java new file mode 100644 index 0000000000..1d268b6855 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/NotAllowedExceptionMapper.java @@ -0,0 +1,12 @@ +package sonia.scm.api.rest; + +import javax.ws.rs.NotAllowedException; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; + +@Provider +public class NotAllowedExceptionMapper extends StatusExceptionMapper { + public NotAllowedExceptionMapper() { + super(NotAllowedException.class, Response.Status.METHOD_NOT_ALLOWED); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java index 4253c456fb..dfc0bd2a5d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/AbstractManagerResource.java @@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources; import com.google.common.annotations.VisibleForTesting; import com.google.common.net.UrlEscapers; -import org.apache.commons.beanutils.BeanComparator; import org.apache.shiro.authz.AuthorizationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,6 +46,7 @@ import sonia.scm.ModelObject; import sonia.scm.PageResult; import sonia.scm.api.rest.RestExceptionResult; import sonia.scm.util.AssertUtil; +import sonia.scm.util.Comparables; import sonia.scm.util.Util; import javax.ws.rs.core.CacheControl; @@ -56,15 +56,10 @@ import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; -import java.beans.BeanInfo; -import java.beans.IntrospectionException; -import java.beans.Introspector; -import java.beans.PropertyDescriptor; -import java.util.Arrays; +import java.net.URI; import java.util.Collection; import java.util.Comparator; import java.util.Date; -import java.net.URI; //~--- JDK imports ------------------------------------------------------------ @@ -510,21 +505,11 @@ public abstract class AbstractManagerResource { return builder.build(); } - @SuppressWarnings("unchecked") - private Comparator createComparator(String sortBy, boolean desc) - { - checkSortByField(sortBy); - Comparator comparator; - - if (desc) - { - comparator = new BeanReverseComparator(sortBy); + private Comparator createComparator(String sortBy, boolean desc) { + Comparator comparator = Comparables.comparator(type, sortBy); + if (desc) { + comparator = comparator.reversed(); } - else - { - comparator = new BeanComparator(sortBy); - } - return comparator; } @@ -558,21 +543,6 @@ public abstract class AbstractManagerResource { return items; } - // We have to handle IntrospectionException here, because it's a checked exception - // It shouldn't occur really - so creating a new unchecked exception would be over-engineered here - @SuppressWarnings("squid:S00112") - private void checkSortByField(String sortBy) { - try { - BeanInfo info = Introspector.getBeanInfo(type); - PropertyDescriptor[] pds = info.getPropertyDescriptors(); - if (Arrays.stream(pds).noneMatch(p -> p.getName().equals(sortBy))) { - throw new IllegalArgumentException("sortBy"); - } - } catch (IntrospectionException e) { - throw new RuntimeException("error introspecting model type " + type.getName(), e); - } - } - protected PageResult fetchPage(String sortBy, boolean desc, int pageNumber, int pageSize) { AssertUtil.assertPositive(pageNumber); @@ -608,51 +578,4 @@ public abstract class AbstractManagerResource { return lastModified; } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 11/06/09 - * @author Enter your name here... - */ - private static class BeanReverseComparator extends BeanComparator - { - - /** Field description */ - private static final long serialVersionUID = -8535047820348790009L; - - //~--- constructors ------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param sortby - */ - private BeanReverseComparator(String sortby) - { - super(sortby); - } - - //~--- methods ------------------------------------------------------------ - - /** - * Method description - * - * - * @param o1 - * @param o2 - * - * @return - */ - @Override - @SuppressWarnings("unchecked") - public int compare(Object o1, Object o2) - { - return super.compare(o1, o2) * -1; - } - } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java index d2ce744c19..79b5dbc2ae 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/BrowserStreamingOutput.java @@ -6,8 +6,6 @@ import sonia.scm.repository.api.CatCommandBuilder; import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; -import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.IOException; import java.io.OutputStream; diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java index d177e05a5e..b7f994b967 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/DiffStreamingOutput.java @@ -42,7 +42,6 @@ import sonia.scm.repository.api.RepositoryService; import sonia.scm.util.IOUtil; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; import javax.ws.rs.core.StreamingOutput; import java.io.IOException; import java.io.OutputStream; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java index f5bdc850ab..343d9c8bc8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -12,8 +13,7 @@ public class BranchDto extends HalRepresentation { private String name; private String revision; - @Override - protected HalRepresentation add(Links links) { - return super.add(links); + BranchDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 658abbded8..71b1127ad8 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -26,7 +26,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; -import java.util.List; import static sonia.scm.ContextEntry.ContextBuilder.entity; import static sonia.scm.NotFoundException.notFound; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 7e6f0c074c..c940b1ffd9 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.NamespaceAndName; @@ -15,7 +15,7 @@ import static de.otto.edison.hal.Link.linkBuilder; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper { +public abstract class BranchToBranchDtoMapper extends HalAppenderMapper { @Inject private ResourceLinks resourceLinks; @@ -23,16 +23,17 @@ public abstract class BranchToBranchDtoMapper extends LinkAppenderMapper { @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName); - @AfterMapping - void appendLinks(Branch source, @MappingTarget BranchDto target, @Context NamespaceAndName namespaceAndName) { + @ObjectFactory + BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch) { Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.branch().self(namespaceAndName, target.getName())) - .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build()) - .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()) - .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); + .self(resourceLinks.branch().self(namespaceAndName, branch.getName())) + .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build()) + .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build()) + .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), branch.getRevision())).build()); - appendLinks(new EdisonLinkAppender(linksBuilder), source, namespaceAndName); + Embedded.Builder embeddedBuilder = Embedded.embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName); - target.add(linksBuilder.build()); + return new BranchDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java similarity index 64% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java index 219062d320..479a43aef1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.Branch; import sonia.scm.repository.Changeset; import sonia.scm.repository.Repository; @@ -19,11 +19,12 @@ import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @Mapper -public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMapper implements InstantAttributeMapper, ChangesetToChangesetDtoMapper{ @Inject private RepositoryServiceFactory serviceFactory; @@ -31,7 +32,6 @@ public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper i @Inject private ResourceLinks resourceLinks; - @Inject private BranchCollectionToDtoMapper branchCollectionToDtoMapper; @@ -46,31 +46,35 @@ public abstract class ChangesetToChangesetDtoMapper extends LinkAppenderMapper i public abstract ChangesetDto map(Changeset changeset, @Context Repository repository); - @AfterMapping - void appendLinks(Changeset source, @MappingTarget ChangesetDto target, @Context Repository repository) { + @ObjectFactory + ChangesetDto createDto(@Context Repository repository, Changeset source) { String namespace = repository.getNamespace(); String name = repository.getName(); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + try (RepositoryService repositoryService = serviceFactory.create(repository)) { if (repositoryService.isSupported(Command.TAGS)) { - target.withEmbedded("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, + embeddedBuilder.with("tags", tagCollectionToDtoMapper.getTagDtoList(namespace, name, getListOfObjects(source.getTags(), tagName -> new Tag(tagName, source.getId())))); } if (repositoryService.isSupported(Command.BRANCHES)) { - target.withEmbedded("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, + embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, getListOfObjects(source.getBranches(), branchName -> new Branch(branchName, source.getId())))); } } - target.withEmbedded("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); + embeddedBuilder.with("parents", getListOfObjects(source.getParents(), parent -> changesetToParentDtoMapper.map(new Changeset(parent, 0L, null), repository))); Links.Builder linksBuilder = linkingTo() - .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId())) - .single(link("diff", resourceLinks.diff().self(namespace, name, target.getId()))) - .single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId()))); + .self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), source.getId())) + .single(link("diff", resourceLinks.diff().self(namespace, name, source.getId()))) + .single(link("sources", resourceLinks.source().self(namespace, name, source.getId()))) + .single(link("modifications", resourceLinks.modifications().self(namespace, name, source.getId()))); - appendLinks(new EdisonLinkAppender(linksBuilder), source, repository); - target.add(linksBuilder.build()); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), source, repository); + + return new ChangesetDto(linksBuilder.build(), embeddedBuilder.build()); } private List getListOfObjects(List list, Function mapFunction) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java similarity index 52% rename from scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java rename to scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java index c4e699cb58..769de2b705 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonLinkAppender.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/EdisonHalAppender.java @@ -1,27 +1,36 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; +import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import java.util.ArrayList; import java.util.List; -class EdisonLinkAppender implements LinkAppender { +class EdisonHalAppender implements HalAppender { - private final Links.Builder builder; + private final Links.Builder linkBuilder; + private final Embedded.Builder embeddedBuilder; - EdisonLinkAppender(Links.Builder builder) { - this.builder = builder; + EdisonHalAppender(Links.Builder linkBuilder, Embedded.Builder embeddedBuilder) { + this.linkBuilder = linkBuilder; + this.embeddedBuilder = embeddedBuilder; } @Override - public void appendOne(String rel, String href) { - builder.single(Link.link(rel, href)); + public void appendLink(String rel, String href) { + linkBuilder.single(Link.link(rel, href)); } @Override - public LinkArrayBuilder arrayBuilder(String rel) { - return new EdisonLinkArrayBuilder(builder, rel); + public LinkArrayBuilder linkArrayBuilder(String rel) { + return new EdisonLinkArrayBuilder(linkBuilder, rel); + } + + @Override + public void appendEmbedded(String rel, HalRepresentation embedded) { + embeddedBuilder.with(rel, embedded); } private static class EdisonLinkArrayBuilder implements LinkArrayBuilder { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java index bd889d5de5..d155fbede6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ErrorDto.java @@ -5,7 +5,6 @@ import lombok.Getter; import lombok.Setter; import sonia.scm.ContextEntry; -import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java index c183d731c6..0bce564e35 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectDto.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -27,10 +28,8 @@ public class FileObjectDto extends HalRepresentation { @JsonInclude(JsonInclude.Include.NON_EMPTY) private String revision; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + public FileObjectDto(Links links, Embedded embedded) { + super(links, embedded); } public void setChildren(List children) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java index 2432d5168c..608dea9f26 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapper.java @@ -1,24 +1,22 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Context; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.repository.FileObject; import sonia.scm.repository.NamespaceAndName; import sonia.scm.repository.SubRepository; import javax.inject.Inject; -import java.util.List; -import java.util.stream.Collectors; - +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; @Mapper -public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper implements InstantAttributeMapper { +public abstract class FileObjectToFileObjectDtoMapper extends HalAppenderMapper implements InstantAttributeMapper { @Inject private ResourceLinks resourceLinks; @@ -28,20 +26,21 @@ public abstract class FileObjectToFileObjectDtoMapper extends LinkAppenderMapper abstract SubRepositoryDto mapSubrepository(SubRepository subRepository); - @AfterMapping - void addLinks(FileObject fileObject, @MappingTarget FileObjectDto dto, @Context NamespaceAndName namespaceAndName, @Context String revision) { + @ObjectFactory + FileObjectDto createDto(@Context NamespaceAndName namespaceAndName, @Context String revision, FileObject fileObject) { String path = removeFirstSlash(fileObject.getPath()); Links.Builder links = Links.linkingTo(); - if (dto.isDirectory()) { + if (fileObject.isDirectory()) { links.self(resourceLinks.source().sourceWithPath(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)); } else { links.self(resourceLinks.source().content(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path)); links.single(link("history", resourceLinks.fileHistory().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), revision, path))); } - appendLinks(new EdisonLinkAppender(links), fileObject, namespaceAndName, revision); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(links, embeddedBuilder), fileObject, namespaceAndName, revision); - dto.add(links.build()); + return new FileObjectDto(links.build(), embeddedBuilder.build()); } private String removeFirstSlash(String source) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java index 4c111e6707..6c13dc33a5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupCollectionResource.java @@ -10,7 +10,6 @@ import sonia.scm.group.GroupManager; import sonia.scm.web.VndMediaType; import javax.inject.Inject; -import javax.inject.Named; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java index 760beab1da..a150570316 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDto.java @@ -1,12 +1,12 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.hibernate.validator.constraints.NotEmpty; import javax.validation.constraints.Pattern; import java.time.Instant; @@ -28,13 +28,7 @@ public class GroupDto extends HalRepresentation { private Map properties; private List members; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); - } - - public HalRepresentation withMembers(List members) { - return super.withEmbedded("members", members); + GroupDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java index be1aca5814..3812f700da 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupDtoToGroupMapper.java @@ -4,8 +4,6 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import sonia.scm.group.Group; -import java.time.Instant; - @Mapper public abstract class GroupDtoToGroupMapper extends BaseDtoMapper { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java index bf866af350..7d5ddae548 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapper.java @@ -1,9 +1,9 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; 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.group.Group; import sonia.scm.group.GroupPermissions; import sonia.scm.security.PermissionPermissions; @@ -12,6 +12,7 @@ import javax.inject.Inject; import java.util.List; import java.util.stream.Collectors; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -23,28 +24,26 @@ public abstract class GroupToGroupDtoMapper extends BaseMapper @Inject private ResourceLinks resourceLinks; - @AfterMapping - void appendLinks(Group group, @MappingTarget GroupDto target) { - Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(target.getName())); + @ObjectFactory + GroupDto createDto(Group group) { + Links.Builder linksBuilder = linkingTo().self(resourceLinks.group().self(group.getName())); if (GroupPermissions.delete(group).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.group().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.group().delete(group.getName()))); } if (GroupPermissions.modify(group).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.group().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.group().update(group.getName()))); } if (PermissionPermissions.read().isPermitted()) { - linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(target.getName()))); + linksBuilder.single(link("permissions", resourceLinks.groupPermissions().permissions(group.getName()))); } - appendLinks(new EdisonLinkAppender(linksBuilder), group); - - target.add(linksBuilder.build()); - } - - @AfterMapping - void mapMembers(Group group, @MappingTarget GroupDto target) { + Embedded.Builder embeddedBuilder = embeddedBuilder(); List memberDtos = group.getMembers().stream().map(this::createMember).collect(Collectors.toList()); - target.withMembers(memberDtos); + embeddedBuilder.with("members", memberDtos); + + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), group); + + return new GroupDto(linksBuilder.build(), embeddedBuilder.build()); } private MemberDto createMember(String name) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java index 9346420f58..16f945332d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -9,8 +10,8 @@ public class IndexDto extends HalRepresentation { private final String version; - IndexDto(String version, Links links) { - super(links); + IndexDto(Links links, Embedded embedded, String version) { + super(links, embedded); this.version = version; } } 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 3eff661385..90445bcdc2 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 @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.Lists; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; @@ -13,9 +14,10 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; import java.util.List; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; -public class IndexDtoGenerator extends LinkAppenderMapper { +public class IndexDtoGenerator extends HalAppenderMapper { private final ResourceLinks resourceLinks; private final SCMContextProvider scmContextProvider; @@ -61,8 +63,9 @@ public class IndexDtoGenerator extends LinkAppenderMapper { builder.single(link("login", resourceLinks.authentication().jsonLogin())); } - appendLinks(new EdisonLinkAppender(builder), new Index()); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(builder, embeddedBuilder), new Index()); - return new IndexDto(scmContextProvider.getVersion(), builder.build()); + return new IndexDto(builder.build(), embeddedBuilder.build(), scmContextProvider.getVersion()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java index 890e268ed5..8472eb9fc1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistration.java @@ -10,30 +10,30 @@ import javax.servlet.ServletContextListener; import java.util.Set; /** - * Registers every {@link LinkEnricher} which is annotated with an {@link Enrich} annotation. + * Registers every {@link HalEnricher} which is annotated with an {@link Enrich} annotation. */ @Extension public class LinkEnricherAutoRegistration implements ServletContextListener { private static final Logger LOG = LoggerFactory.getLogger(LinkEnricherAutoRegistration.class); - private final LinkEnricherRegistry registry; - private final Set enrichers; + private final HalEnricherRegistry registry; + private final Set enrichers; @Inject - public LinkEnricherAutoRegistration(LinkEnricherRegistry registry, Set enrichers) { + public LinkEnricherAutoRegistration(HalEnricherRegistry registry, Set enrichers) { this.registry = registry; this.enrichers = enrichers; } @Override public void contextInitialized(ServletContextEvent sce) { - for (LinkEnricher enricher : enrichers) { + for (HalEnricher enricher : enrichers) { Enrich annotation = enricher.getClass().getAnnotation(Enrich.class); if (annotation != null) { registry.register(annotation.value(), enricher); } else { - LOG.warn("found LinkEnricher extension {} without Enrich annotation", enricher.getClass()); + LOG.warn("found HalEnricher extension {} without Enrich annotation", enricher.getClass()); } } } 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 859a6481f6..c74f16ad70 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 @@ -28,7 +28,7 @@ public class MapperModule extends AbstractModule { bind(RepositoryPermissionDtoToRepositoryPermissionMapper.class).to(Mappers.getMapper(RepositoryPermissionDtoToRepositoryPermissionMapper.class).getClass()); bind(RepositoryPermissionToRepositoryPermissionDtoMapper.class).to(Mappers.getMapper(RepositoryPermissionToRepositoryPermissionDtoMapper.class).getClass()); - bind(ChangesetToChangesetDtoMapper.class).to(Mappers.getMapper(ChangesetToChangesetDtoMapper.class).getClass()); + bind(ChangesetToChangesetDtoMapper.class).to(Mappers.getMapper(DefaultChangesetToChangesetDtoMapper.class).getClass()); bind(ChangesetToParentDtoMapper.class).to(Mappers.getMapper(ChangesetToParentDtoMapper.class).getClass()); bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass()); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java index 5488faca28..84fbbfe290 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDto.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -18,9 +19,7 @@ public class MeDto extends HalRepresentation { private String mail; private List groups; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + MeDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java index 082db7fd94..b5e1998066 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/MeDtoFactory.java @@ -1,6 +1,7 @@ package sonia.scm.api.v2.resources; import com.google.common.collect.ImmutableList; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; import org.apache.shiro.subject.PrincipalCollection; @@ -13,10 +14,11 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; import java.util.Collections; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; -public class MeDtoFactory extends LinkAppenderMapper { +public class MeDtoFactory extends HalAppenderMapper { private final ResourceLinks resourceLinks; private final UserManager userManager; @@ -29,15 +31,11 @@ public class MeDtoFactory extends LinkAppenderMapper { public MeDto create() { PrincipalCollection principals = getPrincipalCollection(); - - MeDto dto = new MeDto(); - User user = principals.oneByType(User.class); + MeDto dto = createDto(user); mapUserProperties(user, dto); mapGroups(principals, dto); - - appendLinks(user, dto); return dto; } @@ -61,21 +59,22 @@ public class MeDtoFactory extends LinkAppenderMapper { } - private void appendLinks(User user, MeDto target) { + private MeDto createDto(User user) { Links.Builder linksBuilder = linkingTo().self(resourceLinks.me().self()); if (UserPermissions.delete(user).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.me().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.me().delete(user.getName()))); } if (UserPermissions.modify(user).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.me().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.me().update(user.getName()))); } if (userManager.isTypeDefault(user) && UserPermissions.changePassword(user).isPermitted()) { linksBuilder.single(link("password", resourceLinks.me().passwordChange())); } - appendLinks(new EdisonLinkAppender(linksBuilder), new Me(), user); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), new Me(), user); - target.add(linksBuilder.build()); + return new MeDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java index 87d1aeca9f..31269d468e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/PermissionCollectionToDtoMapper.java @@ -1,7 +1,6 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; -import org.mapstruct.Context; import sonia.scm.security.PermissionDescriptor; import sonia.scm.security.PermissionPermissions; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java index a9bd5c2424..e1e1260a4d 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryCollectionResource.java @@ -23,7 +23,6 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; -import static java.util.Arrays.asList; import static java.util.Collections.singletonList; public class RepositoryCollectionResource { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java index ddfe432d73..8b48311bba 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryDto.java @@ -1,9 +1,11 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.annotation.JsonInclude; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.validator.constraints.Email; import org.hibernate.validator.constraints.NotEmpty; @@ -13,7 +15,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; -@Getter @Setter +@Getter @Setter @NoArgsConstructor public class RepositoryDto extends HalRepresentation { @Email @@ -31,9 +33,7 @@ public class RepositoryDto extends HalRepresentation { private String type; protected Map properties; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + RepositoryDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java index 09683db488..fe8c2c19b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionDto.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import com.fasterxml.jackson.annotation.JsonInclude; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; import lombok.Getter; @@ -9,7 +8,6 @@ import lombok.Setter; import lombok.ToString; import org.hibernate.validator.constraints.NotEmpty; -import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import java.util.Collection; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java index 19929b63ba..9e680b7e5c 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapper.java @@ -1,11 +1,11 @@ package sonia.scm.api.v2.resources; import com.google.inject.Inject; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Link; 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.repository.Feature; import sonia.scm.repository.HealthCheckFailure; import sonia.scm.repository.Repository; @@ -17,6 +17,7 @@ import sonia.scm.repository.api.ScmProtocol; import java.util.List; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; import static java.util.stream.Collectors.toList; @@ -33,17 +34,17 @@ public abstract class RepositoryToRepositoryDtoMapper extends BaseMapper properties; - @Override - @SuppressWarnings("squid:S1185") // We want to have this method available in this package - protected HalRepresentation add(Links links) { - return super.add(links); + UserDto(Links links, Embedded embedded) { + super(links, embedded); } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java index 3c7e9fd7f1..ac641e3e66 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/UserToUserDtoMapper.java @@ -1,10 +1,10 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Embedded; import de.otto.edison.hal.Links; -import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; +import org.mapstruct.ObjectFactory; import sonia.scm.security.PermissionPermissions; import sonia.scm.user.User; import sonia.scm.user.UserManager; @@ -12,6 +12,7 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; +import static de.otto.edison.hal.Embedded.embeddedBuilder; import static de.otto.edison.hal.Link.link; import static de.otto.edison.hal.Links.linkingTo; @@ -31,25 +32,26 @@ public abstract class UserToUserDtoMapper extends BaseMapper { @Inject private ResourceLinks resourceLinks; - @AfterMapping - protected void appendLinks(User user, @MappingTarget UserDto target) { - Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(target.getName())); + @ObjectFactory + UserDto createDto(User user) { + Links.Builder linksBuilder = linkingTo().self(resourceLinks.user().self(user.getName())); if (UserPermissions.delete(user).isPermitted()) { - linksBuilder.single(link("delete", resourceLinks.user().delete(target.getName()))); + linksBuilder.single(link("delete", resourceLinks.user().delete(user.getName()))); } if (UserPermissions.modify(user).isPermitted()) { - linksBuilder.single(link("update", resourceLinks.user().update(target.getName()))); + linksBuilder.single(link("update", resourceLinks.user().update(user.getName()))); if (userManager.isTypeDefault(user)) { - linksBuilder.single(link("password", resourceLinks.user().passwordChange(target.getName()))); + linksBuilder.single(link("password", resourceLinks.user().passwordChange(user.getName()))); } } if (PermissionPermissions.read().isPermitted()) { - linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(target.getName()))); + linksBuilder.single(link("permissions", resourceLinks.userPermissions().permissions(user.getName()))); } - appendLinks(new EdisonLinkAppender(linksBuilder), user); + Embedded.Builder embeddedBuilder = embeddedBuilder(); + applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), user); - target.add(linksBuilder.build()); + return new UserDto(linksBuilder.build(), embeddedBuilder.build()); } } diff --git a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java index afaa28bfe8..be5a1e7ac2 100644 --- a/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/boot/BootstrapContextListener.java @@ -33,12 +33,9 @@ package sonia.scm.boot; //~--- non-JDK imports -------------------------------------------------------- -import com.github.legman.Subscribe; - import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.io.Files; -import com.google.inject.servlet.GuiceFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java index b77a927a2d..fc52ef4eff 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/MDCFilter.java @@ -42,7 +42,6 @@ import org.slf4j.MDC; import sonia.scm.SCMContext; import sonia.scm.security.DefaultKeyGenerator; -import sonia.scm.security.KeyGenerator; import sonia.scm.web.filter.HttpFilter; //~--- JDK imports ------------------------------------------------------------ diff --git a/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java index 508e804d1f..e7d020ae18 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/PropagatePrincipleFilter.java @@ -51,8 +51,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; - //~--- JDK imports ------------------------------------------------------------ /** diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java index 64e26405a1..5b895a34fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java +++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java @@ -35,7 +35,6 @@ import io.jsonwebtoken.Claims; import java.util.Collections; import java.util.Date; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; diff --git a/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java b/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java index 6b6b06aa9c..1fab500d79 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java +++ b/scm-webapp/src/main/java/sonia/scm/security/RepositoryRole.java @@ -1,7 +1,5 @@ package sonia.scm.security; -import org.apache.commons.collections.CollectionUtils; - import java.util.Collection; import java.util.Collections; import java.util.Objects; @@ -33,8 +31,9 @@ public class RepositoryRole { if (this == o) return true; if (!(o instanceof RepositoryRole)) return false; RepositoryRole that = (RepositoryRole) o; - return name.equals(that.name) && - CollectionUtils.isEqualCollection(this.verbs, that.verbs); + return name.equals(that.name) + && this.verbs.containsAll(that.verbs) + && this.verbs.size() == that.verbs.size(); } @Override diff --git a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java index 77683bd6be..e58945a346 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/filter/HttpProtocolServletAuthenticationFilter.java @@ -17,9 +17,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Set; -import static sonia.scm.util.HttpUtil.AUTHENTICATION_REALM; -import static sonia.scm.util.HttpUtil.HEADER_WWW_AUTHENTICATE; - @Priority(Filters.PRIORITY_AUTHENTICATION) @WebElement(value = HttpProtocolServlet.PATTERN) public class HttpProtocolServletAuthenticationFilter extends AuthenticationFilter { diff --git a/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java index 696174d6e0..fd9745be83 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/rest/resources/AbstractManagerResourceTest.java @@ -21,7 +21,7 @@ import java.util.Comparator; import static java.util.Collections.emptyList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java index b60775a73b..8bdbacafc2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/JsonFiltersTest.java @@ -3,7 +3,6 @@ package sonia.scm.api.v2; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.common.collect.Lists; import com.google.common.io.Resources; import org.junit.Test; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java index 4994c11b08..9216922e19 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchRootResourceTest.java @@ -82,7 +82,7 @@ public class BranchRootResourceTest extends RepositoryTestBase { @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private final Subject subject = mock(Subject.class); private final ThreadState subjectThreadState = new SubjectThreadState(subject); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java index d2e202576a..3e64ab95b6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java @@ -24,12 +24,12 @@ class BranchToBranchDtoMapperTest { @Test void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Branch.class, (ctx, appender) -> { NamespaceAndName namespaceAndName = ctx.oneRequireByType(NamespaceAndName.class); Branch branch = ctx.oneRequireByType(Branch.class); - appender.appendOne("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName()); + appender.appendLink("ka", "http://" + namespaceAndName.logString() + "/" + branch.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java index 69695279e6..7653fbe122 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import org.assertj.core.api.Assertions; import org.junit.Test; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; @@ -17,7 +16,7 @@ public class ChangesetCollectionToDtoMapperTest { public static final Repository REPOSITORY = new Repository("", "git", "space", "name"); public static final Changeset CHANGESET = new Changeset(); - private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper = mock(ChangesetToChangesetDtoMapper.class); + private final DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper = mock(DefaultChangesetToChangesetDtoMapperImpl.class); private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, ResourceLinksMock.createMock(URI.create("/"))); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java index d5c0f91f81..952c8504f6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetRootResourceTest.java @@ -65,7 +65,7 @@ public class ChangesetRootResourceTest extends RepositoryTestBase { private ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private ChangesetRootResource changesetRootResource; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java index 4fb25d2371..66689c6ac6 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ConfigResourceTest.java @@ -18,6 +18,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -68,7 +69,7 @@ public class ConfigResourceTest { @Test @SubjectAware(username = "readOnly") - public void shouldGetGlobalConfig() throws URISyntaxException { + public void shouldGetGlobalConfig() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + ConfigResource.CONFIG_PATH_V2); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java new file mode 100644 index 0000000000..ff149c5dc5 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonHalAppenderTest.java @@ -0,0 +1,61 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.Embedded; +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Link; +import de.otto.edison.hal.Links; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static de.otto.edison.hal.Embedded.embeddedBuilder; +import static de.otto.edison.hal.Links.linkingTo; +import static org.assertj.core.api.Assertions.assertThat; + +class EdisonHalAppenderTest { + + private Links.Builder linksBuilder; + private Embedded.Builder embeddedBuilder; + private EdisonHalAppender appender; + + @BeforeEach + void prepare() { + linksBuilder = linkingTo(); + embeddedBuilder = embeddedBuilder(); + appender = new EdisonHalAppender(linksBuilder, embeddedBuilder); + } + + @Test + void shouldAppendOneLink() { + appender.appendLink("self", "https://scm.hitchhiker.com"); + + Links links = linksBuilder.build(); + assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com"); + } + + @Test + void shouldAppendMultipleLinks() { + appender.linkArrayBuilder("items") + .append("one", "http://one") + .append("two", "http://two") + .build(); + + List items = linksBuilder.build().getLinksBy("items"); + assertThat(items).hasSize(2); + } + + @Test + void shouldAppendEmbedded() { + HalRepresentation one = new HalRepresentation(); + appender.appendEmbedded("one", one); + + HalRepresentation two = new HalRepresentation(); + appender.appendEmbedded("two", new HalRepresentation()); + + Embedded embedded = embeddedBuilder.build(); + assertThat(embedded.getItemsBy("one")).containsOnly(one); + assertThat(embedded.getItemsBy("two")).containsOnly(two); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java deleted file mode 100644 index e97415cc09..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/EdisonLinkAppenderTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package sonia.scm.api.v2.resources; - -import de.otto.edison.hal.Link; -import de.otto.edison.hal.Links; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static de.otto.edison.hal.Links.linkingTo; -import static org.assertj.core.api.Assertions.assertThat; - -class EdisonLinkAppenderTest { - - private Links.Builder builder; - private EdisonLinkAppender appender; - - @BeforeEach - void prepare() { - builder = linkingTo(); - appender = new EdisonLinkAppender(builder); - } - - @Test - void shouldAppendOneLink() { - appender.appendOne("self", "https://scm.hitchhiker.com"); - - Links links = builder.build(); - assertThat(links.getLinkBy("self").get().getHref()).isEqualTo("https://scm.hitchhiker.com"); - } - - @Test - void shouldAppendMultipleLinks() { - appender.arrayBuilder("items") - .append("one", "http://one") - .append("two", "http://two") - .build(); - - List items = builder.build().getLinksBy("items"); - assertThat(items).hasSize(2); - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java index 52c9a434c0..a8b3c15158 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileHistoryResourceTest.java @@ -66,7 +66,7 @@ public class FileHistoryResourceTest extends RepositoryTestBase { private FileHistoryCollectionToDtoMapper fileHistoryCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private FileHistoryRootResource fileHistoryRootResource; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java index b25410210f..55058a1684 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/FileObjectToFileObjectDtoMapperTest.java @@ -73,13 +73,13 @@ public class FileObjectToFileObjectDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(FileObject.class, (ctx, appender) -> { NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class); FileObject fo = ctx.oneRequireByType(FileObject.class); String rev = ctx.oneRequireByType(String.class); - appender.appendOne("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev); + appender.appendLink("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java index 646e9d0839..3e2d0f9663 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupRootResourceTest.java @@ -24,6 +24,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -100,7 +101,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetGroup() throws URISyntaxException { + public void shouldGetGroup() throws URISyntaxException, UnsupportedEncodingException { Group group = createDummyGroup(); when(groupManager.get("admin")).thenReturn(group); @@ -305,7 +306,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetAll() throws URISyntaxException { + public void shouldGetAll() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2); MockHttpResponse response = new MockHttpResponse(); @@ -317,7 +318,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetPermissionLink() throws URISyntaxException { + public void shouldGetPermissionLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2 + "admin"); MockHttpResponse response = new MockHttpResponse(); @@ -329,7 +330,7 @@ public class GroupRootResourceTest { } @Test - public void shouldGetPermissions() throws URISyntaxException { + public void shouldGetPermissions() throws URISyntaxException, UnsupportedEncodingException { when(permissionAssigner.readPermissionsForGroup("admin")).thenReturn(singletonList(new PermissionDescriptor("something:*"))); MockHttpRequest request = MockHttpRequest.get("/" + GroupRootResource.GROUPS_PATH_V2 + "admin/permissions"); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java index b681dff21f..045124ad91 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/GroupToGroupDtoMapperTest.java @@ -11,7 +11,6 @@ import org.mockito.InjectMocks; import sonia.scm.group.Group; import java.net.URI; -import java.net.URISyntaxException; import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; @@ -91,10 +90,10 @@ public class GroupToGroupDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Group.class, (ctx, appender) -> { Group group = ctx.oneRequireByType(Group.class); - appender.appendOne("some", "http://" + group.getName()); + appender.appendLink("some", "http://" + group.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java new file mode 100644 index 0000000000..314dcf11c2 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/HalEnricherAutoRegistrationTest.java @@ -0,0 +1,64 @@ +package sonia.scm.api.v2.resources; + +import com.google.common.collect.ImmutableSet; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +class HalEnricherAutoRegistrationTest { + + @Test + void shouldRegisterAllAvailableLinkEnrichers() { + HalEnricher one = new One(); + HalEnricher two = new Two(); + HalEnricher three = new Three(); + HalEnricher four = new Four(); + Set enrichers = ImmutableSet.of(one, two, three, four); + + HalEnricherRegistry registry = new HalEnricherRegistry(); + + LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers); + autoRegistration.contextInitialized(null); + + assertThat(registry.allByType(String.class)).containsOnly(one, two); + assertThat(registry.allByType(Integer.class)).containsOnly(three); + } + + @Enrich(String.class) + public static class One implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + @Enrich(String.class) + public static class Two implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + @Enrich(Integer.class) + public static class Three implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + + public static class Four implements HalEnricher { + + @Override + public void enrich(HalEnricherContext context, HalAppender appender) { + + } + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java index e4495a0455..b965c2f2c3 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IncomingRootResourceTest.java @@ -74,7 +74,7 @@ public class IncomingRootResourceTest extends RepositoryTestBase { private IncomingChangesetCollectionToDtoMapper incomingChangesetCollectionToDtoMapper; @InjectMocks - private ChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; + private DefaultChangesetToChangesetDtoMapperImpl changesetToChangesetDtoMapper; private IncomingRootResource incomingRootResource; diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java deleted file mode 100644 index a2b72abc49..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/LinkEnricherAutoRegistrationTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package sonia.scm.api.v2.resources; - -import com.google.common.collect.ImmutableSet; -import org.junit.jupiter.api.Test; - -import java.util.Set; - -import static org.assertj.core.api.Java6Assertions.assertThat; - -class LinkEnricherAutoRegistrationTest { - - @Test - void shouldRegisterAllAvailableLinkEnrichers() { - LinkEnricher one = new One(); - LinkEnricher two = new Two(); - LinkEnricher three = new Three(); - LinkEnricher four = new Four(); - Set enrichers = ImmutableSet.of(one, two, three, four); - - LinkEnricherRegistry registry = new LinkEnricherRegistry(); - - LinkEnricherAutoRegistration autoRegistration = new LinkEnricherAutoRegistration(registry, enrichers); - autoRegistration.contextInitialized(null); - - assertThat(registry.allByType(String.class)).containsOnly(one, two); - assertThat(registry.allByType(Integer.class)).containsOnly(three); - } - - @Enrich(String.class) - public static class One implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - @Enrich(String.class) - public static class Two implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - @Enrich(Integer.class) - public static class Three implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - - public static class Four implements LinkEnricher { - - @Override - public void enrich(LinkEnricherContext context, LinkAppender appender) { - - } - } - -} diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java index 138387938b..8a00c69229 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeDtoFactoryTest.java @@ -15,7 +15,6 @@ import org.mockito.quality.Strictness; import sonia.scm.group.GroupNames; import sonia.scm.user.User; import sonia.scm.user.UserManager; -import sonia.scm.user.UserPermissions; import sonia.scm.user.UserTestData; import java.net.URI; @@ -170,12 +169,12 @@ class MeDtoFactoryTest { void shouldAppendLinks() { prepareSubject(UserTestData.createTrillian()); - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); meDtoFactory.setRegistry(registry); registry.register(Me.class, (ctx, appender) -> { User user = ctx.oneRequireByType(User.class); - appender.appendOne("profile", "http://hitchhiker.com/users/" + user.getName()); + appender.appendLink("profile", "http://hitchhiker.com/users/" + user.getName()); }); MeDto dto = meDtoFactory.create(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java index 052a059959..cd2a172c1b 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/MeResourceTest.java @@ -22,12 +22,12 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.mockito.MockitoAnnotations.initMocks; import static sonia.scm.api.v2.resources.DispatcherMock.createDispatcher; @@ -78,7 +78,7 @@ public class MeResourceTest { } @Test - public void shouldReturnCurrentlyAuthenticatedUser() throws URISyntaxException { + public void shouldReturnCurrentlyAuthenticatedUser() throws URISyntaxException, UnsupportedEncodingException { applyUserToSubject(originalUser); MockHttpRequest request = MockHttpRequest.get("/" + MeResource.ME_PATH_V2); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java index 2795562b14..4472acb2c5 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryPermissionRootResourceTest.java @@ -36,6 +36,7 @@ import sonia.scm.repository.RepositoryPermission; import sonia.scm.web.VndMediaType; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -53,7 +54,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.jupiter.api.DynamicTest.dynamicTest; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -214,7 +215,12 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { .expectedResponseStatus(200) .path(PATH_OF_ALL_PERMISSIONS + expectedPermission.getName()) .responseValidator((response) -> { - String body = response.getContentAsString(); + String body = null; + try { + body = response.getContentAsString(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } ObjectMapper mapper = new ObjectMapper(); try { RepositoryPermissionDto actualRepositoryPermissionDto = mapper.readValue(body, RepositoryPermissionDto.class); @@ -268,13 +274,21 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestPOSTPermission .content("{\"name\" : \"" + newPermission.getName() + "\" , \"verbs\" : [\"read\",\"pull\",\"push\"], \"groupPermission\" : true}") .expectedResponseStatus(201) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("POST response has no body") .isBlank()) ); assertGettingExpectedPermissions(expectedPermissions, PERMISSION_WRITE); } + private String getContentAsString(MockHttpResponse response) { + try { + return response.getContentAsString(); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("could not get content from response", e); + } + } + @Test public void shouldNotAddExistingPermission() throws URISyntaxException { createUserWithRepositoryAndPermissions(TEST_PERMISSIONS, PERMISSION_WRITE); @@ -296,7 +310,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { .content("{\"name\" : \"" + modifiedPermission.getName() + "\" , \"verbs\" : [\"*\"], \"groupPermission\" : false}") .path(PATH_OF_ALL_PERMISSIONS + modifiedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("PUT response has no body") .isBlank()) ); @@ -312,7 +326,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -327,7 +341,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -335,7 +349,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestDELETEPermission .path(PATH_OF_ALL_PERMISSIONS + deletedPermission.getName()) .expectedResponseStatus(204) - .responseValidator(response -> assertThat(response.getContentAsString()) + .responseValidator(response -> assertThat(getContentAsString(response)) .as("DELETE response has no body") .isBlank()) ); @@ -346,7 +360,7 @@ public class RepositoryPermissionRootResourceTest extends RepositoryTestBase { assertExpectedRequest(requestGETAllPermissions .expectedResponseStatus(200) .responseValidator((response) -> { - String body = response.getContentAsString(); + String body = getContentAsString(response); ObjectMapper mapper = new ObjectMapper(); try { HalRepresentation halRepresentation = mapper.readValue(body, HalRepresentation.class); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java index bf4366f0b2..1f6ed6b3a7 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryRootResourceTest.java @@ -27,6 +27,7 @@ import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -41,9 +42,9 @@ import static javax.servlet.http.HttpServletResponse.SC_PRECONDITION_FAILED; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -120,7 +121,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldFindExistingRepository() throws URISyntaxException { + public void shouldFindExistingRepository() throws URISyntaxException, UnsupportedEncodingException { mockRepository("space", "repo"); MockHttpRequest request = MockHttpRequest.get("/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + "space/repo"); @@ -133,7 +134,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldMapProperties() throws URISyntaxException { + public void shouldMapProperties() throws URISyntaxException, UnsupportedEncodingException { Repository repository = mockRepository("space", "repo"); repository.setProperty("testKey", "testValue"); @@ -146,7 +147,7 @@ public class RepositoryRootResourceTest extends RepositoryTestBase { } @Test - public void shouldGetAll() throws URISyntaxException { + public void shouldGetAll() throws URISyntaxException, UnsupportedEncodingException { PageResult singletonPageResult = createSingletonPageResult(mockRepository("space", "repo")); when(repositoryManager.getPage(any(), eq(0), eq(10))).thenReturn(singletonPageResult); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java index 8469e966c8..4b02508ae8 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/RepositoryToRepositoryDtoMapperTest.java @@ -23,7 +23,7 @@ import static java.util.stream.Stream.of; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; @@ -211,10 +211,10 @@ public class RepositoryToRepositoryDtoMapperTest { @Test public void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Repository.class, (ctx, appender) -> { Repository repository = ctx.oneRequireByType(Repository.class); - appender.appendOne("id", "http://" + repository.getId()); + appender.appendLink("id", "http://" + repository.getId()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java index aa8eb3e7ab..cd3c18de27 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/TagToTagDtoMapperTest.java @@ -22,11 +22,11 @@ class TagToTagDtoMapperTest { @Test void shouldAppendLinks() { - LinkEnricherRegistry registry = new LinkEnricherRegistry(); + HalEnricherRegistry registry = new HalEnricherRegistry(); registry.register(Tag.class, (ctx, appender) -> { NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class); Tag tag = ctx.oneRequireByType(Tag.class); - appender.appendOne("yo", "http://" + repository.logString() + "/" + tag.getName()); + appender.appendLink("yo", "http://" + repository.logString() + "/" + tag.getName()); }); mapper.setRegistry(registry); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java index 99a1435923..b2dafc8cfe 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UIRootResourceTest.java @@ -16,6 +16,7 @@ import sonia.scm.plugin.*; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.HashSet; @@ -87,7 +88,7 @@ public class UIRootResourceTest { } @Test - public void shouldReturnPlugin() throws URISyntaxException { + public void shouldReturnPlugin() throws URISyntaxException, UnsupportedEncodingException { mockPlugins(mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js"))); MockHttpRequest request = MockHttpRequest.get("/v2/ui/plugins/awesome"); @@ -101,7 +102,7 @@ public class UIRootResourceTest { } @Test - public void shouldReturnPlugins() throws URISyntaxException { + public void shouldReturnPlugins() throws URISyntaxException, UnsupportedEncodingException { mockPlugins( mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js")), mockPlugin("special", "Special", createPluginResources("my/special.bundle.js")) @@ -120,7 +121,7 @@ public class UIRootResourceTest { } @Test - public void shouldNotReturnPluginsWithoutResources() throws URISyntaxException { + public void shouldNotReturnPluginsWithoutResources() throws URISyntaxException, UnsupportedEncodingException { mockPlugins( mockPlugin("awesome", "Awesome", createPluginResources("my/awesome.bundle.js")), mockPlugin("special") diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java index 19f247b3b2..552009b73f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserDtoToUserMapperTest.java @@ -10,7 +10,6 @@ import sonia.scm.user.User; import java.time.Instant; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; public class UserDtoToUserMapperTest { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java index 88142e4d50..4047dfadd2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserRootResourceTest.java @@ -14,7 +14,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; import sonia.scm.ContextEntry; import sonia.scm.NotFoundException; import sonia.scm.PageResult; @@ -26,6 +25,7 @@ import sonia.scm.user.UserManager; import sonia.scm.web.VndMediaType; import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; @@ -35,8 +35,8 @@ import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; @@ -76,7 +76,7 @@ public class UserRootResourceTest { private User originalUser; @Before - public void prepareEnvironment() throws Exception { + public void prepareEnvironment() { initMocks(this); originalUser = createDummyUser("Neo"); when(userManager.create(userCaptor.capture())).thenAnswer(invocation -> invocation.getArguments()[0]); @@ -97,7 +97,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreateFullResponseForAdmin() throws URISyntaxException { + public void shouldCreateFullResponseForAdmin() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -137,7 +137,7 @@ public class UserRootResourceTest { @Test @SubjectAware(username = "unpriv") - public void shouldCreateLimitedResponseForSimpleUser() throws URISyntaxException { + public void shouldCreateLimitedResponseForSimpleUser() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -331,7 +331,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreatePageForOnePageOnly() throws URISyntaxException { + public void shouldCreatePageForOnePageOnly() throws URISyntaxException, UnsupportedEncodingException { PageResult singletonPageResult = createSingletonPageResult(1); when(userManager.getPage(any(), eq(0), eq(10))).thenReturn(singletonPageResult); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2); @@ -347,7 +347,7 @@ public class UserRootResourceTest { } @Test - public void shouldCreatePageForMultiplePages() throws URISyntaxException { + public void shouldCreatePageForMultiplePages() throws URISyntaxException, UnsupportedEncodingException { PageResult singletonPageResult = createSingletonPageResult(3); when(userManager.getPage(any(), eq(1), eq(1))).thenReturn(singletonPageResult); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "?page=1&pageSize=1"); @@ -365,7 +365,7 @@ public class UserRootResourceTest { } @Test - public void shouldGetPermissionLink() throws URISyntaxException { + public void shouldGetPermissionLink() throws URISyntaxException, UnsupportedEncodingException { MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo"); MockHttpResponse response = new MockHttpResponse(); @@ -377,7 +377,7 @@ public class UserRootResourceTest { } @Test - public void shouldGetPermissions() throws URISyntaxException { + public void shouldGetPermissions() throws URISyntaxException, UnsupportedEncodingException { when(permissionAssigner.readPermissionsForUser("Neo")).thenReturn(singletonList(new PermissionDescriptor("something:*"))); MockHttpRequest request = MockHttpRequest.get("/" + UserRootResource.USERS_PATH_V2 + "Neo/permissions"); MockHttpResponse response = new MockHttpResponse(); diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java index 9924dae81b..ae1d75dddf 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/UserToUserDtoMapperTest.java @@ -155,8 +155,8 @@ public class UserToUserDtoMapperTest { public void shouldAppendLink() { User trillian = UserTestData.createTrillian(); - LinkEnricherRegistry registry = new LinkEnricherRegistry(); - registry.register(User.class, (ctx, appender) -> appender.appendOne("sample", "http://" + ctx.oneByType(User.class).get().getName())); + HalEnricherRegistry registry = new HalEnricherRegistry(); + registry.register(User.class, (ctx, appender) -> appender.appendLink("sample", "http://" + ctx.oneByType(User.class).get().getName())); mapper.setRegistry(registry); UserDto userDto = mapper.map(trillian); diff --git a/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java b/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java index b8b538c82b..eac4a12340 100644 --- a/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java @@ -2,7 +2,6 @@ package sonia.scm.boot; import com.github.legman.Subscribe; import com.google.common.base.Charsets; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; diff --git a/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java b/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java index a26cf3b215..c9d8c594b4 100644 --- a/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/boot/ServletContextCleanerTest.java @@ -13,7 +13,6 @@ import java.util.Enumeration; import java.util.Set; import java.util.Vector; -import static org.junit.Assert.*; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java index 8cdb162740..a367d171a1 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/GitLfsITCase.java @@ -136,10 +136,13 @@ public class GitLfsITCase { } private void createUser(User user) { - UserDto dto = new UserToUserDtoMapperImpl(){ - @Override - protected void appendLinks(User user, UserDto target) {} - }.map(user); + UserDto dto = new UserDto(); + dto.setName(user.getName()); + dto.setMail(user.getMail()); + dto.setDisplayName(user.getDisplayName()); + dto.setType(user.getType()); + dto.setActive(user.isActive()); + dto.setAdmin(user.isAdmin()); dto.setPassword(user.getPassword()); createResource(adminClient, "users") .accept("*/*") diff --git a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java index a60c884b64..399f20cd3f 100644 --- a/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java +++ b/scm-webapp/src/test/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponseTest.java @@ -41,6 +41,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.io.ByteSource; +import org.hamcrest.Matchers; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,8 +50,6 @@ import org.mockito.junit.MockitoJUnitRunner; import sonia.scm.config.ScmConfiguration; -import static org.hamcrest.Matchers.*; - import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -136,7 +135,7 @@ public class DefaultAdvancedHttpResponseTest connection, 200, "OK"); Multimap headers = response.getHeaders(); - assertThat(headers.get("Test"), contains("One", "Two")); + assertThat(headers.get("Test"), Matchers.contains("One", "Two")); assertTrue(headers.get("Test-2").isEmpty()); } @@ -144,8 +143,7 @@ public class DefaultAdvancedHttpResponseTest /** Field description */ private final DefaultAdvancedHttpClient client = - new DefaultAdvancedHttpClient(new ScmConfiguration(), - new HashSet(), new SSLContextProvider()); + new DefaultAdvancedHttpClient(new ScmConfiguration(), new HashSet<>(), new SSLContextProvider()); /** Field description */ @Mock diff --git a/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java b/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java index ae65f5c1ae..df31977de1 100644 --- a/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/plugin/MultiParentClassLoaderTest.java @@ -29,9 +29,6 @@ package sonia.scm.plugin; -import com.google.common.base.Enums; -import com.google.common.collect.Iterables; -import com.google.common.collect.Iterators; import java.io.IOException; import java.net.URL; import java.util.Arrays; diff --git a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java index efaeb702fe..baf4c659cc 100644 --- a/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java +++ b/scm-webapp/src/test/java/sonia/scm/schedule/QuartzTaskTest.java @@ -32,12 +32,11 @@ package sonia.scm.schedule; import org.junit.Test; -import static org.junit.Assert.*; + import static org.mockito.Mockito.*; -import static org.hamcrest.Matchers.*; + import org.junit.Before; import org.junit.runner.RunWith; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import org.quartz.JobKey; diff --git a/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java index 5c7aa08f37..c2d75358fd 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/BearerRealmTest.java @@ -33,18 +33,13 @@ package sonia.scm.security; import com.google.common.collect.ImmutableSet; import org.apache.shiro.authc.AuthenticationInfo; -import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; -import org.junit.Ignore; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.jupiter.MockitoExtension; -import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Set; @@ -52,7 +47,6 @@ import java.util.Set; 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.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java b/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java index cce3fea2b1..f59991f2cc 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/SecureKeyResolverTest.java @@ -47,7 +47,6 @@ import sonia.scm.store.ConfigurationEntryStoreFactory; import java.util.Random; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.in; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; diff --git a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java index 8e261b75cc..ab31d751fd 100644 --- a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java @@ -45,9 +45,6 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import sonia.scm.NotFoundException; -import sonia.scm.repository.InitialRepositoryLocationResolver; -import sonia.scm.repository.RepositoryDAO; -import sonia.scm.repository.RepositoryLocationResolver; import sonia.scm.store.JAXBConfigurationStoreFactory; import sonia.scm.user.xml.XmlUserDAO; diff --git a/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java b/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java index 29c7dea358..5f95a171d2 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/cgi/DefaultCGIExecutorTest.java @@ -3,7 +3,7 @@ package sonia.scm.web.cgi; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.MockitoJUnitRunner; import javax.servlet.http.HttpServletRequest; diff --git a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java index e028857e2c..bb3c7b5f1e 100644 --- a/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java +++ b/scm-webapp/src/test/java/sonia/scm/web/i18n/I18nServletTest.java @@ -35,8 +35,6 @@ import java.util.Enumeration; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.Silent.class)