Merge with 2.0.0-m3

This commit is contained in:
René Pfeuffer
2018-12-19 11:10:27 +01:00
61 changed files with 863 additions and 238 deletions

View File

@@ -0,0 +1,9 @@
package sonia.scm;
import java.util.List;
public abstract class BadRequestException extends ExceptionWithContext {
public BadRequestException(List<ContextEntry> context, String message) {
super(context, message);
}
}

View File

@@ -40,13 +40,14 @@ import java.util.Collections;
* @author Sebastian Sdorra * @author Sebastian Sdorra
* @version 1.6 * @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 long serialVersionUID = 256498734456613496L;
private static final String CODE = "9SR8G0kmU1"; private static final String CODE = "9SR8G0kmU1";
public NotSupportedFeatureException(String feature) public FeatureNotSupportedException(String feature)
{ {
super(Collections.emptyList(),createMessage(feature)); super(Collections.emptyList(),createMessage(feature));
} }

View File

@@ -38,7 +38,7 @@ package sonia.scm.repository;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.NotSupportedFeatureException; import sonia.scm.FeatureNotSupportedException;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.event.ScmEventBus; import sonia.scm.event.ScmEventBus;
@@ -167,12 +167,12 @@ public abstract class AbstractRepositoryHandler<C extends RepositoryConfig>
* *
* @return * @return
* *
* @throws NotSupportedFeatureException * @throws FeatureNotSupportedException
*/ */
@Override @Override
public ImportHandler getImportHandler() public ImportHandler getImportHandler()
{ {
throw new NotSupportedFeatureException("import"); throw new FeatureNotSupportedException("import");
} }
/** /**

View File

@@ -36,7 +36,7 @@ package sonia.scm.repository;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import sonia.scm.Handler; import sonia.scm.Handler;
import sonia.scm.NotSupportedFeatureException; import sonia.scm.FeatureNotSupportedException;
import sonia.scm.plugin.ExtensionPoint; import sonia.scm.plugin.ExtensionPoint;
/** /**
@@ -59,9 +59,9 @@ public interface RepositoryHandler
* @return {@link ImportHandler} for the repository type of this handler * @return {@link ImportHandler} for the repository type of this handler
* @since 1.12 * @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. * Returns informations about the version of the RepositoryHandler.

View File

@@ -38,7 +38,7 @@ package sonia.scm.repository.api;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.NotSupportedFeatureException; import sonia.scm.FeatureNotSupportedException;
import sonia.scm.repository.Feature; import sonia.scm.repository.Feature;
import sonia.scm.repository.spi.DiffCommand; import sonia.scm.repository.spi.DiffCommand;
import sonia.scm.repository.spi.DiffCommandRequest; import sonia.scm.repository.spi.DiffCommandRequest;
@@ -203,7 +203,7 @@ public final class DiffCommandBuilder
public DiffCommandBuilder setAncestorChangeset(String revision) public DiffCommandBuilder setAncestorChangeset(String revision)
{ {
if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) { if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) {
throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name()); throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name());
} }
request.setAncestorChangeset(revision); request.setAncestorChangeset(revision);

View File

@@ -39,7 +39,7 @@ import com.google.common.base.Objects;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.NotSupportedFeatureException; import sonia.scm.FeatureNotSupportedException;
import sonia.scm.cache.Cache; import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.repository.Changeset; import sonia.scm.repository.Changeset;
@@ -410,7 +410,7 @@ public final class LogCommandBuilder
*/ */
public LogCommandBuilder setAncestorChangeset(String ancestorChangeset) { public LogCommandBuilder setAncestorChangeset(String ancestorChangeset) {
if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) { if (!supportedFeatures.contains(Feature.INCOMING_REVISION)) {
throw new NotSupportedFeatureException(Feature.INCOMING_REVISION.name()); throw new FeatureNotSupportedException(Feature.INCOMING_REVISION.name());
} }
request.setAncestorChangeset(ancestorChangeset); request.setAncestorChangeset(ancestorChangeset);
return this; return this;

View File

@@ -80,8 +80,20 @@ public interface AccessToken {
*/ */
Date getExpiration(); Date getExpiration();
/**
* Returns refresh expiration of token.
*
* @return refresh expiration
*/
Optional<Date> getRefreshExpiration(); Optional<Date> getRefreshExpiration();
/**
* Returns id of the parent key.
*
* @return parent key id
*/
Optional<String> getParentKey();
/** /**
* Returns the scope of the token. The scope is able to reduce the permissions of the subject in the context of this * 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 * token. For example we could issue a token which can only be used to read a single repository. for more informations

View File

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

View File

@@ -164,7 +164,7 @@ public class DefaultCipherHandler implements CipherHandler {
String result = null; String result = null;
try { try {
byte[] encodedInput = Base64.getDecoder().decode(value); byte[] encodedInput = Base64.getUrlDecoder().decode(value);
byte[] salt = new byte[SALT_LENGTH]; byte[] salt = new byte[SALT_LENGTH];
byte[] encoded = new byte[encodedInput.length - 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(salt, 0, result, 0, SALT_LENGTH);
System.arraycopy(encodedInput, 0, result, SALT_LENGTH, System.arraycopy(encodedInput, 0, result, SALT_LENGTH,
result.length - 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) { } catch (IOException | GeneralSecurityException ex) {
throw new CipherException("could not encode string", ex); throw new CipherException("could not encode string", ex);
} }

View File

@@ -33,6 +33,10 @@
package sonia.scm.store; package sonia.scm.store;
import java.util.Optional;
import static java.util.Optional.ofNullable;
/** /**
* ConfigurationStore for configuration objects. <strong>Note:</strong> the default * ConfigurationStore for configuration objects. <strong>Note:</strong> the default
* implementation use JAXB to marshall the configuration objects. * implementation use JAXB to marshall the configuration objects.
@@ -50,7 +54,17 @@ public interface ConfigurationStore<T>
* *
* @return configuration object from store * @return configuration object from store
*/ */
public T get(); T get();
/**
* Returns the configuration object from store.
*
*
* @return configuration object from store
*/
default Optional<T> getOptional() {
return ofNullable(get());
}
//~--- set methods ---------------------------------------------------------- //~--- set methods ----------------------------------------------------------
@@ -60,5 +74,5 @@ public interface ConfigurationStore<T>
* *
* @param obejct configuration object to store * @param obejct configuration object to store
*/ */
public void set(T obejct); void set(T object);
} }

