mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 23:45:44 +01:00
merge
This commit is contained in:
5
Jenkinsfile
vendored
5
Jenkinsfile
vendored
@@ -50,6 +50,11 @@ node('docker') {
|
|||||||
def dockerImageTag = "2.0.0-dev-${commitHash.substring(0,7)}-${BUILD_NUMBER}"
|
def dockerImageTag = "2.0.0-dev-${commitHash.substring(0,7)}-${BUILD_NUMBER}"
|
||||||
|
|
||||||
if (isMainBranch()) {
|
if (isMainBranch()) {
|
||||||
|
|
||||||
|
stage('Lifecycle') {
|
||||||
|
nexusPolicyEvaluation iqApplication: selectedApplication('scm'), iqScanPatterns: [[scanPattern: 'scm-server/target/scm-server-app.zip']], iqStage: 'build'
|
||||||
|
}
|
||||||
|
|
||||||
stage('Archive') {
|
stage('Archive') {
|
||||||
archiveArtifacts 'scm-webapp/target/scm-webapp.war'
|
archiveArtifacts 'scm-webapp/target/scm-webapp.war'
|
||||||
archiveArtifacts 'scm-server/target/scm-server-app.*'
|
archiveArtifacts 'scm-server/target/scm-server-app.*'
|
||||||
|
|||||||
21
pom.xml
21
pom.xml
@@ -351,21 +351,6 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
<!-- utils -->
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-beanutils</groupId>
|
|
||||||
<artifactId>commons-beanutils</artifactId>
|
|
||||||
<version>1.9.3</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>commons-collections</groupId>
|
|
||||||
<artifactId>commons-collections</artifactId>
|
|
||||||
<version>3.2.2</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- http -->
|
<!-- http -->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -825,11 +810,11 @@
|
|||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
<servlet.version>3.0.1</servlet.version>
|
<servlet.version>3.0.1</servlet.version>
|
||||||
|
|
||||||
<jaxrs.version>2.0.1</jaxrs.version>
|
<jaxrs.version>2.1.1</jaxrs.version>
|
||||||
<resteasy.version>3.1.3.Final</resteasy.version>
|
<resteasy.version>3.6.2.Final</resteasy.version>
|
||||||
<jersey-client.version>1.19.4</jersey-client.version>
|
<jersey-client.version>1.19.4</jersey-client.version>
|
||||||
<enunciate.version>2.11.1</enunciate.version>
|
<enunciate.version>2.11.1</enunciate.version>
|
||||||
<jackson.version>2.8.6</jackson.version>
|
<jackson.version>2.9.8</jackson.version>
|
||||||
<guice.version>4.0</guice.version>
|
<guice.version>4.0</guice.version>
|
||||||
<jaxb.version>2.3.0</jaxb.version>
|
<jaxb.version>2.3.0</jaxb.version>
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.ws.rs</groupId>
|
<groupId>javax.ws.rs</groupId>
|
||||||
<artifactId>javax.ws.rs-api</artifactId>
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -235,7 +236,6 @@
|
|||||||
<links>
|
<links>
|
||||||
<link>http://download.oracle.com/javase/6/docs/api/</link>
|
<link>http://download.oracle.com/javase/6/docs/api/</link>
|
||||||
<link>http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
|
<link>http://download.oracle.com/docs/cd/E17802_01/products/products/servlet/2.5/docs/servlet-2_5-mr2/</link>
|
||||||
<link>http://jersey.java.net/nonav/apidocs/${jersey.version}/jersey/</link>
|
|
||||||
<link>https://google.github.io/guice/api-docs/${guice.version}/javadoc</link>
|
<link>https://google.github.io/guice/api-docs/${guice.version}/javadoc</link>
|
||||||
<link>http://www.slf4j.org/api/</link>
|
<link>http://www.slf4j.org/api/</link>
|
||||||
<link>http://shiro.apache.org/static/${shiro.version}/apidocs/</link>
|
<link>http://shiro.apache.org/static/${shiro.version}/apidocs/</link>
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ package sonia.scm;
|
|||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base class of all handlers.
|
* The base class of all handlers.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
import de.otto.edison.hal.HalRepresentation;
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
|
|
||||||
public abstract class BaseMapper<T, D extends HalRepresentation> extends LinkAppenderMapper implements InstantAttributeMapper {
|
public abstract class BaseMapper<T, D extends HalRepresentation> extends HalAppenderMapper implements InstantAttributeMapper {
|
||||||
|
|
||||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||||
public abstract D map(T modelObject);
|
public abstract D map(T modelObject);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Embedded;
|
||||||
import de.otto.edison.hal.HalRepresentation;
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
import de.otto.edison.hal.Links;
|
import de.otto.edison.hal.Links;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -7,7 +8,6 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@@ -34,16 +34,7 @@ public class ChangesetDto extends HalRepresentation {
|
|||||||
*/
|
*/
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
@Override
|
public ChangesetDto(Links links, Embedded embedded) {
|
||||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
super(links, embedded);
|
||||||
protected HalRepresentation add(Links links) {
|
|
||||||
return super.add(links);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
|
||||||
protected HalRepresentation withEmbedded(String rel, List<? extends HalRepresentation> halRepresentations) {
|
|
||||||
return super.withEmbedded(rel, halRepresentations);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
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
|
* @author Sebastian Sdorra
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public interface LinkAppender {
|
public interface HalAppender {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends one link to the json response.
|
* Appends one link to the json response.
|
||||||
@@ -14,7 +16,7 @@ public interface LinkAppender {
|
|||||||
* @param rel name of relation
|
* @param rel name of relation
|
||||||
* @param href link uri
|
* @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.
|
* 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
|
* @param rel name of link relation
|
||||||
* @return multi link builder
|
* @return multi link builder
|
||||||
*/
|
*/
|
||||||
LinkArrayBuilder arrayBuilder(String rel);
|
LinkArrayBuilder linkArrayBuilder(String rel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends one embedded to the json response.
|
||||||
|
*
|
||||||
|
* @param rel name of relation
|
||||||
|
* @param embeddedItem embedded object
|
||||||
|
*/
|
||||||
|
void appendEmbedded(String rel, HalRepresentation embeddedItem);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for link arrays.
|
* Builder for link arrays.
|
||||||
@@ -4,17 +4,17 @@ import com.google.common.annotations.VisibleForTesting;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class LinkAppenderMapper {
|
public class HalAppenderMapper {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private LinkEnricherRegistry registry;
|
private HalEnricherRegistry registry;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setRegistry(LinkEnricherRegistry registry) {
|
void setRegistry(HalEnricherRegistry registry) {
|
||||||
this.registry = 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
|
// null check is only their to not break existing tests
|
||||||
if (registry != null) {
|
if (registry != null) {
|
||||||
|
|
||||||
@@ -24,10 +24,10 @@ public class LinkAppenderMapper {
|
|||||||
ctx[i + 1] = contextEntries[i];
|
ctx[i + 1] = contextEntries[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkEnricherContext context = LinkEnricherContext.of(ctx);
|
HalEnricherContext context = HalEnricherContext.of(ctx);
|
||||||
|
|
||||||
Iterable<LinkEnricher> enrichers = registry.allByType(source.getClass());
|
Iterable<HalEnricher> enrichers = registry.allByType(source.getClass());
|
||||||
for (LinkEnricher enricher : enrichers) {
|
for (HalEnricher enricher : enrichers) {
|
||||||
enricher.enrich(context, appender);
|
enricher.enrich(context, appender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,8 @@ package sonia.scm.api.v2.resources;
|
|||||||
import sonia.scm.plugin.ExtensionPoint;
|
import sonia.scm.plugin.ExtensionPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link LinkEnricher} can be used to append hateoas links to a specific json response.
|
* 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 LinkEnricherRegistry} which is available
|
* To register an enricher use the {@link Enrich} annotation or the {@link HalEnricherRegistry} which is available
|
||||||
* via injection.
|
* via injection.
|
||||||
*
|
*
|
||||||
* <b>Warning:</b> enrichers are always registered as singletons.
|
* <b>Warning:</b> enrichers are always registered as singletons.
|
||||||
@@ -14,13 +14,13 @@ import sonia.scm.plugin.ExtensionPoint;
|
|||||||
*/
|
*/
|
||||||
@ExtensionPoint
|
@ExtensionPoint
|
||||||
@FunctionalInterface
|
@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 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);
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,17 @@ import java.util.NoSuchElementException;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context object for the {@link LinkEnricher}. The context holds the source object for the json and all related
|
* 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 link creation.
|
* objects, which can be useful for the enrichment.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public final class LinkEnricherContext {
|
public final class HalEnricherContext {
|
||||||
|
|
||||||
private final Map<Class, Object> instanceMap;
|
private final Map<Class, Object> instanceMap;
|
||||||
|
|
||||||
private LinkEnricherContext(Map<Class,Object> instanceMap) {
|
private HalEnricherContext(Map<Class,Object> instanceMap) {
|
||||||
this.instanceMap = instanceMap;
|
this.instanceMap = instanceMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,12 +28,12 @@ public final class LinkEnricherContext {
|
|||||||
*
|
*
|
||||||
* @return context of given entries
|
* @return context of given entries
|
||||||
*/
|
*/
|
||||||
public static LinkEnricherContext of(Object... instances) {
|
public static HalEnricherContext of(Object... instances) {
|
||||||
ImmutableMap.Builder<Class, Object> builder = ImmutableMap.builder();
|
ImmutableMap.Builder<Class, Object> builder = ImmutableMap.builder();
|
||||||
for (Object instance : instances) {
|
for (Object instance : instances) {
|
||||||
builder.put(instance.getClass(), instance);
|
builder.put(instance.getClass(), instance);
|
||||||
}
|
}
|
||||||
return new LinkEnricherContext(builder.build());
|
return new HalEnricherContext(builder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,34 +7,34 @@ import sonia.scm.plugin.Extension;
|
|||||||
import javax.inject.Singleton;
|
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
|
* @author Sebastian Sdorra
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
@Extension
|
@Extension
|
||||||
@Singleton
|
@Singleton
|
||||||
public final class LinkEnricherRegistry {
|
public final class HalEnricherRegistry {
|
||||||
|
|
||||||
private final Multimap<Class, LinkEnricher> enrichers = HashMultimap.create();
|
private final Multimap<Class, HalEnricher> 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 sourceType type of json mapping source
|
||||||
* @param enricher link enricher instance
|
* @param enricher link enricher instance
|
||||||
*/
|
*/
|
||||||
public void register(Class sourceType, LinkEnricher enricher) {
|
public void register(Class sourceType, HalEnricher enricher) {
|
||||||
enrichers.put(sourceType, 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
|
* @param sourceType type of json mapping source
|
||||||
* @return all registered enrichers
|
* @return all registered enrichers
|
||||||
*/
|
*/
|
||||||
public Iterable<LinkEnricher> allByType(Class sourceType) {
|
public Iterable<HalEnricher> allByType(Class sourceType) {
|
||||||
return enrichers.get(sourceType);
|
return enrichers.get(sourceType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
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
|
* @author Sebastian Sdorra
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
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
|
* @author Sebastian Sdorra
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
|
|||||||
@@ -1,24 +1,59 @@
|
|||||||
package sonia.scm.filter;
|
package sonia.scm.filter;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import org.slf4j.Logger;
|
||||||
import sonia.scm.util.WebUtil;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
import javax.inject.Inject;
|
||||||
import javax.ws.rs.container.ContainerResponseContext;
|
import javax.inject.Provider;
|
||||||
import javax.ws.rs.container.ContainerResponseFilter;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.ws.rs.ext.Provider;
|
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.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
@Provider
|
@javax.ws.rs.ext.Provider
|
||||||
@Slf4j
|
public class GZipResponseFilter implements WriterInterceptor {
|
||||||
public class GZipResponseFilter implements ContainerResponseFilter {
|
|
||||||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
|
private static final Logger LOG = LoggerFactory.getLogger(GZipResponseFilter.class);
|
||||||
if (WebUtil.isGzipSupported(requestContext::getHeaderString)) {
|
|
||||||
log.trace("compress output with gzip");
|
private final Provider<HttpServletRequest> requestProvider;
|
||||||
GZIPOutputStream wrappedResponse = new GZIPOutputStream(responseContext.getEntityStream());
|
|
||||||
responseContext.getHeaders().add("Content-Encoding", "gzip");
|
@Inject
|
||||||
responseContext.setEntityStream(wrappedResponse);
|
public GZipResponseFilter(Provider<HttpServletRequest> 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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ package sonia.scm.net.ahc;
|
|||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.HashMultimap;
|
|
||||||
import com.google.common.collect.LinkedHashMultimap;
|
import com.google.common.collect.LinkedHashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ package sonia.scm.plugin;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.inject.Module;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import sonia.scm.ConfigurationException;
|
import sonia.scm.ConfigurationException;
|
||||||
import sonia.scm.io.CommandResult;
|
import sonia.scm.io.CommandResult;
|
||||||
import sonia.scm.io.ExtendedCommand;
|
import sonia.scm.io.ExtendedCommand;
|
||||||
|
import sonia.scm.plugin.PluginLoader;
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -67,11 +68,14 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
LoggerFactory.getLogger(AbstractSimpleRepositoryHandler.class);
|
LoggerFactory.getLogger(AbstractSimpleRepositoryHandler.class);
|
||||||
|
|
||||||
private final RepositoryLocationResolver repositoryLocationResolver;
|
private final RepositoryLocationResolver repositoryLocationResolver;
|
||||||
|
private final PluginLoader pluginLoader;
|
||||||
|
|
||||||
public AbstractSimpleRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
public AbstractSimpleRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||||
RepositoryLocationResolver repositoryLocationResolver) {
|
RepositoryLocationResolver repositoryLocationResolver,
|
||||||
|
PluginLoader pluginLoader) {
|
||||||
super(storeFactory);
|
super(storeFactory);
|
||||||
this.repositoryLocationResolver = repositoryLocationResolver;
|
this.repositoryLocationResolver = repositoryLocationResolver;
|
||||||
|
this.pluginLoader = pluginLoader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -155,7 +159,7 @@ public abstract class AbstractSimpleRepositoryHandler<C extends RepositoryConfig
|
|||||||
String content = defaultContent;
|
String content = defaultContent;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URL url = Resources.getResource(resource);
|
URL url = pluginLoader.getUberClassLoader().getResource(resource);
|
||||||
|
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
content = Resources.toString(url, Charsets.UTF_8);
|
content = Resources.toString(url, Charsets.UTF_8);
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ package sonia.scm.repository;
|
|||||||
|
|
||||||
import com.google.common.base.MoreObjects;
|
import com.google.common.base.MoreObjects;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
|
||||||
import sonia.scm.security.PermissionObject;
|
import sonia.scm.security.PermissionObject;
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
@@ -47,9 +46,10 @@ import javax.xml.bind.annotation.XmlRootElement;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static java.util.Collections.emptyList;
|
import static java.util.Collections.emptyList;
|
||||||
import static java.util.Collections.unmodifiableCollection;
|
import static java.util.Collections.unmodifiableSet;
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class RepositoryPermission implements PermissionObject, Serializable
|
|||||||
private boolean groupPermission = false;
|
private boolean groupPermission = false;
|
||||||
private String name;
|
private String name;
|
||||||
@XmlElement(name = "verb")
|
@XmlElement(name = "verb")
|
||||||
private Collection<String> verbs;
|
private Set<String> verbs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link RepositoryPermission}.
|
* Constructs a new {@link RepositoryPermission}.
|
||||||
@@ -79,7 +79,7 @@ public class RepositoryPermission implements PermissionObject, Serializable
|
|||||||
public RepositoryPermission(String name, Collection<String> verbs, boolean groupPermission)
|
public RepositoryPermission(String name, Collection<String> verbs, boolean groupPermission)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.verbs = unmodifiableCollection(new LinkedHashSet<>(verbs));
|
this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs));
|
||||||
this.groupPermission = groupPermission;
|
this.groupPermission = groupPermission;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,8 @@ public class RepositoryPermission implements PermissionObject, Serializable
|
|||||||
final RepositoryPermission other = (RepositoryPermission) obj;
|
final RepositoryPermission other = (RepositoryPermission) obj;
|
||||||
|
|
||||||
return Objects.equal(name, other.name)
|
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);
|
&& Objects.equal(groupPermission, other.groupPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,6 +210,6 @@ public class RepositoryPermission implements PermissionObject, Serializable
|
|||||||
*/
|
*/
|
||||||
public void setVerbs(Collection<String> verbs)
|
public void setVerbs(Collection<String> verbs)
|
||||||
{
|
{
|
||||||
this.verbs = verbs;
|
this.verbs = unmodifiableSet(new LinkedHashSet<>(verbs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.google.common.base.Objects;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import sonia.scm.Validateable;
|
import sonia.scm.Validateable;
|
||||||
import sonia.scm.repository.Person;
|
import sonia.scm.repository.Person;
|
||||||
import sonia.scm.util.Util;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import com.google.common.base.Objects;
|
|||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlElement;
|
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ package sonia.scm.security;
|
|||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
|
|
||||||
import javax.xml.bind.annotation.XmlAccessType;
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
import javax.xml.bind.annotation.XmlRootElement;
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|||||||
88
scm-core/src/main/java/sonia/scm/util/Comparables.java
Normal file
88
scm-core/src/main/java/sonia/scm/util/Comparables.java
Normal file
@@ -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<Class, BeanInfo> beanInfoCacheLoader = new CacheLoader<Class, BeanInfo>() {
|
||||||
|
@Override
|
||||||
|
public BeanInfo load(Class type) throws IntrospectionException {
|
||||||
|
return Introspector.getBeanInfo(type);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final LoadingCache<Class, BeanInfo> beanInfoCache = CacheBuilder.newBuilder()
|
||||||
|
.maximumSize(50) // limit the cache to avoid consuming to much memory on miss usage
|
||||||
|
.build(beanInfoCacheLoader);
|
||||||
|
|
||||||
|
private Comparables() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Comparator<T> comparator(Class<T> 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<PropertyDescriptor> sortByPropertyDescriptor = Arrays.stream(propertyDescriptors)
|
||||||
|
.filter(p -> p.getName().equals(sortBy))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
return sortByPropertyDescriptor.orElseThrow(() -> new IllegalArgumentException("could not find property " + sortBy));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> BeanInfo createBeanInfo(Class<T> type) {
|
||||||
|
return beanInfoCache.getUnchecked(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class MethodComparator<T> implements Comparator<T> {
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,51 +11,51 @@ import java.util.Optional;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class LinkAppenderMapperTest {
|
class HalAppenderMapperTest {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private LinkAppender appender;
|
private HalAppender appender;
|
||||||
|
|
||||||
private LinkEnricherRegistry registry;
|
private HalEnricherRegistry registry;
|
||||||
private LinkAppenderMapper mapper;
|
private HalAppenderMapper mapper;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void beforeEach() {
|
void beforeEach() {
|
||||||
registry = new LinkEnricherRegistry();
|
registry = new HalEnricherRegistry();
|
||||||
mapper = new LinkAppenderMapper();
|
mapper = new HalAppenderMapper();
|
||||||
mapper.setRegistry(registry);
|
mapper.setRegistry(registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldAppendSimpleLink() {
|
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
|
@Test
|
||||||
void shouldCallMultipleEnrichers() {
|
void shouldCallMultipleEnrichers() {
|
||||||
registry.register(String.class, (ctx, appender) -> appender.appendOne("42", "https://hitchhiker.com"));
|
registry.register(String.class, (ctx, appender) -> appender.appendLink("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("21", "https://scm.hitchhiker.com"));
|
||||||
|
|
||||||
mapper.appendLinks(appender, "hello");
|
mapper.applyEnrichers(appender, "hello");
|
||||||
|
|
||||||
verify(appender).appendOne("42", "https://hitchhiker.com");
|
verify(appender).appendLink("42", "https://hitchhiker.com");
|
||||||
verify(appender).appendOne("21", "https://scm.hitchhiker.com");
|
verify(appender).appendLink("21", "https://scm.hitchhiker.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldAppendLinkByUsingSourceFromContext() {
|
void shouldAppendLinkByUsingSourceFromContext() {
|
||||||
registry.register(String.class, (ctx, appender) -> {
|
registry.register(String.class, (ctx, appender) -> {
|
||||||
Optional<String> rel = ctx.oneByType(String.class);
|
Optional<String> 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
|
@Test
|
||||||
@@ -63,12 +63,12 @@ class LinkAppenderMapperTest {
|
|||||||
registry.register(Integer.class, (ctx, appender) -> {
|
registry.register(Integer.class, (ctx, appender) -> {
|
||||||
Optional<Integer> rel = ctx.oneByType(Integer.class);
|
Optional<Integer> rel = ctx.oneByType(Integer.class);
|
||||||
Optional<String> href = ctx.oneByType(String.class);
|
Optional<String> 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,17 +7,17 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
class LinkEnricherContextTest {
|
class HalEnricherContextTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCreateContextFromSingleObject() {
|
void shouldCreateContextFromSingleObject() {
|
||||||
LinkEnricherContext context = LinkEnricherContext.of("hello");
|
HalEnricherContext context = HalEnricherContext.of("hello");
|
||||||
assertThat(context.oneByType(String.class)).contains("hello");
|
assertThat(context.oneByType(String.class)).contains("hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldCreateContextFromMultipleObjects() {
|
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(String.class)).contains("hello");
|
||||||
assertThat(context.oneByType(Integer.class)).contains(42);
|
assertThat(context.oneByType(Integer.class)).contains(42);
|
||||||
assertThat(context.oneByType(Long.class)).contains(21L);
|
assertThat(context.oneByType(Long.class)).contains(21L);
|
||||||
@@ -25,19 +25,19 @@ class LinkEnricherContextTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnEmptyOptionalForUnknownTypes() {
|
void shouldReturnEmptyOptionalForUnknownTypes() {
|
||||||
LinkEnricherContext context = LinkEnricherContext.of();
|
HalEnricherContext context = HalEnricherContext.of();
|
||||||
assertThat(context.oneByType(String.class)).isNotPresent();
|
assertThat(context.oneByType(String.class)).isNotPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldReturnRequiredObject() {
|
void shouldReturnRequiredObject() {
|
||||||
LinkEnricherContext context = LinkEnricherContext.of("hello");
|
HalEnricherContext context = HalEnricherContext.of("hello");
|
||||||
assertThat(context.oneRequireByType(String.class)).isEqualTo("hello");
|
assertThat(context.oneRequireByType(String.class)).isEqualTo("hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldThrowAnNoSuchElementExceptionForUnknownTypes() {
|
void shouldThrowAnNoSuchElementExceptionForUnknownTypes() {
|
||||||
LinkEnricherContext context = LinkEnricherContext.of();
|
HalEnricherContext context = HalEnricherContext.of();
|
||||||
assertThrows(NoSuchElementException.class, () -> context.oneRequireByType(String.class));
|
assertThrows(NoSuchElementException.class, () -> context.oneRequireByType(String.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5,54 +5,54 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class LinkEnricherRegistryTest {
|
class HalEnricherRegistryTest {
|
||||||
|
|
||||||
private LinkEnricherRegistry registry;
|
private HalEnricherRegistry registry;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUpObjectUnderTest() {
|
void setUpObjectUnderTest() {
|
||||||
registry = new LinkEnricherRegistry();
|
registry = new HalEnricherRegistry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRegisterTheEnricher() {
|
void shouldRegisterTheEnricher() {
|
||||||
SampleLinkEnricher enricher = new SampleLinkEnricher();
|
SampleHalEnricher enricher = new SampleHalEnricher();
|
||||||
registry.register(String.class, enricher);
|
registry.register(String.class, enricher);
|
||||||
|
|
||||||
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
|
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
|
||||||
assertThat(enrichers).containsOnly(enricher);
|
assertThat(enrichers).containsOnly(enricher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRegisterMultipleEnrichers() {
|
void shouldRegisterMultipleEnrichers() {
|
||||||
SampleLinkEnricher one = new SampleLinkEnricher();
|
SampleHalEnricher one = new SampleHalEnricher();
|
||||||
registry.register(String.class, one);
|
registry.register(String.class, one);
|
||||||
|
|
||||||
SampleLinkEnricher two = new SampleLinkEnricher();
|
SampleHalEnricher two = new SampleHalEnricher();
|
||||||
registry.register(String.class, two);
|
registry.register(String.class, two);
|
||||||
|
|
||||||
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
|
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
|
||||||
assertThat(enrichers).containsOnly(one, two);
|
assertThat(enrichers).containsOnly(one, two);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldRegisterEnrichersForDifferentTypes() {
|
void shouldRegisterEnrichersForDifferentTypes() {
|
||||||
SampleLinkEnricher one = new SampleLinkEnricher();
|
SampleHalEnricher one = new SampleHalEnricher();
|
||||||
registry.register(String.class, one);
|
registry.register(String.class, one);
|
||||||
|
|
||||||
SampleLinkEnricher two = new SampleLinkEnricher();
|
SampleHalEnricher two = new SampleHalEnricher();
|
||||||
registry.register(Integer.class, two);
|
registry.register(Integer.class, two);
|
||||||
|
|
||||||
Iterable<LinkEnricher> enrichers = registry.allByType(String.class);
|
Iterable<HalEnricher> enrichers = registry.allByType(String.class);
|
||||||
assertThat(enrichers).containsOnly(one);
|
assertThat(enrichers).containsOnly(one);
|
||||||
|
|
||||||
enrichers = registry.allByType(Integer.class);
|
enrichers = registry.allByType(Integer.class);
|
||||||
assertThat(enrichers).containsOnly(two);
|
assertThat(enrichers).containsOnly(two);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SampleLinkEnricher implements LinkEnricher {
|
private static class SampleHalEnricher implements HalEnricher {
|
||||||
@Override
|
@Override
|
||||||
public void enrich(LinkEnricherContext context, LinkAppender appender) {
|
public void enrich(HalEnricherContext context, HalAppender appender) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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<String,Object> 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -36,7 +36,7 @@ import com.google.common.io.ByteSource;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|||||||
@@ -46,4 +46,13 @@ class RepositoryPermissionTest {
|
|||||||
|
|
||||||
assertThat(permission1).isNotEqualTo(permission2);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package sonia.scm.security;
|
package sonia.scm.security;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import org.apache.shiro.authc.AuthenticationInfo;
|
import org.apache.shiro.authc.AuthenticationInfo;
|
||||||
import org.apache.shiro.authc.DisabledAccountException;
|
import org.apache.shiro.authc.DisabledAccountException;
|
||||||
import org.apache.shiro.authc.UnknownAccountException;
|
import org.apache.shiro.authc.UnknownAccountException;
|
||||||
|
|||||||
57
scm-core/src/test/java/sonia/scm/util/ComparablesTest.java
Normal file
57
scm-core/src/test/java/sonia/scm/util/ComparablesTest.java
Normal file
@@ -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<One> 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 {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -37,9 +37,6 @@ package sonia.scm.store;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.repository.RepositoryLocationResolver;
|
import sonia.scm.repository.RepositoryLocationResolver;
|
||||||
import sonia.scm.security.KeyGenerator;
|
import sonia.scm.security.KeyGenerator;
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.GenericDAO;
|
import sonia.scm.GenericDAO;
|
||||||
import sonia.scm.ModelObject;
|
import sonia.scm.ModelObject;
|
||||||
import sonia.scm.group.xml.XmlGroupDAO;
|
|
||||||
import sonia.scm.store.ConfigurationStore;
|
import sonia.scm.store.ConfigurationStore;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package sonia.scm.xml;
|
package sonia.scm.xml;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ package sonia.scm.it;
|
|||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.assertj.core.api.Assertions;
|
import org.assertj.core.api.Assertions;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import io.restassured.response.Response;
|
|||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.user.User;
|
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import javax.ws.rs.PathParam;
|
|||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Response;
|
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.
|
* RESTful Web Service Resource to manage the configuration of the git plugin.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.SCMContextProvider;
|
import sonia.scm.SCMContextProvider;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.plugin.PluginLoader;
|
||||||
import sonia.scm.repository.spi.GitRepositoryServiceProvider;
|
import sonia.scm.repository.spi.GitRepositoryServiceProvider;
|
||||||
import sonia.scm.schedule.Scheduler;
|
import sonia.scm.schedule.Scheduler;
|
||||||
import sonia.scm.schedule.Task;
|
import sonia.scm.schedule.Task;
|
||||||
@@ -103,9 +104,10 @@ public class GitRepositoryHandler
|
|||||||
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
public GitRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||||
Scheduler scheduler,
|
Scheduler scheduler,
|
||||||
RepositoryLocationResolver repositoryLocationResolver,
|
RepositoryLocationResolver repositoryLocationResolver,
|
||||||
GitWorkdirFactory workdirFactory)
|
GitWorkdirFactory workdirFactory,
|
||||||
|
PluginLoader pluginLoader)
|
||||||
{
|
{
|
||||||
super(storeFactory, repositoryLocationResolver);
|
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
||||||
this.scheduler = scheduler;
|
this.scheduler = scheduler;
|
||||||
this.workdirFactory = workdirFactory;
|
this.workdirFactory = workdirFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ package sonia.scm.web;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
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 type {Branch, Repository} from "@scm-manager/ui-types";
|
||||||
import {translate} from "react-i18next";
|
import {translate} from "react-i18next";
|
||||||
|
|
||||||
@@ -113,6 +113,7 @@ class RepositoryConfig extends React.Component<Props, State> {
|
|||||||
if (!(loadingBranches || loadingDefaultBranch)) {
|
if (!(loadingBranches || loadingDefaultBranch)) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Subtitle subtitle={t("scm-git-plugin.repo-config.title")}/>
|
||||||
{this.renderBranchChangedNotification()}
|
{this.renderBranchChangedNotification()}
|
||||||
<form onSubmit={this.submit}>
|
<form onSubmit={this.submit}>
|
||||||
<BranchSelector
|
<BranchSelector
|
||||||
@@ -127,6 +128,7 @@ class RepositoryConfig extends React.Component<Props, State> {
|
|||||||
disabled={!this.state.selectedBranchName}
|
disabled={!this.state.selectedBranchName}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
<hr />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -27,14 +27,9 @@ binder.bind(
|
|||||||
);
|
);
|
||||||
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
||||||
|
|
||||||
cfgBinder.bindRepository(
|
binder.bind("repo-config.route", RepositoryConfig, gitPredicate);
|
||||||
"/configuration",
|
|
||||||
"scm-git-plugin.repo-config.link",
|
|
||||||
"configuration",
|
|
||||||
RepositoryConfig
|
|
||||||
);
|
|
||||||
// global config
|
|
||||||
|
|
||||||
|
// global config
|
||||||
cfgBinder.bindGlobal(
|
cfgBinder.bindGlobal(
|
||||||
"/git",
|
"/git",
|
||||||
"scm-git-plugin.config.link",
|
"scm-git-plugin.config.link",
|
||||||
|
|||||||
@@ -1,9 +1,56 @@
|
|||||||
{
|
{
|
||||||
"scm-git-plugin": {
|
"scm-git-plugin": {
|
||||||
"information": {
|
"information": {
|
||||||
"clone" : "Repository Klonen",
|
"clone" : "Repository klonen",
|
||||||
"create" : "Neue Repository erstellen",
|
"create" : "Neues Repository erstellen",
|
||||||
"replace" : "Eine existierende Repository aktualisieren"
|
"replace" : "Ein bestehendes Repository aktualisieren",
|
||||||
|
"merge": {
|
||||||
|
"heading": "Merge des Source Branch in den Target Branch",
|
||||||
|
"checkout": "1. Sicherstellen, dass der Workspace aufgeräumt ist und der Target Branch ausgecheckt wurde.",
|
||||||
|
"update": "2. Update Workspace",
|
||||||
|
"merge": "3. Merge Source Branch",
|
||||||
|
"resolve": "4. Merge Konflikte auflösen und korrigierte Dateien dem Index hinzufügen.",
|
||||||
|
"commit": "5. Commit",
|
||||||
|
"push": "6. Push des Merge"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Git",
|
||||||
|
"title": "Git Konfiguration",
|
||||||
|
"gcExpression": "GC Cron Ausdruck",
|
||||||
|
"gcExpressionHelpText": "Benutze Quartz Cron Ausdrücke (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK), um git GC regelmäßig auszuführen.",
|
||||||
|
"nonFastForwardDisallowed": "Deaktiviere \"Non Fast-Forward\"",
|
||||||
|
"nonFastForwardDisallowedHelpText": "Git Pushes ablehnen, die nicht \"fast-forward\" sind, wie \"--force\".",
|
||||||
|
"disabled": "Deaktiviert",
|
||||||
|
"disabledHelpText": "Aktiviere oder deaktiviere das Git Plugin",
|
||||||
|
"submit": "Speichern"
|
||||||
|
},
|
||||||
|
"repo-config": {
|
||||||
|
"link": "Konfiguration",
|
||||||
|
"title": "Git Einstellungen",
|
||||||
|
"default-branch": "Standard Branch",
|
||||||
|
"submit": "Speichern",
|
||||||
|
"error": {
|
||||||
|
"title": "Fehler",
|
||||||
|
"subtitle": "Ein Fehler ist aufgetreten."
|
||||||
|
},
|
||||||
|
"success": "Der standard Branch wurde geändert!"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions" : {
|
||||||
|
"configuration": {
|
||||||
|
"read": {
|
||||||
|
"git": {
|
||||||
|
"displayName": "Git Konfiguration lesen",
|
||||||
|
"description": "Darf die git Konfiguration lesen."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"git": {
|
||||||
|
"displayName": "Git Konfiguration schreiben",
|
||||||
|
"description": "Darf die git Konfiguration verändern."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
},
|
},
|
||||||
"repo-config": {
|
"repo-config": {
|
||||||
"link": "Configuration",
|
"link": "Configuration",
|
||||||
|
"title": "Git Settings",
|
||||||
"default-branch": "Default branch",
|
"default-branch": "Default branch",
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"error": {
|
"error": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.GitConfig;
|
import sonia.scm.repository.GitConfig;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.mockito.Captor;
|
|||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.GitConfig;
|
import sonia.scm.repository.GitConfig;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
@@ -29,6 +29,7 @@ import sonia.scm.store.ConfigurationStoreFactory;
|
|||||||
import sonia.scm.web.GitVndMediaType;
|
import sonia.scm.web.GitVndMediaType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readWrite")
|
@SubjectAware(username = "readWrite")
|
||||||
public void shouldGetGitConfig() throws URISyntaxException {
|
public void shouldGetGitConfig() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
@@ -115,7 +116,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readWrite")
|
@SubjectAware(username = "readWrite")
|
||||||
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException {
|
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
when(repositoryHandler.getConfig()).thenReturn(null);
|
when(repositoryHandler.getConfig()).thenReturn(null);
|
||||||
|
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
@@ -126,7 +127,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@SubjectAware(username = "readOnly")
|
||||||
public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException {
|
public void shouldGetGitConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
@@ -159,7 +160,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "writeOnly")
|
@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"));
|
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");
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X");
|
||||||
@@ -176,7 +177,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@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"));
|
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");
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X");
|
||||||
@@ -193,7 +194,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "writeOnly")
|
@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"));
|
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X"));
|
||||||
GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig();
|
GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig();
|
||||||
gitRepositoryConfig.setDefaultBranch("test");
|
gitRepositoryConfig.setDefaultBranch("test");
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.GitConfig;
|
import sonia.scm.repository.GitConfig;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|||||||
@@ -14,9 +14,6 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
import sonia.scm.repository.RepositoryManager;
|
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.JsonEnricherContext;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import org.junit.rules.TemporaryFolder;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
RepositoryLocationResolver locationResolver,
|
RepositoryLocationResolver locationResolver,
|
||||||
File directory) {
|
File directory) {
|
||||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||||
scheduler, locationResolver, gitWorkdirFactory);
|
scheduler, locationResolver, gitWorkdirFactory, null);
|
||||||
repositoryHandler.init(contextProvider);
|
repositoryHandler.init(contextProvider);
|
||||||
|
|
||||||
GitConfig config = new GitConfig();
|
GitConfig config = new GitConfig();
|
||||||
@@ -108,7 +108,7 @@ public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
@Test
|
@Test
|
||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
GitRepositoryHandler repositoryHandler = new GitRepositoryHandler(factory,
|
||||||
scheduler, locationResolver, gitWorkdirFactory);
|
scheduler, locationResolver, gitWorkdirFactory, null);
|
||||||
GitConfig config = new GitConfig();
|
GitConfig config = new GitConfig();
|
||||||
config.setDisabled(false);
|
config.setDisabled(false);
|
||||||
config.setGcExpression("gc exp");
|
config.setGcExpression("gc exp");
|
||||||
|
|||||||
@@ -35,10 +35,8 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.store.InMemoryConfigurationStore;
|
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -35,11 +35,9 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
|
||||||
import sonia.scm.repository.BlameLine;
|
import sonia.scm.repository.BlameLine;
|
||||||
import sonia.scm.repository.BlameResult;
|
import sonia.scm.repository.BlameResult;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|||||||
@@ -32,11 +32,9 @@
|
|||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
|
||||||
import sonia.scm.repository.BrowserResult;
|
import sonia.scm.repository.BrowserResult;
|
||||||
import sonia.scm.repository.FileObject;
|
import sonia.scm.repository.FileObject;
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|||||||
@@ -36,10 +36,8 @@ package sonia.scm.repository.spi;
|
|||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import sonia.scm.event.ScmEventBus;
|
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.ClearRepositoryCacheEvent;
|
|
||||||
import sonia.scm.repository.GitRepositoryConfig;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.Modifications;
|
import sonia.scm.repository.Modifications;
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.GitConfig;
|
import sonia.scm.repository.GitConfig;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
|
||||||
|
|||||||
@@ -35,14 +35,12 @@ package sonia.scm.installer;
|
|||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import sonia.scm.repository.HgConfig;
|
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
import sonia.scm.util.IOUtil;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.io.INIConfiguration;
|
import sonia.scm.io.INIConfiguration;
|
||||||
import sonia.scm.io.INIConfigurationReader;
|
import sonia.scm.io.INIConfigurationReader;
|
||||||
import sonia.scm.io.INIConfigurationWriter;
|
|
||||||
import sonia.scm.io.INISection;
|
import sonia.scm.io.INISection;
|
||||||
import sonia.scm.util.ValidationUtil;
|
import sonia.scm.util.ValidationUtil;
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import sonia.scm.io.INIConfigurationReader;
|
|||||||
import sonia.scm.io.INIConfigurationWriter;
|
import sonia.scm.io.INIConfigurationWriter;
|
||||||
import sonia.scm.io.INISection;
|
import sonia.scm.io.INISection;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.plugin.PluginLoader;
|
||||||
import sonia.scm.repository.spi.HgRepositoryServiceProvider;
|
import sonia.scm.repository.spi.HgRepositoryServiceProvider;
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
@@ -111,9 +112,10 @@ public class HgRepositoryHandler
|
|||||||
@Inject
|
@Inject
|
||||||
public HgRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
public HgRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||||
Provider<HgContext> hgContextProvider,
|
Provider<HgContext> hgContextProvider,
|
||||||
RepositoryLocationResolver repositoryLocationResolver)
|
RepositoryLocationResolver repositoryLocationResolver,
|
||||||
|
PluginLoader pluginLoader)
|
||||||
{
|
{
|
||||||
super(storeFactory, repositoryLocationResolver);
|
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
||||||
this.hgContextProvider = hgContextProvider;
|
this.hgContextProvider = hgContextProvider;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import com.aragost.javahg.internals.AbstractCommand;
|
|||||||
import com.aragost.javahg.internals.HgInputStream;
|
import com.aragost.javahg.internals.HgInputStream;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
|
|
||||||
import sonia.scm.repository.FileObject;
|
import sonia.scm.repository.FileObject;
|
||||||
import sonia.scm.repository.SubRepository;
|
import sonia.scm.repository.SubRepository;
|
||||||
@@ -52,7 +51,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mercurial command to list files of a repository.
|
* Mercurial command to list files of a repository.
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import sonia.scm.web.filter.PermissionFilter;
|
|||||||
|
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|||||||
@@ -1,9 +1,48 @@
|
|||||||
{
|
{
|
||||||
"scm-hg-plugin": {
|
"scm-hg-plugin": {
|
||||||
"information": {
|
"information": {
|
||||||
"clone" : "Repository Klonen",
|
"clone" : "Repository klonen",
|
||||||
"create" : "Neue Repository erstellen",
|
"create" : "Neues Repository erstellen",
|
||||||
"replace" : "Eine existierende Repository aktualisieren"
|
"replace" : "Ein bestehendes Repository aktualisieren"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Mercurial",
|
||||||
|
"title": "Mercurial Konfiguration",
|
||||||
|
"hgBinary": "HG Binary",
|
||||||
|
"hgBinaryHelpText": "Pfad des Mercurial Binary.",
|
||||||
|
"pythonBinary": "Python Binary",
|
||||||
|
"pythonBinaryHelpText": "Pfad des Python binary.",
|
||||||
|
"pythonPath": "Python Module Such Pfad",
|
||||||
|
"pythonPathHelpText": "Python Module Such Pfad (PYTHONPATH).",
|
||||||
|
"encoding": "Encoding",
|
||||||
|
"encodingHelpText": "Repository Encoding.",
|
||||||
|
"useOptimizedBytecode": "Optimized Bytecode (.pyo)",
|
||||||
|
"useOptimizedBytecodeHelpText": "Verwende den Python '-O' Switch.",
|
||||||
|
"showRevisionInId": "Revision anzeigen",
|
||||||
|
"showRevisionInIdHelpText": "Die Revision als Teil der Node ID anzeigen.",
|
||||||
|
"enableHttpPostArgs": "HttpPostArgs Protocol aktivieren",
|
||||||
|
"enableHttpPostArgsHelpText": "Aktiviert das experimentelle HttpPostArgs Protokoll von Mercurial. Das HttpPostArgs Protokoll verwendet den Post Request Body anstatt des HTTP Headers um Meta Informationen zu versenden. Dieses Vorgehen reduziert die Header Größe der Mercurial Requests. HttpPostArgs wird seit Mercurial 3.8 unterstützt.",
|
||||||
|
"disableHookSSLValidation": "SSL Validierung für Hooks deaktivieren",
|
||||||
|
"disableHookSSLValidationHelpText": "Deaktiviert die Validierung von SSL Zertifikaten für den Mercurial Hook, der die Repositoryänderungen wieder zurück an den SCM-Manager leitet. Diese Option sollte nur benutzt werden, wenn der SCM-Manager ein selbstsigniertes Zertifikat verwendet.",
|
||||||
|
"disabled": "Deaktiviert",
|
||||||
|
"disabledHelpText": "Aktiviert oder deaktiviert das Mercurial Plugin.",
|
||||||
|
"required": "Dieser Konfigurationswert wird benötigt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions" : {
|
||||||
|
"configuration": {
|
||||||
|
"read": {
|
||||||
|
"hg": {
|
||||||
|
"displayName": "Mercurial Konfiguration lesen",
|
||||||
|
"description": "Darf die Mercurial Konfiguration lesen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"hg": {
|
||||||
|
"displayName": "Mercurial Konfiguration schreiben",
|
||||||
|
"description": "Darf die Mercurial Konfiguration verändern"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@
|
|||||||
"showRevisionInId": "Show Revision",
|
"showRevisionInId": "Show Revision",
|
||||||
"showRevisionInIdHelpText": "Show revision as part of the node id.",
|
"showRevisionInIdHelpText": "Show revision as part of the node id.",
|
||||||
"enableHttpPostArgs": "Enable HttpPostArgs Protocol",
|
"enableHttpPostArgs": "Enable HttpPostArgs Protocol",
|
||||||
"enableHttpPostArgsHelpText": "Disables the validation of ssl certificates for the mercurial hook, which forwards the repository changes back to scm-manager. This option should only be used, if SCM-Manager uses a self signed certificate.",
|
"enableHttpPostArgsHelpText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8.",
|
||||||
"disableHookSSLValidation": "Disable SSL Validation on Hooks",
|
"disableHookSSLValidation": "Disable SSL Validation on Hooks",
|
||||||
"disableHookSSLValidationHelpText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8.",
|
"disableHookSSLValidationHelpText": "Disables the validation of ssl certificates for the mercurial hook, which forwards the repository changes back to scm-manager. This option should only be used, if SCM-Manager uses a self signed certificate.",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"disabledHelpText": "Enable or disable the Mercurial plugin.",
|
"disabledHelpText": "Enable or disable the Mercurial plugin.",
|
||||||
"required": "This configuration value is required"
|
"required": "This configuration value is required"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.HgConfig;
|
import sonia.scm.repository.HgConfig;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
import sonia.scm.web.HgVndMediaType;
|
import sonia.scm.web.HgVndMediaType;
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ package sonia.scm.api.v2.resources;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.HgConfig;
|
import sonia.scm.repository.HgConfig;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.installer.HgPackage;
|
import sonia.scm.installer.HgPackage;
|
||||||
import sonia.scm.installer.HgPackageReader;
|
import sonia.scm.installer.HgPackageReader;
|
||||||
import sonia.scm.net.ahc.AdvancedHttpClient;
|
import sonia.scm.net.ahc.AdvancedHttpClient;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.installer.HgPackage;
|
import sonia.scm.installer.HgPackage;
|
||||||
import sonia.scm.installer.HgPackages;
|
import sonia.scm.installer.HgPackages;
|
||||||
|
|
||||||
|
|||||||
@@ -16,15 +16,15 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.HgConfig;
|
import sonia.scm.repository.HgConfig;
|
||||||
import sonia.scm.repository.HgRepositoryHandler;
|
import sonia.scm.repository.HgRepositoryHandler;
|
||||||
import sonia.scm.web.HgVndMediaType;
|
import sonia.scm.web.HgVndMediaType;
|
||||||
|
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public class HgConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readWrite")
|
@SubjectAware(username = "readWrite")
|
||||||
public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException {
|
public void shouldGetHgConfigEvenWhenItsEmpty() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
when(repositoryHandler.getConfig()).thenReturn(null);
|
when(repositoryHandler.getConfig()).thenReturn(null);
|
||||||
|
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
@@ -110,7 +110,7 @@ public class HgConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@SubjectAware(username = "readOnly")
|
||||||
public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException {
|
public void shouldGetHgConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package sonia.scm.api.v2.resources;
|
|||||||
import sonia.scm.installer.HgPackage;
|
import sonia.scm.installer.HgPackage;
|
||||||
import sonia.scm.repository.HgConfig;
|
import sonia.scm.repository.HgConfig;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.HgConfig;
|
import sonia.scm.repository.HgConfig;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory, RepositoryLocationResolver locationResolver, File directory) {
|
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);
|
handler.init(contextProvider);
|
||||||
HgTestUtil.checkForSkip(handler);
|
HgTestUtil.checkForSkip(handler);
|
||||||
@@ -87,7 +87,7 @@ public class HgRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver);
|
HgRepositoryHandler repositoryHandler = new HgRepositoryHandler(factory, provider, locationResolver, null);
|
||||||
|
|
||||||
HgConfig hgConfig = new HgConfig();
|
HgConfig hgConfig = new HgConfig();
|
||||||
hgConfig.setHgBinary("hg");
|
hgConfig.setHgBinary("hg");
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public final class HgTestUtil
|
|||||||
|
|
||||||
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
RepositoryLocationResolver repositoryLocationResolver = new RepositoryLocationResolver(context, repoDao, new InitialRepositoryLocationResolver());
|
||||||
HgRepositoryHandler handler =
|
HgRepositoryHandler handler =
|
||||||
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver);
|
new HgRepositoryHandler(new InMemoryConfigurationStoreFactory(), new HgContextProvider(), repositoryLocationResolver, null);
|
||||||
Path repoDir = directory.toPath();
|
Path repoDir = directory.toPath();
|
||||||
when(repoDao.getPath(any())).thenReturn(repoDir);
|
when(repoDao.getPath(any())).thenReturn(repoDir);
|
||||||
handler.init(context);
|
handler.init(context);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.IOException;
|
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.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|||||||
@@ -48,8 +48,6 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.*;
|
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.CMDS_HEADS_KNOWN_NODES;
|
||||||
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.BOOKMARKS;
|
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.BOOKMARKS;
|
||||||
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.PHASES;
|
import static sonia.scm.web.WireProtocolRequestMockFactory.Namespace.PHASES;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import com.google.common.collect.Lists;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|||||||
@@ -53,9 +53,9 @@ import sonia.scm.io.INIConfigurationWriter;
|
|||||||
import sonia.scm.io.INISection;
|
import sonia.scm.io.INISection;
|
||||||
import sonia.scm.logging.SVNKitLogger;
|
import sonia.scm.logging.SVNKitLogger;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.plugin.PluginLoader;
|
||||||
import sonia.scm.repository.spi.HookEventFacade;
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
import sonia.scm.repository.spi.SvnRepositoryServiceProvider;
|
import sonia.scm.repository.spi.SvnRepositoryServiceProvider;
|
||||||
import sonia.scm.store.ConfigurationStore;
|
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
@@ -97,9 +97,10 @@ public class SvnRepositoryHandler
|
|||||||
@Inject
|
@Inject
|
||||||
public SvnRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
public SvnRepositoryHandler(ConfigurationStoreFactory storeFactory,
|
||||||
HookEventFacade eventFacade,
|
HookEventFacade eventFacade,
|
||||||
RepositoryLocationResolver repositoryLocationResolver)
|
RepositoryLocationResolver repositoryLocationResolver,
|
||||||
|
PluginLoader pluginLoader)
|
||||||
{
|
{
|
||||||
super(storeFactory, repositoryLocationResolver);
|
super(storeFactory, repositoryLocationResolver, pluginLoader);
|
||||||
|
|
||||||
// register logger
|
// register logger
|
||||||
SVNDebugLog.setDefaultLog(new SVNKitLogger());
|
SVNDebugLog.setDefaultLog(new SVNKitLogger());
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.tmatesoft.svn.core.SVNDirEntry;
|
import org.tmatesoft.svn.core.SVNDirEntry;
|
||||||
@@ -53,7 +52,6 @@ import sonia.scm.repository.SvnUtil;
|
|||||||
import sonia.scm.util.Util;
|
import sonia.scm.util.Util;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
//~--- JDK imports ------------------------------------------------------------
|
//~--- JDK imports ------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.tmatesoft.svn.core.SVNException;
|
import org.tmatesoft.svn.core.SVNException;
|
||||||
import org.tmatesoft.svn.core.SVNLogEntry;
|
import org.tmatesoft.svn.core.SVNLogEntry;
|
||||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
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.InternalRepositoryException;
|
||||||
import sonia.scm.repository.Modifications;
|
import sonia.scm.repository.Modifications;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
@@ -19,23 +21,45 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif
|
|||||||
super(context, repository);
|
super(context, repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
public Modifications getModifications(String revisionOrTransactionId) {
|
||||||
public Modifications getModifications(String revision) {
|
Modifications modifications;
|
||||||
Modifications modifications = null;
|
|
||||||
log.debug("get modifications {}", revision);
|
|
||||||
try {
|
try {
|
||||||
long revisionNumber = SvnUtil.parseRevision(revision, repository);
|
if (SvnUtil.isTransactionEntryId(revisionOrTransactionId)) {
|
||||||
|
modifications = getModificationsFromTransaction(SvnUtil.getTransactionId(revisionOrTransactionId));
|
||||||
|
} else {
|
||||||
|
modifications = getModificationFromRevision(revisionOrTransactionId);
|
||||||
|
}
|
||||||
|
return modifications;
|
||||||
|
} catch (SVNException 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();
|
SVNRepository repo = open();
|
||||||
Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber,
|
Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber,
|
||||||
revisionNumber, true, true);
|
revisionNumber, true, true);
|
||||||
if (Util.isNotEmpty(entries)) {
|
if (Util.isNotEmpty(entries)) {
|
||||||
modifications = SvnUtil.createModifications(entries.iterator().next(), revision);
|
return SvnUtil.createModifications(entries.iterator().next(), revision);
|
||||||
}
|
}
|
||||||
} catch (SVNException ex) {
|
return null;
|
||||||
throw new InternalRepositoryException(repository, "could not open repository", ex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return modifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,5 +68,4 @@ public class SvnModificationsCommand extends AbstractSvnCommand implements Modif
|
|||||||
return getModifications(request.getRevision());
|
return getModifications(request.getRevision());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,41 @@
|
|||||||
"scm-svn-plugin": {
|
"scm-svn-plugin": {
|
||||||
"information": {
|
"information": {
|
||||||
"checkout": "Repository auschecken"
|
"checkout": "Repository auschecken"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"link": "Subversion",
|
||||||
|
"title": "Subversion Konfiguration",
|
||||||
|
"compatibility": "Version Kompatibilität",
|
||||||
|
"compatibilityHelpText": "Gibt an, mit welcher Subversion Version die Repositories kompatibel sind.",
|
||||||
|
"compatibility-values": {
|
||||||
|
"none": "Keine Kompatibilität",
|
||||||
|
"pre14": "Vor 1.4 kompatibel",
|
||||||
|
"pre15": "Vor 1.5 kompatibel",
|
||||||
|
"pre16": "Vor 1.6 kompatibel",
|
||||||
|
"pre17": "Vor 1.7 kompatibel",
|
||||||
|
"with17": "Mit 1.7 kompatibel"
|
||||||
|
},
|
||||||
|
"enabledGZip": "GZip Compression aktivieren",
|
||||||
|
"enabledGZipHelpText": "Aktiviert GZip Kompression für SVN Responses",
|
||||||
|
"disabled": "Deaktiviert",
|
||||||
|
"disabledHelpText": "Aktiviert oder deaktiviert das SVN Plugin",
|
||||||
|
"required": "Dieser Konfigurationswert wird benötigt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"permissions": {
|
||||||
|
"configuration": {
|
||||||
|
"read": {
|
||||||
|
"svn": {
|
||||||
|
"displayName": "Subversion Konfiguration lesen",
|
||||||
|
"description": "Darf die Subversion Konfiguration lesen"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"svn": {
|
||||||
|
"displayName": "Subversion Konfiguration schreiben",
|
||||||
|
"description": "Darf die Subversion Konfiguration verändern"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"link": "Subversion",
|
"link": "Subversion",
|
||||||
"title": "Subversion Configuration",
|
"title": "Subversion Configuration",
|
||||||
"compatibility": "Version Compatibility",
|
"compatibility": "Version Compatibility",
|
||||||
"compatibilityHelpText": "Specifies with which subversion version repositories are compatible.",
|
"compatibilityHelpText": "Specifies with which Subversion version repositories are compatible.",
|
||||||
"compatibility-values": {
|
"compatibility-values": {
|
||||||
"none": "No compatibility",
|
"none": "No compatibility",
|
||||||
"pre14": "Pre 1.4 Compatible",
|
"pre14": "Pre 1.4 Compatible",
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
"with17": "With 1.7 Compatible"
|
"with17": "With 1.7 Compatible"
|
||||||
},
|
},
|
||||||
"enabledGZip": "Enable GZip Compression",
|
"enabledGZip": "Enable GZip Compression",
|
||||||
"enabledGZipHelpText": "Enable GZip compression for svn responses.",
|
"enabledGZipHelpText": "Enable GZip compression for SVN responses.",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"disabledHelpText": "Enable or disable the Git plugin",
|
"disabledHelpText": "Enable or disable the SVN plugin",
|
||||||
"required": "This configuration value is required"
|
"required": "This configuration value is required"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package sonia.scm.api.v2.resources;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.Compatibility;
|
import sonia.scm.repository.Compatibility;
|
||||||
import sonia.scm.repository.SvnConfig;
|
import sonia.scm.repository.SvnConfig;
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.SvnConfig;
|
import sonia.scm.repository.SvnConfig;
|
||||||
import sonia.scm.repository.SvnRepositoryHandler;
|
import sonia.scm.repository.SvnRepositoryHandler;
|
||||||
import sonia.scm.web.SvnVndMediaType;
|
import sonia.scm.web.SvnVndMediaType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ public class SvnConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@SubjectAware(username = "readOnly")
|
||||||
public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException {
|
public void shouldGetSvnConfigWithoutUpdateLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
|||||||
@@ -11,11 +11,10 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.Compatibility;
|
import sonia.scm.repository.Compatibility;
|
||||||
import sonia.scm.repository.SvnConfig;
|
import sonia.scm.repository.SvnConfig;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|||||||
@@ -32,14 +32,10 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
import sonia.scm.repository.api.HookContextFactory;
|
import sonia.scm.repository.api.HookContextFactory;
|
||||||
import sonia.scm.repository.spi.HookEventFacade;
|
import sonia.scm.repository.spi.HookEventFacade;
|
||||||
import sonia.scm.store.ConfigurationStore;
|
|
||||||
import sonia.scm.store.ConfigurationStoreFactory;
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -47,7 +43,7 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
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.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.MockitoAnnotations.initMocks;
|
import static org.mockito.MockitoAnnotations.initMocks;
|
||||||
@@ -93,7 +89,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
protected RepositoryHandler createRepositoryHandler(ConfigurationStoreFactory factory,
|
||||||
RepositoryLocationResolver locationResolver,
|
RepositoryLocationResolver locationResolver,
|
||||||
File directory) {
|
File directory) {
|
||||||
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver);
|
SvnRepositoryHandler handler = new SvnRepositoryHandler(factory, null, locationResolver, null);
|
||||||
|
|
||||||
handler.init(contextProvider);
|
handler.init(contextProvider);
|
||||||
|
|
||||||
@@ -109,7 +105,7 @@ public class SvnRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
|||||||
public void getDirectory() {
|
public void getDirectory() {
|
||||||
when(factory.withType(any())).thenCallRealMethod();
|
when(factory.withType(any())).thenCallRealMethod();
|
||||||
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
|
SvnRepositoryHandler repositoryHandler = new SvnRepositoryHandler(factory,
|
||||||
facade, locationResolver);
|
facade, locationResolver, null);
|
||||||
|
|
||||||
SvnConfig svnConfig = new SvnConfig();
|
SvnConfig svnConfig = new SvnConfig();
|
||||||
repositoryHandler.setConfig(svnConfig);
|
repositoryHandler.setConfig(svnConfig);
|
||||||
|
|||||||
@@ -39,8 +39,6 @@ import sonia.scm.repository.Changeset;
|
|||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.Modifications;
|
import sonia.scm.repository.Modifications;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class DummyRepositoryHandler
|
|||||||
private final Set<String> existingRepoNames = new HashSet<>();
|
private final Set<String> existingRepoNames = new HashSet<>();
|
||||||
|
|
||||||
public DummyRepositoryHandler(ConfigurationStoreFactory storeFactory, RepositoryLocationResolver repositoryLocationResolver) {
|
public DummyRepositoryHandler(ConfigurationStoreFactory storeFactory, RepositoryLocationResolver repositoryLocationResolver) {
|
||||||
super(storeFactory, repositoryLocationResolver);
|
super(storeFactory, repositoryLocationResolver, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -13,10 +13,11 @@ const styles = {
|
|||||||
minWidthOfLabel: {
|
minWidthOfLabel: {
|
||||||
minWidth: "4.5rem"
|
minWidth: "4.5rem"
|
||||||
},
|
},
|
||||||
wrapper: {
|
labelSizing: {
|
||||||
padding: "1rem 1.5rem 0.25rem 1.5rem",
|
fontSize: "1rem !important"
|
||||||
border: "1px solid #eee",
|
},
|
||||||
borderRadius: "5px 5px 0 0"
|
noBottomMargin: {
|
||||||
|
marginBottom: "0 !important"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,9 +53,9 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"has-background-light field",
|
"field",
|
||||||
"is-horizontal",
|
"is-horizontal",
|
||||||
classes.wrapper
|
classes.noBottomMargin
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -65,10 +66,14 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
classes.minWidthOfLabel
|
classes.minWidthOfLabel
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<label className="label">{label}</label>
|
<label className={classNames("label", classes.labelSizing)}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="field-body">
|
<div className="field-body">
|
||||||
<div className="field is-narrow">
|
<div
|
||||||
|
className={classNames("field is-narrow", classes.noBottomMargin)}
|
||||||
|
>
|
||||||
<div className="control">
|
<div className="control">
|
||||||
<DropDown
|
<DropDown
|
||||||
className="is-fullwidth"
|
className="is-fullwidth"
|
||||||
|
|||||||
@@ -3,14 +3,17 @@ import React from "react";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
displayName: string,
|
displayName: string,
|
||||||
url: string
|
url: string,
|
||||||
|
disabled: boolean,
|
||||||
|
onClick?: () => void
|
||||||
};
|
};
|
||||||
|
|
||||||
class DownloadButton extends React.Component<Props> {
|
class DownloadButton extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { displayName, url } = this.props;
|
const { displayName, url, disabled, onClick } = this.props;
|
||||||
|
const onClickOrDefault = !!onClick ? onClick : () => {};
|
||||||
return (
|
return (
|
||||||
<a className="button is-large is-link" href={url}>
|
<a className="button is-large is-link" href={url} disabled={disabled} onClick={onClickOrDefault}>
|
||||||
<span className="icon is-medium">
|
<span className="icon is-medium">
|
||||||
<i className="fas fa-arrow-circle-down" />
|
<i className="fas fa-arrow-circle-down" />
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ class ConfigurationBinder {
|
|||||||
binder.bind("config.navigation", ConfigNavLink, configPredicate);
|
binder.bind("config.navigation", ConfigNavLink, configPredicate);
|
||||||
|
|
||||||
// route for global configuration, passes the link from the index resource to component
|
// route for global configuration, passes the link from the index resource to component
|
||||||
const ConfigRoute = ({ url, links }) => {
|
const ConfigRoute = ({ url, links, ...additionalProps }) => {
|
||||||
const link = links[linkName].href;
|
const link = links[linkName].href;
|
||||||
return this.route(url + to, <ConfigurationComponent link={link}/>);
|
return this.route(url + to, <ConfigurationComponent link={link} {...additionalProps} />);
|
||||||
};
|
};
|
||||||
|
|
||||||
// bind config route to extension point
|
// bind config route to extension point
|
||||||
@@ -63,9 +63,36 @@ class ConfigurationBinder {
|
|||||||
|
|
||||||
|
|
||||||
// route for global configuration, passes the current repository to component
|
// route for global configuration, passes the current repository to component
|
||||||
const RepoRoute = ({url, repository}) => {
|
const RepoRoute = ({url, repository, ...additionalProps}) => {
|
||||||
const link = repository._links[linkName].href
|
const link = repository._links[linkName].href;
|
||||||
return this.route(url + to, <RepositoryComponent repository={repository} link={link}/>);
|
return this.route(url + to, <RepositoryComponent repository={repository} link={link} {...additionalProps}/>);
|
||||||
|
};
|
||||||
|
|
||||||
|
// bind config route to extension point
|
||||||
|
binder.bind("repository.route", RepoRoute, repoPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
bindRepositorySetting(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any) {
|
||||||
|
|
||||||
|
// create predicate based on the link name of the current repository route
|
||||||
|
// if the linkname is not available, the navigation link and the route are not bound to the extension points
|
||||||
|
const repoPredicate = (props: Object) => {
|
||||||
|
return props.repository && props.repository._links && props.repository._links[linkName];
|
||||||
|
};
|
||||||
|
|
||||||
|
// create NavigationLink with translated label
|
||||||
|
const RepoNavLink = translate(this.i18nNamespace)(({t, url}) => {
|
||||||
|
return this.navLink(url + "/settings" + to, labelI18nKey, t);
|
||||||
|
});
|
||||||
|
|
||||||
|
// bind navigation link to extension point
|
||||||
|
binder.bind("repository.subnavigation", RepoNavLink, repoPredicate);
|
||||||
|
|
||||||
|
|
||||||
|
// route for global configuration, passes the current repository to component
|
||||||
|
const RepoRoute = ({url, repository, ...additionalProps}) => {
|
||||||
|
const link = repository._links[linkName].href;
|
||||||
|
return this.route(url + "/settings" + to, <RepositoryComponent repository={repository} link={link} {...additionalProps}/>);
|
||||||
};
|
};
|
||||||
|
|
||||||
// bind config route to extension point
|
// bind config route to extension point
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ type Props = {
|
|||||||
buttonLabel: string,
|
buttonLabel: string,
|
||||||
fieldLabel: string,
|
fieldLabel: string,
|
||||||
errorMessage: string,
|
errorMessage: string,
|
||||||
helpText?: string
|
helpText?: string,
|
||||||
|
validateEntry?: string => boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
@@ -25,6 +26,15 @@ class AddEntryToTableField extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isValid = () => {
|
||||||
|
const {validateEntry} = this.props;
|
||||||
|
if (!this.state.entryToAdd || this.state.entryToAdd === "" || !validateEntry) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return validateEntry(this.state.entryToAdd);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
disabled,
|
disabled,
|
||||||
@@ -39,7 +49,7 @@ class AddEntryToTableField extends React.Component<Props, State> {
|
|||||||
label={fieldLabel}
|
label={fieldLabel}
|
||||||
errorMessage={errorMessage}
|
errorMessage={errorMessage}
|
||||||
onChange={this.handleAddEntryChange}
|
onChange={this.handleAddEntryChange}
|
||||||
validationError={false}
|
validationError={!this.isValid()}
|
||||||
value={this.state.entryToAdd}
|
value={this.state.entryToAdd}
|
||||||
onReturnPressed={this.appendEntry}
|
onReturnPressed={this.appendEntry}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -48,7 +58,7 @@ class AddEntryToTableField extends React.Component<Props, State> {
|
|||||||
<AddButton
|
<AddButton
|
||||||
label={buttonLabel}
|
label={buttonLabel}
|
||||||
action={this.addButtonClicked}
|
action={this.addButtonClicked}
|
||||||
disabled={disabled}
|
disabled={disabled || this.state.entryToAdd ==="" || !this.isValid()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class NavLink extends React.Component<Props> {
|
|||||||
|
|
||||||
let showIcon = null;
|
let showIcon = null;
|
||||||
if (icon) {
|
if (icon) {
|
||||||
showIcon = (<><i className={icon}></i>{" "}</>);
|
showIcon = (<><i className={icon} />{" "}</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -50,8 +50,19 @@ class PrimaryNavigation extends React.Component<Props> {
|
|||||||
|
|
||||||
createNavigationItems = () => {
|
createNavigationItems = () => {
|
||||||
const navigationItems = [];
|
const navigationItems = [];
|
||||||
|
const { t, links } = this.props;
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
links,
|
||||||
|
label: t("primary-navigation.first-menu")
|
||||||
|
};
|
||||||
|
|
||||||
const append = this.createNavigationAppender(navigationItems);
|
const append = this.createNavigationAppender(navigationItems);
|
||||||
|
if (binder.hasExtension("primary-navigation.first-menu", props)) {
|
||||||
|
navigationItems.push(
|
||||||
|
<ExtensionPoint name="primary-navigation.first-menu" props={props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
append("/repos", "/(repo|repos)", "primary-navigation.repositories", "repositories");
|
append("/repos", "/(repo|repos)", "primary-navigation.repositories", "repositories");
|
||||||
append("/users", "/(user|users)", "primary-navigation.users", "users");
|
append("/users", "/(user|users)", "primary-navigation.users", "users");
|
||||||
append("/groups", "/(group|groups)", "primary-navigation.groups", "groups");
|
append("/groups", "/(group|groups)", "primary-navigation.groups", "groups");
|
||||||
|
|||||||
@@ -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<Props> {
|
||||||
|
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 = <ul className="sub-menu">{this.props.children}</ul>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li>
|
||||||
|
<Link className={this.isActive(route) ? "is-active" : ""} to={to}>
|
||||||
|
<i className={defaultIcon} /> {label}
|
||||||
|
</Link>
|
||||||
|
{children}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { to, activeOnlyWhenExact } = this.props;
|
||||||
|
|
||||||
|
// removes last part of url
|
||||||
|
let parents = to.split("/");
|
||||||
|
parents.splice(-1, 1);
|
||||||
|
let parent = parents.join("/");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Route
|
||||||
|
path={parent}
|
||||||
|
exact={activeOnlyWhenExact}
|
||||||
|
children={this.renderLink}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubNavigation;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
export { default as NavAction } from "./NavAction.js";
|
export { default as NavAction } from "./NavAction.js";
|
||||||
export { default as NavLink } from "./NavLink.js";
|
export { default as NavLink } from "./NavLink.js";
|
||||||
export { default as Navigation } from "./Navigation.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 PrimaryNavigation } from "./PrimaryNavigation.js";
|
||||||
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js";
|
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js";
|
||||||
export { default as Section } from "./Section.js";
|
export { default as Section } from "./Section.js";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ChangesetDiff extends React.Component<Props> {
|
|||||||
render() {
|
render() {
|
||||||
const { changeset, t } = this.props;
|
const { changeset, t } = this.props;
|
||||||
if (!this.isDiffSupported(changeset)) {
|
if (!this.isDiffSupported(changeset)) {
|
||||||
return <Notification type="danger">{t("changesets.diff.not-supported")}</Notification>;
|
return <Notification type="danger">{t("changesets.changeset.diffNotSupported")}</Notification>;
|
||||||
} else {
|
} else {
|
||||||
const url = this.createUrl(changeset);
|
const url = this.createUrl(changeset);
|
||||||
return <LoadingDiff url={url} />;
|
return <LoadingDiff url={url} />;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class ChangesetList extends React.Component<Props> {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return <div className="box">{content}</div>;
|
return <>{content}</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"login": {
|
"login": {
|
||||||
"title": "Anmeldung",
|
"title": "Anmeldung",
|
||||||
"subtitle": "Bitte anmelden um fortzufahren.",
|
"subtitle": "Bitte anmelden, um fortzufahren.",
|
||||||
"logo-alt": "SCM-Manager",
|
"logo-alt": "SCM-Manager",
|
||||||
"username-placeholder": "Benutzername",
|
"username-placeholder": "Benutzername",
|
||||||
"password-placeholder": "Passwort",
|
"password-placeholder": "Passwort",
|
||||||
@@ -10,13 +10,13 @@
|
|||||||
"logout": {
|
"logout": {
|
||||||
"error": {
|
"error": {
|
||||||
"title": "Abmeldung fehlgeschlagen",
|
"title": "Abmeldung fehlgeschlagen",
|
||||||
"subtitle": "Während der Abmeldung ist ein Fehler aufgetreten"
|
"subtitle": "Während der Abmeldung ist ein Fehler aufgetreten."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
"error": {
|
"error": {
|
||||||
"title": "Fehler",
|
"title": "Fehler",
|
||||||
"subtitle": "Ein unbekannter Fehler ist aufgetreten"
|
"subtitle": "Ein unbekannter Fehler ist aufgetreten."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error-notification": {
|
"error-notification": {
|
||||||
@@ -43,8 +43,10 @@
|
|||||||
"previous": "Zurück"
|
"previous": "Zurück"
|
||||||
},
|
},
|
||||||
"profile": {
|
"profile": {
|
||||||
"navigation-label": "Navigation",
|
"navigationLabel": "Profil Navigation",
|
||||||
"actions-label": "Aktionen",
|
"informationNavLink": "Information",
|
||||||
|
"changePasswordNavLink": "Passwort ändern",
|
||||||
|
"settingsNavLink": "Einstellungen",
|
||||||
"username": "Benutzername",
|
"username": "Benutzername",
|
||||||
"displayName": "Anzeigename",
|
"displayName": "Anzeigename",
|
||||||
"mail": "E-Mail",
|
"mail": "E-Mail",
|
||||||
@@ -52,21 +54,21 @@
|
|||||||
"information": "Informationen",
|
"information": "Informationen",
|
||||||
"change-password": "Passwort ändern",
|
"change-password": "Passwort ändern",
|
||||||
"error-title": "Fehler",
|
"error-title": "Fehler",
|
||||||
"error-subtitle": "Das Profil kann nicht angezeigt werden",
|
"error-subtitle": "Das Profil kann nicht angezeigt werden.",
|
||||||
"error": "Fehler",
|
"error": "Fehler",
|
||||||
"error-message": "'me' ist nicht definiert"
|
"error-message": "'me' ist nicht definiert"
|
||||||
},
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"label": "Passwort",
|
"label": "Passwort",
|
||||||
"newPassword": "Neues Passwort",
|
"newPassword": "Neues Passwort",
|
||||||
"passwordHelpText": "Plaintext Passwort des Benutzers.",
|
"passwordHelpText": "Klartext Passwort des Benutzers.",
|
||||||
"passwordConfirmHelpText": "Passwort zur Bestätigen wiederholen.",
|
"passwordConfirmHelpText": "Passwort zur Bestätigen wiederholen.",
|
||||||
"currentPassword": "Aktuelles Passwort",
|
"currentPassword": "Aktuelles Passwort",
|
||||||
"currentPasswordHelpText": "Dieses Passwort wird momentan bereits verwendet.",
|
"currentPasswordHelpText": "Dieses Passwort wird momentan bereits verwendet.",
|
||||||
"confirmPassword": "Passwort wiederholen",
|
"confirmPassword": "Passwort wiederholen",
|
||||||
"passwordInvalid": "Das Passwort muss zwischen 6 und 32 Zeichen lang sein",
|
"passwordInvalid": "Das Passwort muss zwischen 6 und 32 Zeichen lang sein!",
|
||||||
"passwordConfirmFailed": "Passwörter müssen identisch sein",
|
"passwordConfirmFailed": "Passwörter müssen identisch sein!",
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
"changedSuccessfully": "Passwort erfolgreich geändert"
|
"changedSuccessfully": "Passwort erfolgreich geändert!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
{
|
{
|
||||||
"config": {
|
"config": {
|
||||||
"navigation-title": "Navigation"
|
"navigationLabel": "Einstellungs Navigation",
|
||||||
},
|
"globalConfigurationNavLink": "Globale Einstellungen",
|
||||||
"global-config": {
|
|
||||||
"title": "Einstellungen",
|
"title": "Einstellungen",
|
||||||
"navigation-label": "Globale Einstellungen",
|
"errorTitle": "Fehler",
|
||||||
"error-title": "Fehler",
|
"errorSubtitle": "Unbekannter Einstellungen Fehler"
|
||||||
"error-subtitle": "Unbekannter Einstellungen Fehler"
|
|
||||||
},
|
},
|
||||||
"config-form": {
|
"config-form": {
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
@@ -47,12 +45,12 @@
|
|||||||
"login-attempt": {
|
"login-attempt": {
|
||||||
"name": "Anmeldeversuche",
|
"name": "Anmeldeversuche",
|
||||||
"login-attempt-limit": "Limit für Anmeldeversuche",
|
"login-attempt-limit": "Limit für Anmeldeversuche",
|
||||||
"login-attempt-limit-timeout": "Timeout bei fehlgeschlagenen Anmeldeversuche"
|
"login-attempt-limit-timeout": "Timeout bei fehlgeschlagenen Anmeldeversuchen"
|
||||||
},
|
},
|
||||||
"general-settings": {
|
"general-settings": {
|
||||||
"realm-description": "Realm Beschreibung",
|
"realm-description": "Realm Beschreibung",
|
||||||
"enable-repository-archive": "Repository Archiv aktivieren",
|
"enable-repository-archive": "Repository Archiv aktivieren",
|
||||||
"disable-grouping-grid": "Gruppen deaktiviern",
|
"disable-grouping-grid": "Gruppen deaktivieren",
|
||||||
"date-format": "Datumsformat",
|
"date-format": "Datumsformat",
|
||||||
"anonymous-access-enabled": "Anonyme Zugriffe erlauben",
|
"anonymous-access-enabled": "Anonyme Zugriffe erlauben",
|
||||||
"skip-failed-authenticators": "Fehlgeschlagene Authentifizierer überspringen",
|
"skip-failed-authenticators": "Fehlgeschlagene Authentifizierer überspringen",
|
||||||
@@ -67,27 +65,27 @@
|
|||||||
"plugin-url-invalid": "Dies ist keine gültige URL"
|
"plugin-url-invalid": "Dies ist keine gültige URL"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"realmDescriptionHelpText": "Beschreibung des authentication realm",
|
"realmDescriptionHelpText": "Beschreibung des Authentication Realm.",
|
||||||
"dateFormatHelpText": "Moments Datumsformat. Zulässige Formate sind in der momentjs Dokumentation beschrieben.",
|
"dateFormatHelpText": "Moments Datumsformat. Zulässige Formate sind in der MomentJS Dokumentation beschrieben.",
|
||||||
"pluginRepositoryHelpText": "Die URL des Plugin Repositories. Beschreibung der Platzhalter: version = SCM-Manager Version; os = Betriebssystem; arch = Architektur",
|
"pluginRepositoryHelpText": "Die URL des Plugin Repositories. Beschreibung der Platzhalter: version = SCM-Manager Version; os = Betriebssystem; arch = Architektur",
|
||||||
"enableForwardingHelpText": "mod_proxy Port Weiterleitung aktivieren.",
|
"enableForwardingHelpText": "mod_proxy Port Weiterleitung aktivieren.",
|
||||||
"enableRepositoryArchiveHelpText": "Repository Archive aktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
"enableRepositoryArchiveHelpText": "Repository Archive aktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
||||||
"disableGroupingGridHelpText": "Repository Gruppen deaktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
"disableGroupingGridHelpText": "Repository Gruppen deaktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
|
||||||
"allowAnonymousAccessHelpText": "Anonyme Benutzer haben Zugriff auf öffentliche Repositories.",
|
"allowAnonymousAccessHelpText": "Anonyme Benutzer haben Zugriff auf öffentliche Repositories.",
|
||||||
"skipFailedAuthenticatorsHelpText": "Die Kette der Authentifikatoren wird nicht beendet wenn ein Authentifikator einen Benutzer findet, ihn aber nicht erfolgreich authentifizieren kann.",
|
"skipFailedAuthenticatorsHelpText": "Die Kette der Authentifikatoren wird nicht beendet, wenn ein Authentifikator einen Benutzer findet, ihn aber nicht erfolgreich authentifizieren kann.",
|
||||||
"adminGroupsHelpText": "Namen von Gruppen mit Admin-Berechtigungen.",
|
"adminGroupsHelpText": "Namen von Gruppen mit Admin-Berechtigungen.",
|
||||||
"adminUsersHelpText": "Namen von Benutzern mit Admin-Berechtigungen.",
|
"adminUsersHelpText": "Namen von Benutzern mit Admin-Berechtigungen.",
|
||||||
"forceBaseUrlHelpText": "Zugriffe, die von einer anderen URL kommen, werden auf die Base URL weiter geleitet.",
|
"forceBaseUrlHelpText": "Zugriffe, die von einer anderen URL kommen, werden auf die Base URL weiter geleitet.",
|
||||||
"baseUrlHelpText": "Die URL der Applikation mit Kontextpfad, z.B. http://localhost:8080/scm",
|
"baseUrlHelpText": "Die URL der Applikation mit Kontextpfad, z.B. http://localhost:8080/scm",
|
||||||
"loginAttemptLimitHelpText": "Maximale Anzahl von Anmeldeversuchen. Durch Verwendung von -1 wird die Begrenzung der Anmeldeversuche deaktiviert.",
|
"loginAttemptLimitHelpText": "Maximale Anzahl von Anmeldeversuchen. Durch Verwendung von -1 wird die Begrenzung der Anmeldeversuche deaktiviert.",
|
||||||
"loginAttemptLimitTimeoutHelpText": "Timeout in Sekunden für Benutzer, die vorübergehend wegen zu vieler fehlgeschlagener Anmeldeversuche deaktiviert wurden.",
|
"loginAttemptLimitTimeoutHelpText": "Timeout in Sekunden für Benutzer, die vorübergehend wegen zu vieler fehlgeschlagener Anmeldeversuche, deaktiviert wurden.",
|
||||||
"enableProxyHelpText": "Proxy aktiviern",
|
"enableProxyHelpText": "Proxy aktivieren",
|
||||||
"proxyPortHelpText": "Der Proxy Port",
|
"proxyPortHelpText": "Der Proxy Port",
|
||||||
"proxyPasswordHelpText": "Das Passwort für die Proxy Server Anmeldung.",
|
"proxyPasswordHelpText": "Das Passwort für die Proxy Server Anmeldung.",
|
||||||
"proxyServerHelpText": "Der Proxy Server",
|
"proxyServerHelpText": "Der Proxy Server",
|
||||||
"proxyUserHelpText": "Der Benutzername für die Proxy Server Anmeldung.",
|
"proxyUserHelpText": "Der Benutzername für die Proxy Server Anmeldung.",
|
||||||
"proxyExcludesHelpText": "Glob patterns für Hostnamen, die von den Proxy-Einstellungen ausgeschlossen werden sollen.",
|
"proxyExcludesHelpText": "Glob patterns für Hostnamen, die von den Proxy-Einstellungen ausgeschlossen werden sollen.",
|
||||||
"enableXsrfProtectionHelpText": "Xsrf Cookie Protection aktivieren. Hinweis: Dieses Feature befindet sich noch im Experimentalstatus.",
|
"enableXsrfProtectionHelpText": "Xsrf Cookie Protection aktivieren. Hinweis: Dieses Feature befindet sich noch im Experimentalstatus.",
|
||||||
"defaultNameSpaceStrategyHelpText": "Die Standardstrategie für Namespaces"
|
"defaultNameSpaceStrategyHelpText": "Die Standardstrategie für Namespaces."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,23 @@
|
|||||||
"title": "Gruppen",
|
"title": "Gruppen",
|
||||||
"subtitle": "Verwaltung der Gruppen"
|
"subtitle": "Verwaltung der Gruppen"
|
||||||
},
|
},
|
||||||
"single-group": {
|
"singleGroup": {
|
||||||
"error-title": "Fehler",
|
"errorTitle": "Fehler",
|
||||||
"error-subtitle": "Unbekannter Gruppen Fehler",
|
"errorSubtitle": "Unbekannter Gruppen Fehler",
|
||||||
"navigation-label": "Navigation",
|
"menu": {
|
||||||
"actions-label": "Aktionen",
|
"navigationLabel": "Gruppen Navigation",
|
||||||
"information-label": "Informationen",
|
"informationNavLink": "Informationen",
|
||||||
"back-label": "Zurück"
|
"settingsNavLink": "Einstellungen",
|
||||||
|
"generalNavLink": "Generell",
|
||||||
|
"setPermissionsNavLink": "Berechtigungen"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"add-group": {
|
"add-group": {
|
||||||
"title": "Gruppe erstellen",
|
"title": "Gruppe erstellen",
|
||||||
"subtitle": "Erstllen einer neuen Gruppe"
|
"subtitle": "Erstellen einer neuen Gruppe"
|
||||||
},
|
},
|
||||||
"create-group-button": {
|
"create-group-button": {
|
||||||
"label": "Erstellen"
|
"label": "Gruppe erstellen"
|
||||||
},
|
},
|
||||||
"edit-group-button": {
|
"edit-group-button": {
|
||||||
"label": "Bearbeiten"
|
"label": "Bearbeiten"
|
||||||
@@ -41,30 +44,28 @@
|
|||||||
},
|
},
|
||||||
"add-member-autocomplete": {
|
"add-member-autocomplete": {
|
||||||
"placeholder": "Benutzername eingeben",
|
"placeholder": "Benutzername eingeben",
|
||||||
"loading": "suche...",
|
"loading": "Suche...",
|
||||||
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
"no-options": "Kein Vorschlag für Benutzername verfügbar"
|
||||||
},
|
},
|
||||||
|
"groupForm": {
|
||||||
"group-form": {
|
"subtitle": "Gruppe bearbeiten",
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
"name-error": "Name ist ungültig",
|
"nameError": "Name ist ungültig",
|
||||||
"description-error": "Beschreibung ist ungültig",
|
"descriptionError": "Beschreibung ist ungültig",
|
||||||
"help": {
|
"help": {
|
||||||
"nameHelpText": "Einzigartiger Name der Gruppe",
|
"nameHelpText": "Eindeutiger Name der Gruppe",
|
||||||
"descriptionHelpText": "Eine kurze Beschreibung der Gruppe",
|
"descriptionHelpText": "Eine kurze Beschreibung der Gruppe",
|
||||||
"memberHelpText": "Benutzername des Mitglieds der Gruppe"
|
"memberHelpText": "Benutzername des Mitglieds der Gruppe"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"delete-group-button": {
|
"deleteGroup": {
|
||||||
"label": "Löschen",
|
"subtitle": "Gruppe löschen",
|
||||||
"confirm-alert": {
|
"button": "Löschen",
|
||||||
|
"confirmAlert": {
|
||||||
"title": "Gruppe löschen",
|
"title": "Gruppe löschen",
|
||||||
"message": "Soll die Gruppe wirklich gelöscht werden?",
|
"message": "Soll die Gruppe wirklich gelöscht werden?",
|
||||||
"submit": "Ja",
|
"submit": "Ja",
|
||||||
"cancel": "Nein"
|
"cancel": "Nein"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"set-permissions-button": {
|
|
||||||
"label": "Berechtigungen ändern"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user