mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
Merge with 2.0.0-m3
This commit is contained in:
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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));
|
||||||
}
|
}
|
||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java
Normal file
25
scm-core/src/main/java/sonia/scm/xml/XmlInstantAdapter.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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": [
|
||||||
|
|||||||
@@ -63,8 +63,9 @@ 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
|
||||||
|
|||||||
36
scm-ui-components/packages/ui-components/src/repos/Diff.js
Normal file
36
scm-ui-components/packages/ui-components/src/repos/Diff.js
Normal 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;
|
||||||
@@ -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;
|
||||||
@@ -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);
|
||||||
@@ -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";
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
export * from "./changesets";
|
export * from "./changesets";
|
||||||
|
export { default as Diff } from "./Diff";
|
||||||
|
export { default as LoadingDiff } from "./LoadingDiff";
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" +
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user