View File

@@ -32,6 +32,10 @@
package sonia.scm.store; package sonia.scm.store;
import java.util.Optional;
import static java.util.Optional.ofNullable;
/** /**
* Base class for {@link BlobStore} and {@link DataStore}. * Base class for {@link BlobStore} and {@link DataStore}.
* *
@@ -67,4 +71,16 @@ public interface MultiEntryStore<T> {
* @return item with the given id * @return item with the given id
*/ */
public T get(String 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<T> getOptional(String id) {
return ofNullable(get(id));
}
} }

View File

@@ -1,12 +1,13 @@
package sonia.scm.user; package sonia.scm.user;
import sonia.scm.BadRequestException;
import sonia.scm.ContextEntry; 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"; 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) { public ChangePasswordNotAllowedException(ContextEntry.ContextBuilder context, String type) {
super(context.build(), String.format(WRONG_USER_TYPE, type)); super(context.build(), String.format(WRONG_USER_TYPE, type));

View File

@@ -1,9 +1,10 @@
package sonia.scm.user; package sonia.scm.user;
import sonia.scm.BadRequestException;
import sonia.scm.ContextEntry; 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"; private static final String CODE = "8YR7aawFW1";

View File

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

View File

@@ -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<String, Instant> {
@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);
}
}

View File

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

View File

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

View File

@@ -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/"
}
}
}

View File

@@ -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/"
}
}
}
]
}
}

View File

