diff --git a/scm-core/src/main/java/sonia/scm/BadRequestException.java b/scm-core/src/main/java/sonia/scm/BadRequestException.java
new file mode 100644
index 0000000000..544ed75a0b
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/BadRequestException.java
@@ -0,0 +1,9 @@
+package sonia.scm;
+
+import java.util.List;
+
+public abstract class BadRequestException extends ExceptionWithContext {
+ public BadRequestException(List context, String message) {
+ super(context, message);
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java b/scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java
similarity index 88%
rename from scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java
rename to scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java
index daf996ee6c..2d64af4318 100644
--- a/scm-core/src/main/java/sonia/scm/NotSupportedFeatureException.java
+++ b/scm-core/src/main/java/sonia/scm/FeatureNotSupportedException.java
@@ -40,13 +40,14 @@ import java.util.Collections;
* @author Sebastian Sdorra
* @version 1.6
*/
-public class NotSupportedFeatureException extends ExceptionWithContext {
+@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here
+public class FeatureNotSupportedException extends BadRequestException {
private static final long serialVersionUID = 256498734456613496L;
private static final String CODE = "9SR8G0kmU1";
- public NotSupportedFeatureException(String feature)
+ public FeatureNotSupportedException(String feature)
{
super(Collections.emptyList(),createMessage(feature));
}
diff --git a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java
index a3e8a1da73..1e9cc3d374 100644
--- a/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java
+++ b/scm-core/src/main/java/sonia/scm/repository/AbstractRepositoryHandler.java
@@ -38,7 +38,7 @@ package sonia.scm.repository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sonia.scm.NotSupportedFeatureException;
+import sonia.scm.FeatureNotSupportedException;
import sonia.scm.SCMContextProvider;
import sonia.scm.event.ScmEventBus;
@@ -167,12 +167,12 @@ public abstract class AbstractRepositoryHandler
*
* @return
*
- * @throws NotSupportedFeatureException
+ * @throws FeatureNotSupportedException
*/
@Override
public ImportHandler getImportHandler()
{
- throw new NotSupportedFeatureException("import");
+ throw new FeatureNotSupportedException("import");
}
/**
diff --git a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java
index cb19cb7f5e..aaa090827a 100644
--- a/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java
+++ b/scm-core/src/main/java/sonia/scm/repository/RepositoryHandler.java
@@ -36,7 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.Handler;
-import sonia.scm.NotSupportedFeatureException;
+import sonia.scm.FeatureNotSupportedException;
import sonia.scm.plugin.ExtensionPoint;
/**
@@ -59,9 +59,9 @@ public interface RepositoryHandler
* @return {@link ImportHandler} for the repository type of this handler
* @since 1.12
*
- * @throws NotSupportedFeatureException
+ * @throws FeatureNotSupportedException
*/
- public ImportHandler getImportHandler() throws NotSupportedFeatureException;
+ public ImportHandler getImportHandler() throws FeatureNotSupportedException;
/**
* Returns informations about the version of the RepositoryHandler.
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java
index 32b633a67c..9e7094d5bf 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/DiffCommandBuilder.java
@@ -38,7 +38,7 @@ package sonia.scm.repository.api;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sonia.scm.NotSupportedFeatureException;
+import sonia.scm.FeatureNotSupportedException;
import sonia.scm.repository.Feature;
import sonia.scm.repository.spi.DiffCommand;
import sonia.scm.repository.spi.DiffCommandRequest;
@@ -203,7 +203,7 @@ public final class DiffCommandBuilder
public DiffCommandBuilder setAncestorChangeset(String revision)
{
if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) {
- throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name());
+ throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name());
}
request.setAncestorChangeset(revision);
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java
index 7b8e172661..917b81391f 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java
@@ -39,7 +39,7 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import sonia.scm.NotSupportedFeatureException;
+import sonia.scm.FeatureNotSupportedException;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.repository.Changeset;
@@ -410,7 +410,7 @@ public final class LogCommandBuilder
*/
public LogCommandBuilder setAncestorChangeset(String ancestorChangeset) {
if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) {
- throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name());
+ throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name());
}
request.setAncestorChangeset(ancestorChangeset);
return this;
diff --git a/scm-core/src/main/java/sonia/scm/security/AccessToken.java b/scm-core/src/main/java/sonia/scm/security/AccessToken.java
index c2a5f4b747..ac7700b030 100644
--- a/scm-core/src/main/java/sonia/scm/security/AccessToken.java
+++ b/scm-core/src/main/java/sonia/scm/security/AccessToken.java
@@ -80,8 +80,20 @@ public interface AccessToken {
*/
Date getExpiration();
+ /**
+ * Returns refresh expiration of token.
+ *
+ * @return refresh expiration
+ */
Optional getRefreshExpiration();
+ /**
+ * Returns id of the parent key.
+ *
+ * @return parent key id
+ */
+ Optional getParentKey();
+
/**
* Returns the scope of the token. The scope is able to reduce the permissions of the subject in the context of this
* token. For example we could issue a token which can only be used to read a single repository. for more informations
diff --git a/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java b/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java
new file mode 100644
index 0000000000..999c693b8f
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java
@@ -0,0 +1,30 @@
+package sonia.scm.security;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Generates cookies and invalidates access token cookies.
+ *
+ * @author Sebastian Sdorra
+ * @since 2.0.0
+ */
+public interface AccessTokenCookieIssuer {
+
+ /**
+ * Creates a cookie for token authentication and attaches it to the response.
+ *
+ * @param request http servlet request
+ * @param response http servlet response
+ * @param accessToken access token
+ */
+ void authenticate(HttpServletRequest request, HttpServletResponse response, AccessToken accessToken);
+ /**
+ * Invalidates the authentication cookie.
+ *
+ * @param request http servlet request
+ * @param response http servlet response
+ */
+ void invalidate(HttpServletRequest request, HttpServletResponse response);
+
+}
diff --git a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java
index b4f0d81cd3..9c1fa590cc 100644
--- a/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java
+++ b/scm-core/src/main/java/sonia/scm/security/DefaultCipherHandler.java
@@ -164,7 +164,7 @@ public class DefaultCipherHandler implements CipherHandler {
String result = null;
try {
- byte[] encodedInput = Base64.getDecoder().decode(value);
+ byte[] encodedInput = Base64.getUrlDecoder().decode(value);
byte[] salt = new byte[SALT_LENGTH];
byte[] encoded = new byte[encodedInput.length - SALT_LENGTH];
@@ -221,7 +221,7 @@ public class DefaultCipherHandler implements CipherHandler {
System.arraycopy(salt, 0, result, 0, SALT_LENGTH);
System.arraycopy(encodedInput, 0, result, SALT_LENGTH,
result.length - SALT_LENGTH);
- res = new String(Base64.getEncoder().encode(result), ENCODING);
+ res = new String(Base64.getUrlEncoder().encode(result), ENCODING);
} catch (IOException | GeneralSecurityException ex) {
throw new CipherException("could not encode string", ex);
}
diff --git a/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java b/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java
index a7f21dd304..bcdc0443ca 100644
--- a/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java
+++ b/scm-core/src/main/java/sonia/scm/store/ConfigurationStore.java
@@ -33,6 +33,10 @@
package sonia.scm.store;
+import java.util.Optional;
+
+import static java.util.Optional.ofNullable;
+
/**
* ConfigurationStore for configuration objects. Note: the default
* implementation use JAXB to marshall the configuration objects.
@@ -50,7 +54,17 @@ public interface ConfigurationStore
*
* @return configuration object from store
*/
- public T get();
+ T get();
+
+ /**
+ * Returns the configuration object from store.
+ *
+ *
+ * @return configuration object from store
+ */
+ default Optional getOptional() {
+ return ofNullable(get());
+ }
//~--- set methods ----------------------------------------------------------
@@ -60,5 +74,5 @@ public interface ConfigurationStore
*
* @param obejct configuration object to store
*/
- public void set(T obejct);
+ void set(T object);
}
diff --git a/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java b/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java
index 9a35cee0e0..c1a8863758 100644
--- a/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java
+++ b/scm-core/src/main/java/sonia/scm/store/MultiEntryStore.java
@@ -32,6 +32,10 @@
package sonia.scm.store;
+import java.util.Optional;
+
+import static java.util.Optional.ofNullable;
+
/**
* Base class for {@link BlobStore} and {@link DataStore}.
*
@@ -67,4 +71,16 @@ public interface MultiEntryStore {
* @return item with the given id
*/
public T get(String id);
+
+ /**
+ * Returns the item with the given id from the store.
+ *
+ *
+ * @param id id of the item to return
+ *
+ * @return item with the given id
+ */
+ default Optional getOptional(String id) {
+ return ofNullable(get(id));
+ }
}
diff --git a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java
index caa35e0b88..b0f8117e82 100644
--- a/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java
+++ b/scm-core/src/main/java/sonia/scm/user/ChangePasswordNotAllowedException.java
@@ -1,12 +1,13 @@
package sonia.scm.user;
+import sonia.scm.BadRequestException;
import sonia.scm.ContextEntry;
-import sonia.scm.ExceptionWithContext;
-public class ChangePasswordNotAllowedException extends ExceptionWithContext {
+@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here
+public class ChangePasswordNotAllowedException extends BadRequestException {
private static final String CODE = "9BR7qpDAe1";
- public static final String WRONG_USER_TYPE = "User of type %s are not allowed to change password";
+ public static final String WRONG_USER_TYPE = "Users of type %s are not allowed to change password";
public ChangePasswordNotAllowedException(ContextEntry.ContextBuilder context, String type) {
super(context.build(), String.format(WRONG_USER_TYPE, type));
diff --git a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java
index 93a6a7c1d1..6f1bfd9954 100644
--- a/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java
+++ b/scm-core/src/main/java/sonia/scm/user/InvalidPasswordException.java
@@ -1,9 +1,10 @@
package sonia.scm.user;
+import sonia.scm.BadRequestException;
import sonia.scm.ContextEntry;
-import sonia.scm.ExceptionWithContext;
-public class InvalidPasswordException extends ExceptionWithContext {
+@SuppressWarnings("squid:MaximumInheritanceDepth") // exceptions have a deep inheritance depth themselves; therefore we accept this here
+public class InvalidPasswordException extends BadRequestException {
private static final String CODE = "8YR7aawFW1";
diff --git a/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java b/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java
new file mode 100644
index 0000000000..2cb4674d24
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/web/AbstractRepositoryJsonEnricher.java
@@ -0,0 +1,40 @@
+package sonia.scm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static java.util.Collections.singletonMap;
+import static sonia.scm.web.VndMediaType.REPOSITORY;
+import static sonia.scm.web.VndMediaType.REPOSITORY_COLLECTION;
+
+public abstract class AbstractRepositoryJsonEnricher extends JsonEnricherBase {
+
+ public AbstractRepositoryJsonEnricher(ObjectMapper objectMapper) {
+ super(objectMapper);
+ }
+
+ @Override
+ public void enrich(JsonEnricherContext context) {
+ if (resultHasMediaType(REPOSITORY, context)) {
+ JsonNode repositoryNode = context.getResponseEntity();
+ enrichRepositoryNode(repositoryNode);
+ } else if (resultHasMediaType(REPOSITORY_COLLECTION, context)) {
+ JsonNode repositoryCollectionNode = context.getResponseEntity().get("_embedded").withArray("repositories");
+ repositoryCollectionNode.elements().forEachRemaining(this::enrichRepositoryNode);
+ }
+ }
+
+ private void enrichRepositoryNode(JsonNode repositoryNode) {
+ String namespace = repositoryNode.get("namespace").asText();
+ String name = repositoryNode.get("name").asText();
+
+ enrichRepositoryNode(repositoryNode, namespace, name);
+ }
+
+ protected abstract void enrichRepositoryNode(JsonNode repositoryNode, String namespace, String name);
+
+ protected void addLink(JsonNode repositoryNode, String linkName, String link) {
+ JsonNode hrefNode = createObject(singletonMap("href", value(link)));
+ addPropertyNode(repositoryNode.get("_links"), linkName, hrefNode);
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java b/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java
new file mode 100644
index 0000000000..9b8d718851
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java
@@ -0,0 +1,25 @@
+package sonia.scm.xml;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
+
+/**
+ * JAXB adapter for {@link Instant} objects.
+ *
+ * @since 2.0.0
+ */
+public class XmlInstantAdapter extends XmlAdapter {
+
+ @Override
+ public String marshal(Instant instant) {
+ return DateTimeFormatter.ISO_INSTANT.format(instant);
+ }
+
+ @Override
+ public Instant unmarshal(String text) {
+ TemporalAccessor parsed = DateTimeFormatter.ISO_INSTANT.parse(text);
+ return Instant.from(parsed);
+ }
+}
diff --git a/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java b/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java
new file mode 100644
index 0000000000..2c8ef76464
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/web/AbstractRepositoryJsonEnricherTest.java
@@ -0,0 +1,107 @@
+package sonia.scm.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.io.Resources;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import sonia.scm.api.v2.resources.ScmPathInfoStore;
+
+import javax.ws.rs.core.MediaType;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URL;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(MockitoExtension.class)
+class AbstractRepositoryJsonEnricherTest {
+
+ private final ObjectMapper objectMapper = new ObjectMapper();
+ private AbstractRepositoryJsonEnricher linkEnricher;
+ private JsonNode rootNode;
+
+ @BeforeEach
+ void globalSetUp() {
+ ScmPathInfoStore pathInfoStore = new ScmPathInfoStore();
+ pathInfoStore.set(() -> URI.create("/"));
+
+ linkEnricher = new AbstractRepositoryJsonEnricher(objectMapper) {
+ @Override
+ protected void enrichRepositoryNode(JsonNode repositoryNode, String namespace, String name) {
+ addLink(repositoryNode, "new-link", "/somewhere");
+ }
+ };
+ }
+
+ @Test
+ void shouldEnrichRepositories() throws IOException {
+ URL resource = Resources.getResource("sonia/scm/repository/repository-001.json");
+ rootNode = objectMapper.readTree(resource);
+
+ JsonEnricherContext context = new JsonEnricherContext(
+ URI.create("/"),
+ MediaType.valueOf(VndMediaType.REPOSITORY),
+ rootNode
+ );
+
+ linkEnricher.enrich(context);
+
+ String configLink = context.getResponseEntity()
+ .get("_links")
+ .get("new-link")
+ .get("href")
+ .asText();
+
+ assertThat(configLink).isEqualTo("/somewhere");
+ }
+
+ @Test
+ void shouldEnrichAllRepositories() throws IOException {
+ URL resource = Resources.getResource("sonia/scm/repository/repository-collection-001.json");
+ rootNode = objectMapper.readTree(resource);
+
+ JsonEnricherContext context = new JsonEnricherContext(
+ URI.create("/"),
+ MediaType.valueOf(VndMediaType.REPOSITORY_COLLECTION),
+ rootNode
+ );
+
+ linkEnricher.enrich(context);
+
+ context.getResponseEntity()
+ .get("_embedded")
+ .withArray("repositories")
+ .elements()
+ .forEachRemaining(node -> {
+ String configLink = node
+ .get("_links")
+ .get("new-link")
+ .get("href")
+ .asText();
+
+ assertThat(configLink).isEqualTo("/somewhere");
+ });
+ }
+
+ @Test
+ void shouldNotModifyObjectsWithUnsupportedMediaType() throws IOException {
+ URL resource = Resources.getResource("sonia/scm/repository/repository-001.json");
+ rootNode = objectMapper.readTree(resource);
+ JsonEnricherContext context = new JsonEnricherContext(
+ URI.create("/"),
+ MediaType.valueOf(VndMediaType.USER),
+ rootNode
+ );
+
+ linkEnricher.enrich(context);
+
+ boolean hasNewPullRequestLink = context.getResponseEntity()
+ .get("_links")
+ .has("new-link");
+
+ assertThat(hasNewPullRequestLink).isFalse();
+ }
+}
diff --git a/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java b/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java
new file mode 100644
index 0000000000..eb1ea86aee
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/xml/XmlInstantAdapterTest.java
@@ -0,0 +1,47 @@
+package sonia.scm.xml;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junitpioneer.jupiter.TempDirectory;
+
+import javax.xml.bind.JAXB;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import java.nio.file.Path;
+import java.time.Instant;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+@ExtendWith(TempDirectory.class)
+class XmlInstantAdapterTest {
+
+ @Test
+ void shouldMarshalAndUnmarshalInstant(@TempDirectory.TempDir Path tempDirectory) {
+ Path path = tempDirectory.resolve("instant.xml");
+
+ Instant instant = Instant.now();
+ InstantObject object = new InstantObject(instant);
+ JAXB.marshal(object, path.toFile());
+
+ InstantObject unmarshaled = JAXB.unmarshal(path.toFile(), InstantObject.class);
+ assertEquals(instant, unmarshaled.instant);
+ }
+
+ @XmlRootElement(name = "instant-object")
+ @XmlAccessorType(XmlAccessType.FIELD)
+ public static class InstantObject {
+
+ @XmlJavaTypeAdapter(XmlInstantAdapter.class)
+ private Instant instant;
+
+ public InstantObject() {
+ }
+
+ InstantObject(Instant instant) {
+ this.instant = instant;
+ }
+ }
+
+}
diff --git a/scm-core/src/test/resources/sonia/scm/repository/repository-001.json b/scm-core/src/test/resources/sonia/scm/repository/repository-001.json
new file mode 100644
index 0000000000..43ea136942
--- /dev/null
+++ b/scm-core/src/test/resources/sonia/scm/repository/repository-001.json
@@ -0,0 +1,42 @@
+{
+ "creationDate": "2018-11-09T09:48:32.732Z",
+ "description": "Handling static webresources made easy",
+ "healthCheckFailures": [],
+ "lastModified": "2018-11-09T09:49:20.973Z",
+ "namespace": "scmadmin",
+ "name": "web-resources",
+ "archived": false,
+ "type": "git",
+ "_links": {
+ "self": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "delete": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "update": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "permissions": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/"
+ },
+ "protocol": [
+ {
+ "href": "http://localhost:8081/scm/repo/scmadmin/web-resources",
+ "name": "http"
+ }
+ ],
+ "tags": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/"
+ },
+ "branches": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/"
+ },
+ "changesets": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/"
+ },
+ "sources": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/"
+ }
+ }
+}
diff --git a/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json b/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json
new file mode 100644
index 0000000000..f4eeb24bbc
--- /dev/null
+++ b/scm-core/src/test/resources/sonia/scm/repository/repository-collection-001.json
@@ -0,0 +1,106 @@
+{
+ "page": 0,
+ "pageTotal": 1,
+ "_links": {
+ "self": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10"
+ },
+ "first": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10"
+ },
+ "last": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/?page=0&pageSize=10"
+ },
+ "create": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/"
+ }
+ },
+ "_embedded": {
+ "repositories": [
+ {
+ "creationDate": "2018-11-09T09:48:32.732Z",
+ "description": "Handling static webresources made easy",
+ "healthCheckFailures": [],
+ "lastModified": "2018-11-09T09:49:20.973Z",
+ "namespace": "scmadmin",
+ "name": "web-resources",
+ "archived": false,
+ "type": "git",
+ "_links": {
+ "self": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "delete": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "update": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "permissions": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/"
+ },
+ "protocol": [
+ {
+ "href": "http://localhost:8081/scm/repo/scmadmin/web-resources",
+ "name": "http"
+ }
+ ],
+ "tags": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/"
+ },
+ "branches": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/"
+ },
+ "changesets": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/"
+ },
+ "sources": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/"
+ }
+ }
+ },
+ {
+ "creationDate": "2018-11-09T09:48:32.732Z",
+ "description": "Handling static webresources made easy",
+ "healthCheckFailures": [],
+ "lastModified": "2018-11-09T09:49:20.973Z",
+ "namespace": "scmadmin",
+ "name": "web-resources",
+ "archived": false,
+ "type": "git",
+ "_links": {
+ "self": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "delete": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "update": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources"
+ },
+ "permissions": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/permissions/"
+ },
+ "protocol": [
+ {
+ "href": "http://localhost:8081/scm/repo/scmadmin/web-resources",
+ "name": "http"
+ }
+ ],
+ "tags": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/tags/"
+ },
+ "branches": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/branches/"
+ },
+ "changesets": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/changesets/"
+ },
+ "sources": {
+ "href": "http://localhost:8081/scm/api/v2/repositories/scmadmin/web-resources/sources/"
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java
index 099ab53baa..d37a150723 100644
--- a/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java
+++ b/scm-dao-xml/src/main/java/sonia/scm/store/FileBasedStoreFactory.java
@@ -58,8 +58,6 @@ public abstract class FileBasedStoreFactory {
private RepositoryLocationResolver repositoryLocationResolver;
private Store store;
- private File storeDirectory;
-
protected FileBasedStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, Store store) {
this.contextProvider = contextProvider;
this.repositoryLocationResolver = repositoryLocationResolver;
@@ -75,17 +73,16 @@ public abstract class FileBasedStoreFactory {
}
protected File getStoreLocation(String name, Class type, Repository repository) {
- if (storeDirectory == null) {
- if (repository != null) {
- LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName());
- storeDirectory = this.getStoreDirectory(store, repository);
- } else {
- LOG.debug("create store with type: {} and name: {} ", type, name);
- storeDirectory = this.getStoreDirectory(store);
- }
- IOUtil.mkdirs(storeDirectory);
+ File storeDirectory;
+ if (repository != null) {
+ LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName());
+ storeDirectory = this.getStoreDirectory(store, repository);
+ } else {
+ LOG.debug("create store with type: {} and name: {} ", type, name);
+ storeDirectory = this.getStoreDirectory(store);
}
- return new File(this.storeDirectory, name);
+ IOUtil.mkdirs(storeDirectory);
+ return new File(storeDirectory, name);
}
/**
diff --git a/scm-plugins/scm-git-plugin/package.json b/scm-plugins/scm-git-plugin/package.json
index b617351264..3145b6a338 100644
--- a/scm-plugins/scm-git-plugin/package.json
+++ b/scm-plugins/scm-git-plugin/package.json
@@ -12,6 +12,6 @@
"@scm-manager/ui-extensions": "^0.1.1"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22"
+ "@scm-manager/ui-bundler": "^0.0.24"
}
}
diff --git a/scm-plugins/scm-git-plugin/yarn.lock b/scm-plugins/scm-git-plugin/yarn.lock
index 471beb513e..234ed65102 100644
--- a/scm-plugins/scm-git-plugin/yarn.lock
+++ b/scm-plugins/scm-git-plugin/yarn.lock
@@ -707,9 +707,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-plugins/scm-hg-plugin/package.json b/scm-plugins/scm-hg-plugin/package.json
index 6bccf3bd96..0638a464de 100644
--- a/scm-plugins/scm-hg-plugin/package.json
+++ b/scm-plugins/scm-hg-plugin/package.json
@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.1.1"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22"
+ "@scm-manager/ui-bundler": "^0.0.24"
}
}
diff --git a/scm-plugins/scm-hg-plugin/yarn.lock b/scm-plugins/scm-hg-plugin/yarn.lock
index bc53e0a7ce..0666ef408d 100644
--- a/scm-plugins/scm-hg-plugin/yarn.lock
+++ b/scm-plugins/scm-hg-plugin/yarn.lock
@@ -641,9 +641,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-plugins/scm-svn-plugin/package.json b/scm-plugins/scm-svn-plugin/package.json
index 3e509172fd..e51f3b9bfd 100644
--- a/scm-plugins/scm-svn-plugin/package.json
+++ b/scm-plugins/scm-svn-plugin/package.json
@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.1.1"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22"
+ "@scm-manager/ui-bundler": "^0.0.24"
}
}
diff --git a/scm-plugins/scm-svn-plugin/yarn.lock b/scm-plugins/scm-svn-plugin/yarn.lock
index bc53e0a7ce..0666ef408d 100644
--- a/scm-plugins/scm-svn-plugin/yarn.lock
+++ b/scm-plugins/scm-svn-plugin/yarn.lock
@@ -641,9 +641,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java
index 2c5641bfd1..2180afdca2 100644
--- a/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java
+++ b/scm-test/src/main/java/sonia/scm/store/InMemoryConfigurationStoreFactory.java
@@ -42,8 +42,20 @@ package sonia.scm.store;
*/
public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory {
+ private ConfigurationStore store;
+
+ public InMemoryConfigurationStoreFactory() {
+ }
+
+ public InMemoryConfigurationStoreFactory(ConfigurationStore store) {
+ this.store = store;
+ }
+
@Override
public ConfigurationStore getStore(TypedStoreParameters storeParameters) {
+ if (store != null) {
+ return store;
+ }
return new InMemoryConfigurationStore<>();
}
}
diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java
new file mode 100644
index 0000000000..06198d89bf
--- /dev/null
+++ b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStore.java
@@ -0,0 +1,53 @@
+package sonia.scm.store;
+
+import sonia.scm.security.KeyGenerator;
+import sonia.scm.security.UUIDKeyGenerator;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * In memory store implementation of {@link DataStore}.
+ *
+ * @author Sebastian Sdorra
+ *
+ * @param type of stored object
+ */
+public class InMemoryDataStore implements DataStore {
+
+ private final Map store = new HashMap<>();
+ private KeyGenerator generator = new UUIDKeyGenerator();
+
+ @Override
+ public String put(T item) {
+ String key = generator.createKey();
+ store.put(key, item);
+ return key;
+ }
+
+ @Override
+ public void put(String id, T item) {
+ store.put(id, item);
+ }
+
+ @Override
+ public Map getAll() {
+ return Collections.unmodifiableMap(store);
+ }
+
+ @Override
+ public void clear() {
+ store.clear();
+ }
+
+ @Override
+ public void remove(String id) {
+ store.remove(id);
+ }
+
+ @Override
+ public T get(String id) {
+ return store.get(id);
+ }
+}
diff --git a/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java
new file mode 100644
index 0000000000..b0e95e9f9c
--- /dev/null
+++ b/scm-test/src/main/java/sonia/scm/store/InMemoryDataStoreFactory.java
@@ -0,0 +1,26 @@
+package sonia.scm.store;
+
+/**
+ * In memory configuration store factory for testing purposes.
+ *
+ * @author Sebastian Sdorra
+ */
+public class InMemoryDataStoreFactory implements DataStoreFactory {
+
+ private InMemoryDataStore store;
+
+ public InMemoryDataStoreFactory() {
+ }
+
+ public InMemoryDataStoreFactory(InMemoryDataStore store) {
+ this.store = store;
+ }
+
+ @Override
+ public DataStore getStore(TypedStoreParameters storeParameters) {
+ if (store != null) {
+ return store;
+ }
+ return new InMemoryDataStore<>();
+ }
+}
diff --git a/scm-ui-components/packages/ui-components/package.json b/scm-ui-components/packages/ui-components/package.json
index 880d8f3891..06e007e871 100644
--- a/scm-ui-components/packages/ui-components/package.json
+++ b/scm-ui-components/packages/ui-components/package.json
@@ -14,7 +14,7 @@
"eslint-fix": "eslint src --fix"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22",
+ "@scm-manager/ui-bundler": "^0.0.24",
"create-index": "^2.3.0",
"enzyme": "^3.5.0",
"enzyme-adapter-react-16": "^1.3.1",
@@ -35,7 +35,8 @@
"react-i18next": "^7.11.0",
"react-jss": "^8.6.1",
"react-router-dom": "^4.3.1",
- "react-select": "^2.1.2"
+ "react-select": "^2.1.2",
+ "diff2html": "^2.5.0"
},
"browserify": {
"transform": [
diff --git a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js
index 960fe7db21..1b2b37bb19 100644
--- a/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js
+++ b/scm-ui-components/packages/ui-components/src/config/ConfigurationBinder.js
@@ -63,8 +63,9 @@ class ConfigurationBinder {
// route for global configuration, passes the current repository to component
- const RepoRoute = ({ url, repository }) => {
- return this.route(url + to, );
+ const RepoRoute = ({url, repository}) => {
+ const link = repository._links[linkName].href
+ return this.route(url + to, );
};
// bind config route to extension point
diff --git a/scm-ui-components/packages/ui-components/src/repos/Diff.js b/scm-ui-components/packages/ui-components/src/repos/Diff.js
new file mode 100644
index 0000000000..0b0b31a3d4
--- /dev/null
+++ b/scm-ui-components/packages/ui-components/src/repos/Diff.js
@@ -0,0 +1,36 @@
+//@flow
+import React from "react";
+import { Diff2Html } from "diff2html";
+
+type Props = {
+ diff: string,
+ sideBySide: boolean
+};
+
+class Diff extends React.Component {
+
+ static defaultProps = {
+ sideBySide: false
+ };
+
+ render() {
+ const { diff, sideBySide } = this.props;
+
+ const options = {
+ inputFormat: "diff",
+ outputFormat: sideBySide ? "side-by-side" : "line-by-line",
+ showFiles: false,
+ matching: "lines"
+ };
+
+ const outputHtml = Diff2Html.getPrettyHtml(diff, options);
+
+ return (
+ // eslint-disable-next-line react/no-danger
+
+ );
+ }
+
+}
+
+export default Diff;
diff --git a/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js b/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js
new file mode 100644
index 0000000000..5f6330f0e5
--- /dev/null
+++ b/scm-ui-components/packages/ui-components/src/repos/LoadingDiff.js
@@ -0,0 +1,64 @@
+//@flow
+import React from "react";
+import { apiClient } from "../apiclient";
+import ErrorNotification from "../ErrorNotification";
+import Loading from "../Loading";
+import Diff from "./Diff";
+
+type Props = {
+ url: string,
+ sideBySide: boolean
+};
+
+type State = {
+ diff?: string,
+ loading: boolean,
+ error?: Error
+};
+
+class LoadingDiff extends React.Component {
+
+ static defaultProps = {
+ sideBySide: false
+ };
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ loading: true
+ };
+ }
+
+ componentDidMount() {
+ const { url } = this.props;
+ apiClient
+ .get(url)
+ .then(response => response.text())
+ .then(text => {
+ this.setState({
+ loading: false,
+ diff: text
+ });
+ })
+ .catch(error => {
+ this.setState({
+ loading: false,
+ error
+ });
+ });
+ }
+
+ render() {
+ const { diff, loading, error } = this.state;
+ if (error) {
+ return ;
+ } else if (loading || !diff) {
+ return ;
+ } else {
+ return ;
+ }
+ }
+
+}
+
+export default LoadingDiff;
diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js
new file mode 100644
index 0000000000..857ff8c827
--- /dev/null
+++ b/scm-ui-components/packages/ui-components/src/repos/changesets/ChangesetDiff.js
@@ -0,0 +1,37 @@
+//@flow
+import React from "react";
+import type { Changeset } from "@scm-manager/ui-types";
+import LoadingDiff from "../LoadingDiff";
+import Notification from "../../Notification";
+import {translate} from "react-i18next";
+
+type Props = {
+ changeset: Changeset,
+
+ // context props
+ t: string => string
+};
+
+class ChangesetDiff extends React.Component {
+
+ isDiffSupported(changeset: Changeset) {
+ return !!changeset._links.diff;
+ }
+
+ createUrl(changeset: Changeset) {
+ return changeset._links.diff.href + "?format=GIT";
+ }
+
+ render() {
+ const { changeset, t } = this.props;
+ if (!this.isDiffSupported(changeset)) {
+ return {t("changesets.diff.not-supported")};
+ } else {
+ const url = this.createUrl(changeset);
+ return ;
+ }
+ }
+
+}
+
+export default translate("repos")(ChangesetDiff);
diff --git a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js
index 9651978b35..0e7a5e533d 100644
--- a/scm-ui-components/packages/ui-components/src/repos/changesets/index.js
+++ b/scm-ui-components/packages/ui-components/src/repos/changesets/index.js
@@ -7,3 +7,4 @@ export { default as ChangesetId } from "./ChangesetId";
export { default as ChangesetList } from "./ChangesetList";
export { default as ChangesetRow } from "./ChangesetRow";
export { default as ChangesetTag } from "./ChangesetTag";
+export { default as ChangesetDiff } from "./ChangesetDiff";
diff --git a/scm-ui-components/packages/ui-components/src/repos/index.js b/scm-ui-components/packages/ui-components/src/repos/index.js
index 05ae15d10d..9ebd1e9c55 100644
--- a/scm-ui-components/packages/ui-components/src/repos/index.js
+++ b/scm-ui-components/packages/ui-components/src/repos/index.js
@@ -1,3 +1,5 @@
// @flow
export * from "./changesets";
+export { default as Diff } from "./Diff";
+export { default as LoadingDiff } from "./LoadingDiff";
diff --git a/scm-ui-components/packages/ui-components/yarn.lock b/scm-ui-components/packages/ui-components/yarn.lock
index 743e46f123..062bb75ab1 100644
--- a/scm-ui-components/packages/ui-components/yarn.lock
+++ b/scm-ui-components/packages/ui-components/yarn.lock
@@ -687,9 +687,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
@@ -2444,7 +2444,16 @@ dev-ip@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
-diff@^3.2.0:
+diff2html@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.5.0.tgz#2d16f1a8f115354733b16b0264a594fa7db98aa2"
+ dependencies:
+ diff "^3.5.0"
+ hogan.js "^3.0.2"
+ lodash "^4.17.11"
+ whatwg-fetch "^3.0.0"
+
+diff@^3.2.0, diff@^3.5.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -3858,6 +3867,13 @@ hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+hogan.js@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd"
+ dependencies:
+ mkdirp "0.3.0"
+ nopt "1.0.10"
+
hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
@@ -5258,7 +5274,7 @@ lodash.templatesettings@^3.0.0:
lodash._reinterpolate "^3.0.0"
lodash.escape "^3.0.0"
-lodash@^4.13.1, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5:
+lodash@^4.13.1, lodash@^4.15.0, lodash@^4.16.6, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
@@ -5518,6 +5534,10 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
+mkdirp@0.3.0:
+ version "0.3.0"
+ resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
+
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -5696,6 +5716,12 @@ nomnom@~1.6.2:
colors "0.5.x"
underscore "~1.4.4"
+nopt@1.0.10, nopt@~1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+ dependencies:
+ abbrev "1"
+
nopt@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
@@ -5703,12 +5729,6 @@ nopt@^4.0.1:
abbrev "1"
osenv "^0.1.4"
-nopt@~1.0.10:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
- dependencies:
- abbrev "1"
-
normalize-package-data@^2.3.2:
version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
@@ -8040,6 +8060,10 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
dependencies:
iconv-lite "0.4.24"
+whatwg-fetch@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
+
whatwg-mimetype@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"
diff --git a/scm-ui-components/packages/ui-types/package.json b/scm-ui-components/packages/ui-types/package.json
index 8a009d314c..4d87265379 100644
--- a/scm-ui-components/packages/ui-types/package.json
+++ b/scm-ui-components/packages/ui-types/package.json
@@ -14,7 +14,7 @@
"check": "flow check"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22"
+ "@scm-manager/ui-bundler": "^0.0.24"
},
"browserify": {
"transform": [
diff --git a/scm-ui-components/packages/ui-types/yarn.lock b/scm-ui-components/packages/ui-types/yarn.lock
index a19d99dfbf..ee367343ee 100644
--- a/scm-ui-components/packages/ui-types/yarn.lock
+++ b/scm-ui-components/packages/ui-types/yarn.lock
@@ -707,9 +707,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
diff --git a/scm-ui/package.json b/scm-ui/package.json
index 6fd0febd9c..2f68b75317 100644
--- a/scm-ui/package.json
+++ b/scm-ui/package.json
@@ -11,7 +11,7 @@
"bulma": "^0.7.1",
"bulma-tooltip": "^2.0.2",
"classnames": "^2.2.5",
- "diff2html": "^2.4.0",
+ "diff2html": "^2.5.0",
"font-awesome": "^4.7.0",
"history": "^4.7.2",
"i18next": "^11.4.0",
@@ -21,7 +21,6 @@
"node-sass": "^4.9.3",
"postcss-easy-import": "^3.0.0",
"react": "^16.4.2",
- "react-diff-view": "^1.7.0",
"react-dom": "^16.4.2",
"react-i18next": "^7.9.0",
"react-jss": "^8.6.0",
@@ -52,7 +51,7 @@
"pre-commit": "jest && flow && eslint src"
},
"devDependencies": {
- "@scm-manager/ui-bundler": "^0.0.22",
+ "@scm-manager/ui-bundler": "^0.0.24",
"concat": "^1.0.3",
"copyfiles": "^2.0.0",
"enzyme": "^3.3.0",
diff --git a/scm-ui/public/locales/en/repos.json b/scm-ui/public/locales/en/repos.json
index 8296b46907..d6cdaa1d8d 100644
--- a/scm-ui/public/locales/en/repos.json
+++ b/scm-ui/public/locales/en/repos.json
@@ -66,6 +66,9 @@
}
},
"changesets": {
+ "diff": {
+ "not-supported": "Diff of changesets is not supported by the type of repository"
+ },
"error-title": "Error",
"error-subtitle": "Could not fetch changesets",
"changeset": {
diff --git a/scm-ui/src/repos/components/changesets/ChangesetDetails.js b/scm-ui/src/repos/components/changesets/ChangesetDetails.js
index 2f6c4b410e..483042a779 100644
--- a/scm-ui/src/repos/components/changesets/ChangesetDetails.js
+++ b/scm-ui/src/repos/components/changesets/ChangesetDetails.js
@@ -9,14 +9,14 @@ import {
ChangesetId,
ChangesetTag,
ChangesetAuthor,
+ ChangesetDiff,
AvatarWrapper,
AvatarImage,
- changesets
+ changesets,
} from "@scm-manager/ui-components";
import classNames from "classnames";
import type { Tag } from "@scm-manager/ui-types";
-import ScmDiff from "../../containers/ScmDiff";
const styles = {
spacing: {
@@ -78,7 +78,7 @@ class ChangesetDetails extends React.Component {
-
+
);
diff --git a/scm-ui/src/repos/containers/BranchSelector.js b/scm-ui/src/repos/containers/BranchSelector.js
index 2183e13b69..ced1cb8f44 100644
--- a/scm-ui/src/repos/containers/BranchSelector.js
+++ b/scm-ui/src/repos/containers/BranchSelector.js
@@ -11,6 +11,9 @@ import classNames from "classnames";
const styles = {
zeroflex: {
flexGrow: 0
+ },
+ minWidthOfLabel: {
+ minWidth: "4.5rem"
}
};
@@ -45,7 +48,12 @@ class BranchSelector extends React.Component {
return (
diff --git a/scm-ui/src/repos/containers/RepositoryRoot.js b/scm-ui/src/repos/containers/RepositoryRoot.js
index 81de0296fc..a3f69fe70b 100644
--- a/scm-ui/src/repos/containers/RepositoryRoot.js
+++ b/scm-ui/src/repos/containers/RepositoryRoot.js
@@ -114,7 +114,7 @@ class RepositoryRoot extends React.Component
{
return (
-
+
{
- constructor(props: Props) {
- super(props);
- this.state = { diff: "" };
- }
-
- componentDidMount() {
- const { changeset } = this.props;
- const url = changeset._links.diff.href+"?format=GIT";
- apiClient
- .get(url)
- .then(response => response.text())
- .then(text => this.setState({ ...this.state, diff: text }))
- .catch(error => this.setState({ ...this.state, error }));
- }
-
- render() {
- const options = {
- inputFormat: "diff",
- outputFormat: this.props.sideBySide ? "side-by-side" : "line-by-line",
- showFiles: false,
- matching: "lines"
- };
-
- const outputHtml = Diff2Html.getPrettyHtml(this.state.diff, options);
-
- return (
- // eslint-disable-next-line react/no-danger
-
- );
- }
-}
-
-export default ScmDiff;
diff --git a/scm-ui/src/repos/sources/components/FileTree.js b/scm-ui/src/repos/sources/components/FileTree.js
index 1dd11870ae..cbe03df62f 100644
--- a/scm-ui/src/repos/sources/components/FileTree.js
+++ b/scm-ui/src/repos/sources/components/FileTree.js
@@ -119,7 +119,9 @@ class FileTree extends React.Component {
|
{t("sources.file-tree.lastModified")}
|
- {t("sources.file-tree.description")} |
+
+ {t("sources.file-tree.description")}
+ |
diff --git a/scm-ui/src/repos/sources/components/FileTreeLeaf.js b/scm-ui/src/repos/sources/components/FileTreeLeaf.js
index b4e2ad59ea..20905a8354 100644
--- a/scm-ui/src/repos/sources/components/FileTreeLeaf.js
+++ b/scm-ui/src/repos/sources/components/FileTreeLeaf.js
@@ -6,10 +6,14 @@ import FileSize from "./FileSize";
import FileIcon from "./FileIcon";
import { Link } from "react-router-dom";
import type { File } from "@scm-manager/ui-types";
+import classNames from "classnames";
const styles = {
iconColumn: {
width: "16px"
+ },
+ wordBreakMinWidth: {
+ minWidth: "10em"
}
};
@@ -71,12 +75,14 @@ class FileTreeLeaf extends React.Component {
return (
| {this.createFileIcon(file)} |
- {this.createFileName(file)} |
+ {this.createFileName(file)} |
{fileSize} |
|
- {file.description} |
+
+ {file.description}
+ |
);
}
diff --git a/scm-ui/src/repos/sources/containers/Content.js b/scm-ui/src/repos/sources/containers/Content.js
index 34d024b2a2..4565746999 100644
--- a/scm-ui/src/repos/sources/containers/Content.js
+++ b/scm-ui/src/repos/sources/containers/Content.js
@@ -41,6 +41,13 @@ const styles = {
isVerticalCenter: {
display: "flex",
alignItems: "center"
+ },
+ wordBreak: {
+ WebkitHyphens: "auto",
+ MozHyphens: "auto",
+ MsHyphens: "auto",
+ hypens: "auto",
+ wordBreak: "break-all",
}
};
@@ -93,7 +100,7 @@ class Content extends React.Component {
classes.marginInHeader
)}
/>
- {file.name}
+ {file.name}
{selector}
@@ -125,11 +132,11 @@ class Content extends React.Component {
| {t("sources.content.path")} |
- {file.path} |
+ {file.path} |
| {t("sources.content.branch")} |
- {revision} |
+ {revision} |
| {t("sources.content.size")} |
@@ -141,7 +148,7 @@ class Content extends React.Component {
| {t("sources.content.description")} |
- {description} |
+ {description} |
diff --git a/scm-ui/styles/scm.scss b/scm-ui/styles/scm.scss
index 4d306df6ad..8937ae3068 100644
--- a/scm-ui/styles/scm.scss
+++ b/scm-ui/styles/scm.scss
@@ -27,6 +27,14 @@ $blue: #33B2E8;
padding: 0 0 0 3.8em !important;
}
+.is-word-break {
+ -webkit-hyphens: auto;
+ -moz-hyphens: auto;
+ -ms-hyphens: auto;
+ hyphens: auto;
+ word-break: break-all;
+}
+
.main {
min-height: calc(100vh - 260px);
}
diff --git a/scm-ui/yarn.lock b/scm-ui/yarn.lock
index 6aeb0f7703..3ddf27be96 100644
--- a/scm-ui/yarn.lock
+++ b/scm-ui/yarn.lock
@@ -698,9 +698,9 @@
version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
-"@scm-manager/ui-bundler@^0.0.22":
- version "0.0.22"
- resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a"
+"@scm-manager/ui-bundler@^0.0.24":
+ version "0.0.24"
+ resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies:
"@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0"
@@ -1925,7 +1925,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-classnames@^2.2.5, classnames@^2.2.6:
+classnames@^2.2.5:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
@@ -2549,14 +2549,14 @@ dev-ip@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
-diff2html@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.4.0.tgz#de632384eefa5a7f6b0e92eafb1fa25d22dc88ab"
+diff2html@^2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.5.0.tgz#2d16f1a8f115354733b16b0264a594fa7db98aa2"
dependencies:
diff "^3.5.0"
hogan.js "^3.0.2"
- lodash "^4.17.10"
- whatwg-fetch "^2.0.4"
+ lodash "^4.17.11"
+ whatwg-fetch "^3.0.0"
diff@^3.2.0, diff@^3.5.0:
version "3.5.0"
@@ -3604,10 +3604,6 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-gitdiff-parser@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/gitdiff-parser/-/gitdiff-parser-0.1.2.tgz#26a256e05e9c2d5016b512a96c1dacb40862b92a"
-
glob-base@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -5481,10 +5477,6 @@ lodash.escape@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
-lodash.findlastindex@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.findlastindex/-/lodash.findlastindex-4.6.0.tgz#b8375ac0f02e9b926375cdf8dc3ea814abf9c6ac"
-
lodash.flattendeep@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
@@ -5517,10 +5509,6 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
-lodash.mapvalues@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c"
-
lodash.memoize@~3.0.3:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
@@ -5558,7 +5546,7 @@ lodash.templatesettings@^3.0.0:
lodash._reinterpolate "^3.0.0"
lodash.escape "^3.0.0"
-lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
@@ -5861,7 +5849,7 @@ mixin-deep@^1.2.0:
mkdirp@0.3.0:
version "0.3.0"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
+ resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
@@ -6919,18 +6907,6 @@ rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-diff-view@^1.7.0:
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/react-diff-view/-/react-diff-view-1.8.1.tgz#0b9b4adcb92de6730d28177d68654dfcc2097f73"
- dependencies:
- classnames "^2.2.6"
- gitdiff-parser "^0.1.2"
- leven "^2.1.0"
- lodash.escape "^4.0.1"
- lodash.findlastindex "^4.6.0"
- lodash.mapvalues "^4.6.0"
- warning "^4.0.1"
-
react-dom@^16.4.2:
version "16.5.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7"
@@ -8730,10 +8706,6 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
dependencies:
iconv-lite "0.4.24"
-whatwg-fetch@^2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
-
whatwg-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
index 9555ad66b5..d7846dbac5 100644
--- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
+++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java
@@ -79,14 +79,14 @@ import sonia.scm.repository.spi.HookEventFacade;
import sonia.scm.repository.xml.XmlRepositoryDAO;
import sonia.scm.schedule.QuartzScheduler;
import sonia.scm.schedule.Scheduler;
+import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.security.AuthorizationChangedEventProducer;
import sonia.scm.security.CipherHandler;
import sonia.scm.security.CipherUtil;
import sonia.scm.security.ConfigurableLoginAttemptHandler;
-import sonia.scm.security.DefaultJwtAccessTokenRefreshStrategy;
+import sonia.scm.security.DefaultAccessTokenCookieIssuer;
import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.security.DefaultSecuritySystem;
-import sonia.scm.security.JwtAccessTokenRefreshStrategy;
import sonia.scm.security.KeyGenerator;
import sonia.scm.security.LoginAttemptHandler;
import sonia.scm.security.SecuritySystem;
@@ -320,6 +320,7 @@ public class ScmServletModule extends ServletModule
// bind events
// bind(LastModifiedUpdateListener.class);
+ bind(AccessTokenCookieIssuer.class).to(DefaultAccessTokenCookieIssuer.class);
bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java
new file mode 100644
index 0000000000..e529bc7c1a
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/api/rest/BadRequestExceptionMapper.java
@@ -0,0 +1,16 @@
+package sonia.scm.api.rest;
+
+import sonia.scm.BadRequestException;
+import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
+
+import javax.inject.Inject;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+public class BadRequestExceptionMapper extends ContextualExceptionMapper {
+ @Inject
+ public BadRequestExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
+ super(BadRequestException.class, Response.Status.BAD_REQUEST, mapper);
+ }
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java
index 6bf6c8e803..64b20fc10c 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/RepositoryImportResource.java
@@ -46,7 +46,7 @@ import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.NotFoundException;
-import sonia.scm.NotSupportedFeatureException;
+import sonia.scm.FeatureNotSupportedException;
import sonia.scm.Type;
import sonia.scm.api.rest.RestActionUploadResult;
import sonia.scm.api.v2.resources.RepositoryResource;
@@ -394,7 +394,7 @@ public class RepositoryImportResource
response = Response.ok(result).build();
}
- catch (NotSupportedFeatureException ex)
+ catch (FeatureNotSupportedException ex)
{
logger
.warn(
@@ -609,7 +609,7 @@ public class RepositoryImportResource
types.add(t);
}
}
- catch (NotSupportedFeatureException ex)
+ catch (FeatureNotSupportedException ex)
{
if (logger.isTraceEnabled())
{
@@ -711,7 +711,7 @@ public class RepositoryImportResource
}
}
}
- catch (NotSupportedFeatureException ex)
+ catch (FeatureNotSupportedException ex)
{
throw new WebApplicationException(ex, Response.Status.BAD_REQUEST);
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java
deleted file mode 100644
index 6a48663aa5..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/NotSupportedFeatureExceptionMapper.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package sonia.scm.api.v2;
-
-import sonia.scm.NotSupportedFeatureException;
-import sonia.scm.api.rest.ContextualExceptionMapper;
-import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
-
-import javax.inject.Inject;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
-
-@Provider
-public class NotSupportedFeatureExceptionMapper extends ContextualExceptionMapper {
- @Inject
- public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
- super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper);
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java
deleted file mode 100644
index 18a6e6e75c..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangePasswordNotAllowedExceptionMapper.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import sonia.scm.api.rest.ContextualExceptionMapper;
-import sonia.scm.user.ChangePasswordNotAllowedException;
-import sonia.scm.user.InvalidPasswordException;
-
-import javax.inject.Inject;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
-
-@Provider
-public class ChangePasswordNotAllowedExceptionMapper extends ContextualExceptionMapper {
- @Inject
- public ChangePasswordNotAllowedExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
- super(ChangePasswordNotAllowedException.class, Response.Status.BAD_REQUEST, mapper);
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java
deleted file mode 100644
index 7a1d311a1c..0000000000
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/InvalidPasswordExceptionMapper.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package sonia.scm.api.v2.resources;
-
-import sonia.scm.api.rest.ContextualExceptionMapper;
-import sonia.scm.user.InvalidPasswordException;
-
-import javax.inject.Inject;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.ext.Provider;
-
-@Provider
-public class InvalidPasswordExceptionMapper extends ContextualExceptionMapper {
-
- @Inject
- public InvalidPasswordExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
- super(InvalidPasswordException.class, Response.Status.BAD_REQUEST, mapper);
- }
-}
diff --git a/scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java
similarity index 93%
rename from scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java
rename to scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java
index bb1473dca6..fd3f0e0d6f 100644
--- a/scm-webapp/src/main/java/sonia/scm/security/AccessTokenCookieIssuer.java
+++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultAccessTokenCookieIssuer.java
@@ -51,12 +51,12 @@ import java.util.concurrent.TimeUnit;
* @author Sebastian Sdorra
* @since 2.0.0
*/
-public final class AccessTokenCookieIssuer {
+public final class DefaultAccessTokenCookieIssuer implements AccessTokenCookieIssuer {
/**
- * the logger for AccessTokenCookieIssuer
+ * the logger for DefaultAccessTokenCookieIssuer
*/
- private static final Logger LOG = LoggerFactory.getLogger(AccessTokenCookieIssuer.class);
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultAccessTokenCookieIssuer.class);
private final ScmConfiguration configuration;
@@ -66,7 +66,7 @@ public final class AccessTokenCookieIssuer {
* @param configuration scm main configuration
*/
@Inject
- public AccessTokenCookieIssuer(ScmConfiguration configuration) {
+ public DefaultAccessTokenCookieIssuer(ScmConfiguration configuration) {
this.configuration = configuration;
}
diff --git a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java
index 8fb5929188..4418cb40a8 100644
--- a/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java
+++ b/scm-webapp/src/main/java/sonia/scm/security/JwtAccessToken.java
@@ -87,6 +87,7 @@ public final class JwtAccessToken implements AccessToken {
return ofNullable(claims.get(REFRESHABLE_UNTIL_CLAIM_KEY, Date.class));
}
+ @Override
public Optional getParentKey() {
return ofNullable(claims.get(PARENT_TOKEN_ID_CLAIM_KEY).toString());
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java
index 42428f9f77..1123dc94ce 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/AuthenticationResourceTest.java
@@ -18,6 +18,7 @@ import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilder;
import sonia.scm.security.AccessTokenBuilderFactory;
import sonia.scm.security.AccessTokenCookieIssuer;
+import sonia.scm.security.DefaultAccessTokenCookieIssuer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -46,7 +47,7 @@ public class AuthenticationResourceTest {
@Mock
private AccessTokenBuilder accessTokenBuilder;
- private AccessTokenCookieIssuer cookieIssuer = new AccessTokenCookieIssuer(mock(ScmConfiguration.class));
+ private AccessTokenCookieIssuer cookieIssuer = new DefaultAccessTokenCookieIssuer(mock(ScmConfiguration.class));
private static final String AUTH_JSON_TRILLIAN = "{\n" +
"\t\"cookie\": true,\n" +
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java
index 9638a8aa49..fe205e88a1 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/DispatcherMock.java
@@ -4,9 +4,9 @@ import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
+import sonia.scm.api.rest.BadRequestExceptionMapper;
import sonia.scm.api.rest.ConcurrentModificationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper;
-import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper;
public class DispatcherMock {
public static Dispatcher createDispatcher(Object resource) {
@@ -18,9 +18,7 @@ public class DispatcherMock {
dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper));
- dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper));
- dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper));
- dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper));
+ dispatcher.getProviderFactory().register(new BadRequestExceptionMapper(mapper));
return dispatcher;
}
}
diff --git a/scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java b/scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java
similarity index 93%
rename from scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java
rename to scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java
index 03cf174226..9c80cfc67b 100644
--- a/scm-webapp/src/test/java/sonia/scm/security/AccessTokenCookieIssuerTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/security/DefaultAccessTokenCookieIssuerTest.java
@@ -20,11 +20,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
-public class AccessTokenCookieIssuerTest {
+public class DefaultAccessTokenCookieIssuerTest {
private ScmConfiguration configuration;
- private AccessTokenCookieIssuer issuer;
+ private DefaultAccessTokenCookieIssuer issuer;
@Mock
private HttpServletRequest request;
@@ -41,7 +41,7 @@ public class AccessTokenCookieIssuerTest {
@Before
public void setUp() {
configuration = new ScmConfiguration();
- issuer = new AccessTokenCookieIssuer(configuration);
+ issuer = new DefaultAccessTokenCookieIssuer(configuration);
}
@Test