@@ -58,8 +58,6 @@ public abstract class FileBasedStoreFactory {
private RepositoryLocationResolver repositoryLocationResolver; private RepositoryLocationResolver repositoryLocationResolver;
private Store store; private Store store;
private File storeDirectory;
protected FileBasedStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, Store store) { protected FileBasedStoreFactory(SCMContextProvider contextProvider , RepositoryLocationResolver repositoryLocationResolver, Store store) {
this.contextProvider = contextProvider; this.contextProvider = contextProvider;
this.repositoryLocationResolver = repositoryLocationResolver; this.repositoryLocationResolver = repositoryLocationResolver;
@@ -75,7 +73,7 @@ public abstract class FileBasedStoreFactory {
} }
protected File getStoreLocation(String name, Class type, Repository repository) { protected File getStoreLocation(String name, Class type, Repository repository) {
if (storeDirectory == null) { File storeDirectory;
if (repository != null) { if (repository != null) {
LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName()); LOG.debug("create store with type: {}, name: {} and repository: {}", type, name, repository.getNamespaceAndName());
storeDirectory = this.getStoreDirectory(store, repository); storeDirectory = this.getStoreDirectory(store, repository);
@@ -84,8 +82,7 @@ public abstract class FileBasedStoreFactory {
storeDirectory = this.getStoreDirectory(store); storeDirectory = this.getStoreDirectory(store);
} }
IOUtil.mkdirs(storeDirectory); IOUtil.mkdirs(storeDirectory);
} return new File(storeDirectory, name);
return new File(this.storeDirectory, name);
} }
/** /**

View File

@@ -12,6 +12,6 @@
"@scm-manager/ui-extensions": "^0.1.1" "@scm-manager/ui-extensions": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22" "@scm-manager/ui-bundler": "^0.0.24"
} }
} }

View File

@@ -707,9 +707,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.1.1" "@scm-manager/ui-extensions": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22" "@scm-manager/ui-bundler": "^0.0.24"
} }
} }

View File

@@ -641,9 +641,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -9,6 +9,6 @@
"@scm-manager/ui-extensions": "^0.1.1" "@scm-manager/ui-extensions": "^0.1.1"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22" "@scm-manager/ui-bundler": "^0.0.24"
} }
} }

View File

@@ -641,9 +641,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -42,8 +42,20 @@ package sonia.scm.store;
*/ */
public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory { public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFactory {
private ConfigurationStore store;
public InMemoryConfigurationStoreFactory() {
}
public InMemoryConfigurationStoreFactory(ConfigurationStore store) {
this.store = store;
}
@Override @Override
public ConfigurationStore getStore(TypedStoreParameters storeParameters) { public ConfigurationStore getStore(TypedStoreParameters storeParameters) {
if (store != null) {
return store;
}
return new InMemoryConfigurationStore<>(); return new InMemoryConfigurationStore<>();
} }
} }

View File

@@ -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 <T> type of stored object
*/
public class InMemoryDataStore<T> implements DataStore<T> {
private final Map<String, T> 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<String, T> 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);
}
}

View File

@@ -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 <T> DataStore<T> getStore(TypedStoreParameters<T> storeParameters) {
if (store != null) {
return store;
}
return new InMemoryDataStore<>();
}
}

View File

@@ -14,7 +14,7 @@
"eslint-fix": "eslint src --fix" "eslint-fix": "eslint src --fix"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22", "@scm-manager/ui-bundler": "^0.0.24",
"create-index": "^2.3.0", "create-index": "^2.3.0",
"enzyme": "^3.5.0", "enzyme": "^3.5.0",
"enzyme-adapter-react-16": "^1.3.1", "enzyme-adapter-react-16": "^1.3.1",
@@ -35,7 +35,8 @@
"react-i18next": "^7.11.0", "react-i18next": "^7.11.0",
"react-jss": "^8.6.1", "react-jss": "^8.6.1",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-select": "^2.1.2" "react-select": "^2.1.2",
"diff2html": "^2.5.0"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [

View File

@@ -64,7 +64,8 @@ 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}) => {
return this.route(url + to, <RepositoryComponent repository={repository}/>); const link = repository._links[linkName].href
return this.route(url + to, <RepositoryComponent repository={repository} link={link}/>);
}; };
// bind config route to extension point // bind config route to extension point

View File

@@ -0,0 +1,36 @@
//@flow
import React from "react";
import { Diff2Html } from "diff2html";
type Props = {
diff: string,
sideBySide: boolean
};
class Diff extends React.Component<Props> {
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
<div dangerouslySetInnerHTML={{ __html: outputHtml }} />
);
}
}
export default Diff;

View File

@@ -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<Props, State> {
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 <ErrorNotification error={error} />;
} else if (loading || !diff) {
return <Loading />;
} else {
return <Diff diff={diff} />;
}
}
}
export default LoadingDiff;

View File

@@ -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<Props> {
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 <Notification type="danger">{t("changesets.diff.not-supported")}</Notification>;
} else {
const url = this.createUrl(changeset);
return <LoadingDiff url={url} />;
}
}
}
export default translate("repos")(ChangesetDiff);

View File

@@ -7,3 +7,4 @@ export { default as ChangesetId } from "./ChangesetId";
export { default as ChangesetList } from "./ChangesetList"; export { default as ChangesetList } from "./ChangesetList";
export { default as ChangesetRow } from "./ChangesetRow"; export { default as ChangesetRow } from "./ChangesetRow";
export { default as ChangesetTag } from "./ChangesetTag"; export { default as ChangesetTag } from "./ChangesetTag";
export { default as ChangesetDiff } from "./ChangesetDiff";

View File

@@ -1,3 +1,5 @@
// @flow // @flow
export * from "./changesets"; export * from "./changesets";
export { default as Diff } from "./Diff";
export { default as LoadingDiff } from "./LoadingDiff";

View File

@@ -687,9 +687,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"
@@ -2444,7 +2444,16 @@ dev-ip@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" 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" version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -3858,6 +3867,13 @@ hoek@2.x.x:
version "2.16.3" version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" 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: hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0:
version "2.5.5" version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" 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._reinterpolate "^3.0.0"
lodash.escape "^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" version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 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" for-in "^1.0.2"
is-extendable "^1.0.1" 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: "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1" version "0.5.1"
resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -5696,6 +5716,12 @@ nomnom@~1.6.2:
colors "0.5.x" colors "0.5.x"
underscore "~1.4.4" 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: nopt@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
@@ -5703,12 +5729,6 @@ nopt@^4.0.1:
abbrev "1" abbrev "1"
osenv "^0.1.4" 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: normalize-package-data@^2.3.2:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" 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: dependencies:
iconv-lite "0.4.24" 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: whatwg-mimetype@^2.1.0:
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz#a3d58ef10b76009b042d03e25591ece89b88d171"

View File

@@ -14,7 +14,7 @@
"check": "flow check" "check": "flow check"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22" "@scm-manager/ui-bundler": "^0.0.24"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [

View File

@@ -707,9 +707,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"

View File

@@ -11,7 +11,7 @@
"bulma": "^0.7.1", "bulma": "^0.7.1",
"bulma-tooltip": "^2.0.2", "bulma-tooltip": "^2.0.2",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"diff2html": "^2.4.0", "diff2html": "^2.5.0",
"font-awesome": "^4.7.0", "font-awesome": "^4.7.0",
"history": "^4.7.2", "history": "^4.7.2",
"i18next": "^11.4.0", "i18next": "^11.4.0",
@@ -21,7 +21,6 @@
"node-sass": "^4.9.3", "node-sass": "^4.9.3",
"postcss-easy-import": "^3.0.0", "postcss-easy-import": "^3.0.0",
"react": "^16.4.2", "react": "^16.4.2",
"react-diff-view": "^1.7.0",
"react-dom": "^16.4.2", "react-dom": "^16.4.2",
"react-i18next": "^7.9.0", "react-i18next": "^7.9.0",
"react-jss": "^8.6.0", "react-jss": "^8.6.0",
@@ -52,7 +51,7 @@
"pre-commit": "jest && flow && eslint src" "pre-commit": "jest && flow && eslint src"
}, },
"devDependencies": { "devDependencies": {
"@scm-manager/ui-bundler": "^0.0.22", "@scm-manager/ui-bundler": "^0.0.24",
"concat": "^1.0.3", "concat": "^1.0.3",
"copyfiles": "^2.0.0", "copyfiles": "^2.0.0",
"enzyme": "^3.3.0", "enzyme": "^3.3.0",

View File

@@ -66,6 +66,9 @@
} }
}, },
"changesets": { "changesets": {
"diff": {
"not-supported": "Diff of changesets is not supported by the type of repository"
},
"error-title": "Error", "error-title": "Error",
"error-subtitle": "Could not fetch changesets", "error-subtitle": "Could not fetch changesets",
"changeset": { "changeset": {

View File

@@ -9,14 +9,14 @@ import {
ChangesetId, ChangesetId,
ChangesetTag, ChangesetTag,
ChangesetAuthor, ChangesetAuthor,
ChangesetDiff,
AvatarWrapper, AvatarWrapper,
AvatarImage, AvatarImage,
changesets changesets,
} from "@scm-manager/ui-components"; } from "@scm-manager/ui-components";
import classNames from "classnames"; import classNames from "classnames";
import type { Tag } from "@scm-manager/ui-types"; import type { Tag } from "@scm-manager/ui-types";
import ScmDiff from "../../containers/ScmDiff";
const styles = { const styles = {
spacing: { spacing: {
@@ -78,7 +78,7 @@ class ChangesetDetails extends React.Component<Props> {
</p> </p>
</div> </div>
<div> <div>
<ScmDiff changeset={changeset} sideBySide={false}/> <ChangesetDiff changeset={changeset} />
</div> </div>
</div> </div>
); );

View File

@@ -11,6 +11,9 @@ import classNames from "classnames";
const styles = { const styles = {
zeroflex: { zeroflex: {
flexGrow: 0 flexGrow: 0
},
minWidthOfLabel: {
minWidth: "4.5rem"
} }
}; };
@@ -45,7 +48,12 @@ class BranchSelector extends React.Component<Props, State> {
return ( return (
<div className="box field is-horizontal"> <div className="box field is-horizontal">
<div <div
className={classNames("field-label", "is-normal", classes.zeroflex)} className={classNames(
"field-label",
"is-normal",
classes.zeroflex,
classes.minWidthOfLabel
)}
> >
<label className="label">{t("branch-selector.label")}</label> <label className="label">{t("branch-selector.label")}</label>
</div> </div>

View File

@@ -114,7 +114,7 @@ class RepositoryRoot extends React.Component<Props> {
return ( return (
<Page title={repository.namespace + "/" + repository.name}> <Page title={repository.namespace + "/" + repository.name}>
<div className="columns"> <div className="columns">
<div className="column is-three-quarters"> <div className="column is-three-quarters is-clipped">
<Switch> <Switch>
<Route <Route
path={url} path={url}

View File

@@ -1,51 +0,0 @@
// @flow
import React from "react";
import { apiClient } from "@scm-manager/ui-components";
import type { Changeset } from "@scm-manager/ui-types";
import { Diff2Html } from "diff2html";
type Props = {
changeset: Changeset,
sideBySide: boolean
};
type State = {
diff: string,
error?: Error
};
class ScmDiff extends React.Component<Props, State> {
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
<div dangerouslySetInnerHTML={{ __html: outputHtml }} />
);
}
}
export default ScmDiff;

View File

@@ -119,7 +119,9 @@ class FileTree extends React.Component<Props> {
<th className="is-hidden-mobile"> <th className="is-hidden-mobile">
{t("sources.file-tree.lastModified")} {t("sources.file-tree.lastModified")}
</th> </th>
<th>{t("sources.file-tree.description")}</th> <th className="is-hidden-mobile">
{t("sources.file-tree.description")}
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>

View File

@@ -6,10 +6,14 @@ import FileSize from "./FileSize";
import FileIcon from "./FileIcon"; import FileIcon from "./FileIcon";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import type { File } from "@scm-manager/ui-types"; import type { File } from "@scm-manager/ui-types";
import classNames from "classnames";
const styles = { const styles = {
iconColumn: { iconColumn: {
width: "16px" width: "16px"
},
wordBreakMinWidth: {
minWidth: "10em"
} }
}; };
@@ -71,12 +75,14 @@ class FileTreeLeaf extends React.Component<Props> {
return ( return (
<tr> <tr>
<td className={classes.iconColumn}>{this.createFileIcon(file)}</td> <td className={classes.iconColumn}>{this.createFileIcon(file)}</td>
<td>{this.createFileName(file)}</td> <td className={classNames(classes.wordBreakMinWidth, "is-word-break")}>{this.createFileName(file)}</td>
<td className="is-hidden-mobile">{fileSize}</td> <td className="is-hidden-mobile">{fileSize}</td>
<td className="is-hidden-mobile"> <td className="is-hidden-mobile">
<DateFromNow date={file.lastModified} /> <DateFromNow date={file.lastModified} />
</td> </td>
<td>{file.description}</td> <td className={classNames(classes.wordBreakMinWidth, "is-word-break", "is-hidden-mobile")}>
{file.description}
</td>
</tr> </tr>
); );
} }

View File

@@ -41,6 +41,13 @@ const styles = {
isVerticalCenter: { isVerticalCenter: {
display: "flex", display: "flex",
alignItems: "center" alignItems: "center"
},
wordBreak: {
WebkitHyphens: "auto",
MozHyphens: "auto",
MsHyphens: "auto",
hypens: "auto",
wordBreak: "break-all",
} }
}; };
@@ -93,7 +100,7 @@ class Content extends React.Component<Props, State> {
classes.marginInHeader classes.marginInHeader
)} )}
/> />
<span>{file.name}</span> <span className={classes.wordBreak}>{file.name}</span>
</div> </div>
<div className="media-right">{selector}</div> <div className="media-right">{selector}</div>
</article> </article>
@@ -125,11 +132,11 @@ class Content extends React.Component<Props, State> {
<tbody> <tbody>
<tr> <tr>
<td>{t("sources.content.path")}</td> <td>{t("sources.content.path")}</td>
<td>{file.path}</td> <td className={classes.wordBreak}>{file.path}</td>
</tr> </tr>
<tr> <tr>
<td>{t("sources.content.branch")}</td> <td>{t("sources.content.branch")}</td>
<td>{revision}</td> <td className={classes.wordBreak}>{revision}</td>
</tr> </tr>
<tr> <tr>
<td>{t("sources.content.size")}</td> <td>{t("sources.content.size")}</td>
@@ -141,7 +148,7 @@ class Content extends React.Component<Props, State> {
</tr> </tr>
<tr> <tr>
<td>{t("sources.content.description")}</td> <td>{t("sources.content.description")}</td>
<td>{description}</td> <td className={classes.wordBreak}>{description}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -27,6 +27,14 @@ $blue: #33B2E8;
padding: 0 0 0 3.8em !important; 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 { .main {
min-height: calc(100vh - 260px); min-height: calc(100vh - 260px);
} }

View File

@@ -698,9 +698,9 @@
version "0.0.2" version "0.0.2"
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85" resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
"@scm-manager/ui-bundler@^0.0.22": "@scm-manager/ui-bundler@^0.0.24":
version "0.0.22" version "0.0.24"
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.22.tgz#6eaed4e1f0b1fbc6ed1ebbf7eb0f5585f760949a" resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.24.tgz#034d5500c79b438c48d8f7ee985be07c4ea46d1e"
dependencies: dependencies:
"@babel/core" "^7.0.0" "@babel/core" "^7.0.0"
"@babel/plugin-proposal-class-properties" "^7.0.0" "@babel/plugin-proposal-class-properties" "^7.0.0"
@@ -1925,7 +1925,7 @@ class-utils@^0.3.5:
isobject "^3.0.0" isobject "^3.0.0"
static-extend "^0.1.1" static-extend "^0.1.1"
classnames@^2.2.5, classnames@^2.2.6: classnames@^2.2.5:
version "2.2.6" version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" 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" version "1.0.1"
resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0" resolved "https://registry.yarnpkg.com/dev-ip/-/dev-ip-1.0.1.tgz#a76a3ed1855be7a012bb8ac16cb80f3c00dc28f0"
diff2html@^2.4.0: diff2html@^2.5.0:
version "2.4.0" version "2.5.0"
resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.4.0.tgz#de632384eefa5a7f6b0e92eafb1fa25d22dc88ab" resolved "https://registry.yarnpkg.com/diff2html/-/diff2html-2.5.0.tgz#2d16f1a8f115354733b16b0264a594fa7db98aa2"
dependencies: dependencies:
diff "^3.5.0" diff "^3.5.0"
hogan.js "^3.0.2" hogan.js "^3.0.2"
lodash "^4.17.10" lodash "^4.17.11"
whatwg-fetch "^2.0.4" whatwg-fetch "^3.0.0"
diff@^3.2.0, diff@^3.5.0: diff@^3.2.0, diff@^3.5.0:
version "3.5.0" version "3.5.0"
@@ -3604,10 +3604,6 @@ getpass@^0.1.1:
dependencies: dependencies:
assert-plus "^1.0.0" 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: glob-base@^0.3.0:
version "0.3.0" version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" 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" version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" 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: lodash.flattendeep@^4.4.0:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" 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.isarguments "^3.0.0"
lodash.isarray "^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: lodash.memoize@~3.0.3:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" 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._reinterpolate "^3.0.0"
lodash.escape "^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" version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" 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: mkdirp@0.3.0:
version "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: "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1" version "0.5.1"
@@ -6919,18 +6907,6 @@ rc@^1.2.7:
minimist "^1.2.0" minimist "^1.2.0"
strip-json-comments "~2.0.1" 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: react-dom@^16.4.2:
version "16.5.2" version "16.5.2"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.5.2.tgz#b69ee47aa20bab5327b2b9d7c1fe2a30f2cfa9d7" 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: dependencies:
iconv-lite "0.4.24" 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: whatwg-fetch@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"

View File

@@ -79,14 +79,14 @@ import sonia.scm.repository.spi.HookEventFacade;
import sonia.scm.repository.xml.XmlRepositoryDAO; import sonia.scm.repository.xml.XmlRepositoryDAO;
import sonia.scm.schedule.QuartzScheduler; import sonia.scm.schedule.QuartzScheduler;
import sonia.scm.schedule.Scheduler; import sonia.scm.schedule.Scheduler;
import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.security.AuthorizationChangedEventProducer; import sonia.scm.security.AuthorizationChangedEventProducer;
import sonia.scm.security.CipherHandler; import sonia.scm.security.CipherHandler;
import sonia.scm.security.CipherUtil; import sonia.scm.security.CipherUtil;
import sonia.scm.security.ConfigurableLoginAttemptHandler; import sonia.scm.security.ConfigurableLoginAttemptHandler;
import sonia.scm.security.DefaultJwtAccessTokenRefreshStrategy; import sonia.scm.security.DefaultAccessTokenCookieIssuer;
import sonia.scm.security.DefaultKeyGenerator; import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.security.DefaultSecuritySystem; import sonia.scm.security.DefaultSecuritySystem;
import sonia.scm.security.JwtAccessTokenRefreshStrategy;
import sonia.scm.security.KeyGenerator; import sonia.scm.security.KeyGenerator;
import sonia.scm.security.LoginAttemptHandler; import sonia.scm.security.LoginAttemptHandler;
import sonia.scm.security.SecuritySystem; import sonia.scm.security.SecuritySystem;
@@ -320,6 +320,7 @@ public class ScmServletModule extends ServletModule
// bind events // bind events
// bind(LastModifiedUpdateListener.class); // bind(LastModifiedUpdateListener.class);
bind(AccessTokenCookieIssuer.class).to(DefaultAccessTokenCookieIssuer.class);
bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class); bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class);
} }

View File

@@ -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<BadRequestException> {
@Inject
public BadRequestExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(BadRequestException.class, Response.Status.BAD_REQUEST, mapper);
}
}

View File

@@ -46,7 +46,7 @@ import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import sonia.scm.NotFoundException; import sonia.scm.NotFoundException;
import sonia.scm.NotSupportedFeatureException; import sonia.scm.FeatureNotSupportedException;
import sonia.scm.Type; import sonia.scm.Type;
import sonia.scm.api.rest.RestActionUploadResult; import sonia.scm.api.rest.RestActionUploadResult;
import sonia.scm.api.v2.resources.RepositoryResource; import sonia.scm.api.v2.resources.RepositoryResource;
@@ -394,7 +394,7 @@ public class RepositoryImportResource
response = Response.ok(result).build(); response = Response.ok(result).build();
} }
catch (NotSupportedFeatureException ex) catch (FeatureNotSupportedException ex)
{ {
logger logger
.warn( .warn(
@@ -609,7 +609,7 @@ public class RepositoryImportResource
types.add(t); types.add(t);
} }
} }
catch (NotSupportedFeatureException ex) catch (FeatureNotSupportedException ex)
{ {
if (logger.isTraceEnabled()) if (logger.isTraceEnabled())
{ {
@@ -711,7 +711,7 @@ public class RepositoryImportResource
} }
} }
} }
catch (NotSupportedFeatureException ex) catch (FeatureNotSupportedException ex)
{ {
throw new WebApplicationException(ex, Response.Status.BAD_REQUEST); throw new WebApplicationException(ex, Response.Status.BAD_REQUEST);
} }

View File

@@ -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<NotSupportedFeatureException> {
@Inject
public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper);
}
}

View File

@@ -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<ChangePasswordNotAllowedException> {
@Inject
public ChangePasswordNotAllowedExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(ChangePasswordNotAllowedException.class, Response.Status.BAD_REQUEST, mapper);
}
}

View File

@@ -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<InvalidPasswordException> {
@Inject
public InvalidPasswordExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
super(InvalidPasswordException.class, Response.Status.BAD_REQUEST, mapper);
}
}

View File

@@ -51,12 +51,12 @@ import java.util.concurrent.TimeUnit;
* @author Sebastian Sdorra * @author Sebastian Sdorra
* @since 2.0.0 * @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; private final ScmConfiguration configuration;
@@ -66,7 +66,7 @@ public final class AccessTokenCookieIssuer {
* @param configuration scm main configuration * @param configuration scm main configuration
*/ */
@Inject @Inject
public AccessTokenCookieIssuer(ScmConfiguration configuration) { public DefaultAccessTokenCookieIssuer(ScmConfiguration configuration) {
this.configuration = configuration; this.configuration = configuration;
} }

View File

@@ -87,6 +87,7 @@ public final class JwtAccessToken implements AccessToken {
return ofNullable(claims.get(REFRESHABLE_UNTIL_CLAIM_KEY, Date.class)); return ofNullable(claims.get(REFRESHABLE_UNTIL_CLAIM_KEY, Date.class));
} }
@Override
public Optional<String> getParentKey() { public Optional<String> getParentKey() {
return ofNullable(claims.get(PARENT_TOKEN_ID_CLAIM_KEY).toString()); return ofNullable(claims.get(PARENT_TOKEN_ID_CLAIM_KEY).toString());
} }

View File

@@ -18,6 +18,7 @@ import sonia.scm.security.AccessToken;
import sonia.scm.security.AccessTokenBuilder; import sonia.scm.security.AccessTokenBuilder;
import sonia.scm.security.AccessTokenBuilderFactory; import sonia.scm.security.AccessTokenBuilderFactory;
import sonia.scm.security.AccessTokenCookieIssuer; import sonia.scm.security.AccessTokenCookieIssuer;
import sonia.scm.security.DefaultAccessTokenCookieIssuer;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
@@ -46,7 +47,7 @@ public class AuthenticationResourceTest {
@Mock @Mock
private AccessTokenBuilder accessTokenBuilder; 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" + private static final String AUTH_JSON_TRILLIAN = "{\n" +
"\t\"cookie\": true,\n" + "\t\"cookie\": true,\n" +

View File

@@ -4,9 +4,9 @@ import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockDispatcherFactory;
import sonia.scm.api.rest.AlreadyExistsExceptionMapper; import sonia.scm.api.rest.AlreadyExistsExceptionMapper;
import sonia.scm.api.rest.AuthorizationExceptionMapper; import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.rest.BadRequestExceptionMapper;
import sonia.scm.api.rest.ConcurrentModificationExceptionMapper; import sonia.scm.api.rest.ConcurrentModificationExceptionMapper;
import sonia.scm.api.v2.NotFoundExceptionMapper; import sonia.scm.api.v2.NotFoundExceptionMapper;
import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper;
public class DispatcherMock { public class DispatcherMock {
public static Dispatcher createDispatcher(Object resource) { public static Dispatcher createDispatcher(Object resource) {
@@ -18,9 +18,7 @@ public class DispatcherMock {
dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper)); dispatcher.getProviderFactory().register(new ConcurrentModificationExceptionMapper(mapper));
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class); dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper)); dispatcher.getProviderFactory().register(new InternalRepositoryExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper)); dispatcher.getProviderFactory().register(new BadRequestExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper));
dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper));
return dispatcher; return dispatcher;
} }
} }

View File

@@ -20,11 +20,11 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class AccessTokenCookieIssuerTest { public class DefaultAccessTokenCookieIssuerTest {
private ScmConfiguration configuration; private ScmConfiguration configuration;
private AccessTokenCookieIssuer issuer; private DefaultAccessTokenCookieIssuer issuer;
@Mock @Mock
private HttpServletRequest request; private HttpServletRequest request;
@@ -41,7 +41,7 @@ public class AccessTokenCookieIssuerTest {
@Before @Before
public void setUp() { public void setUp() {
configuration = new ScmConfiguration(); configuration = new ScmConfiguration();
issuer = new AccessTokenCookieIssuer(configuration); issuer = new DefaultAccessTokenCookieIssuer(configuration);
} }
@Test @Test