mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-12-23 16:59:48 +01:00
merged with 2.0.0-m3
This commit is contained in:
@@ -72,7 +72,7 @@ public interface ConfigurationStore<T>
|
|||||||
* Stores the given configuration object to the store.
|
* Stores the given configuration object to the store.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param obejct configuration object to store
|
* @param object configuration object to store
|
||||||
*/
|
*/
|
||||||
void set(T object);
|
void set(T object);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class AuthenticationFilter extends HttpFilter
|
|||||||
}
|
}
|
||||||
else if (subject.isAuthenticated())
|
else if (subject.isAuthenticated())
|
||||||
{
|
{
|
||||||
logger.trace("user is allready authenticated");
|
logger.trace("user is already authenticated");
|
||||||
processChain(request, response, chain, subject);
|
processChain(request, response, chain, subject);
|
||||||
}
|
}
|
||||||
else if (isAnonymousAccessEnabled())
|
else if (isAnonymousAccessEnabled())
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ import sonia.scm.repository.GitRepositoryHandler;
|
|||||||
import sonia.scm.web.GitVndMediaType;
|
import sonia.scm.web.GitVndMediaType;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.PUT;
|
import javax.ws.rs.PUT;
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RESTful Web Service Resource to manage the configuration of the git plugin.
|
* RESTful Web Service Resource to manage the configuration of the git plugin.
|
||||||
*/
|
*/
|
||||||
@@ -26,13 +30,15 @@ public class GitConfigResource {
|
|||||||
private final GitConfigDtoToGitConfigMapper dtoToConfigMapper;
|
private final GitConfigDtoToGitConfigMapper dtoToConfigMapper;
|
||||||
private final GitConfigToGitConfigDtoMapper configToDtoMapper;
|
private final GitConfigToGitConfigDtoMapper configToDtoMapper;
|
||||||
private final GitRepositoryHandler repositoryHandler;
|
private final GitRepositoryHandler repositoryHandler;
|
||||||
|
private final Provider<GitRepositoryConfigResource> gitRepositoryConfigResource;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public GitConfigResource(GitConfigDtoToGitConfigMapper dtoToConfigMapper, GitConfigToGitConfigDtoMapper configToDtoMapper,
|
public GitConfigResource(GitConfigDtoToGitConfigMapper dtoToConfigMapper, GitConfigToGitConfigDtoMapper configToDtoMapper,
|
||||||
GitRepositoryHandler repositoryHandler) {
|
GitRepositoryHandler repositoryHandler, Provider<GitRepositoryConfigResource> gitRepositoryConfigResource) {
|
||||||
this.dtoToConfigMapper = dtoToConfigMapper;
|
this.dtoToConfigMapper = dtoToConfigMapper;
|
||||||
this.configToDtoMapper = configToDtoMapper;
|
this.configToDtoMapper = configToDtoMapper;
|
||||||
this.repositoryHandler = repositoryHandler;
|
this.repositoryHandler = repositoryHandler;
|
||||||
|
this.gitRepositoryConfigResource = gitRepositoryConfigResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,4 +94,9 @@ public class GitConfigResource {
|
|||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Path("{namespace}/{name}")
|
||||||
|
public GitRepositoryConfigResource getRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||||
|
return gitRepositoryConfigResource.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.github.legman.Subscribe;
|
||||||
|
import sonia.scm.EagerSingleton;
|
||||||
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.repository.ClearRepositoryCacheEvent;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@EagerSingleton @Extension
|
||||||
|
public class GitRepositoryConfigChangeClearRepositoryCacheListener {
|
||||||
|
@Subscribe
|
||||||
|
public void sendClearRepositoryCacheEvent(GitRepositoryConfigChangedEvent event) {
|
||||||
|
if (!Objects.equals(event.getOldConfig().getDefaultBranch(), event.getNewConfig().getDefaultBranch())) {
|
||||||
|
ScmEventBus.getInstance().post(new ClearRepositoryCacheEvent(event.getRepository()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.event.Event;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
@Event
|
||||||
|
public class GitRepositoryConfigChangedEvent {
|
||||||
|
private final Repository repository;
|
||||||
|
private final GitRepositoryConfig oldConfig;
|
||||||
|
private final GitRepositoryConfig newConfig;
|
||||||
|
|
||||||
|
public GitRepositoryConfigChangedEvent(Repository repository, GitRepositoryConfig oldConfig, GitRepositoryConfig newConfig) {
|
||||||
|
this.repository = repository;
|
||||||
|
this.oldConfig = oldConfig;
|
||||||
|
this.newConfig = newConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Repository getRepository() {
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitRepositoryConfig getOldConfig() {
|
||||||
|
return oldConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitRepositoryConfig getNewConfig() {
|
||||||
|
return newConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@SuppressWarnings("squid:S2160") // there is no proper semantic for equals on this dto
|
||||||
|
public class GitRepositoryConfigDto extends HalRepresentation {
|
||||||
|
|
||||||
|
private String defaultBranch;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||||
|
protected HalRepresentation add(Links links) {
|
||||||
|
return super.add(links);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.web.AbstractRepositoryJsonEnricher;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
@Extension
|
||||||
|
public class GitRepositoryConfigEnricher extends AbstractRepositoryJsonEnricher {
|
||||||
|
|
||||||
|
private final Provider<ScmPathInfoStore> scmPathInfoStore;
|
||||||
|
private final RepositoryManager manager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GitRepositoryConfigEnricher(Provider<ScmPathInfoStore> scmPathInfoStore, ObjectMapper objectMapper, RepositoryManager manager) {
|
||||||
|
super(objectMapper);
|
||||||
|
this.scmPathInfoStore = scmPathInfoStore;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void enrichRepositoryNode(JsonNode repositoryNode, String namespace, String name) {
|
||||||
|
if (GitRepositoryHandler.TYPE_NAME.equals(manager.get(new NamespaceAndName(namespace, name)).getType())) {
|
||||||
|
String repositoryConfigLink = new LinkBuilder(scmPathInfoStore.get().get(), GitConfigResource.class)
|
||||||
|
.method("getRepositoryConfig")
|
||||||
|
.parameters(namespace, name)
|
||||||
|
.href();
|
||||||
|
addLink(repositoryNode, "configuration", repositoryConfigLink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import de.otto.edison.hal.Links;
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryPermissions;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static de.otto.edison.hal.Link.link;
|
||||||
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
|
|
||||||
|
// Mapstruct does not support parameterized (i.e. non-default) constructors. Thus, we need to use field injection.
|
||||||
|
@SuppressWarnings("squid:S3306")
|
||||||
|
@Mapper
|
||||||
|
public abstract class GitRepositoryConfigMapper {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
|
public abstract GitRepositoryConfigDto map(GitRepositoryConfig config, @Context Repository repository);
|
||||||
|
public abstract GitRepositoryConfig map(GitRepositoryConfigDto dto);
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void appendLinks(@MappingTarget GitRepositoryConfigDto target, @Context Repository repository) {
|
||||||
|
Links.Builder linksBuilder = linkingTo().self(self());
|
||||||
|
if (RepositoryPermissions.modify(repository).isPermitted()) {
|
||||||
|
linksBuilder.single(link("update", update()));
|
||||||
|
}
|
||||||
|
target.add(linksBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String self() {
|
||||||
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), GitConfigResource.class);
|
||||||
|
return linkBuilder.method("get").parameters().href();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String update() {
|
||||||
|
LinkBuilder linkBuilder = new LinkBuilder(scmPathInfoStore.get(), GitConfigResource.class);
|
||||||
|
return linkBuilder.method("update").parameters().href();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||||
|
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.store.ConfigurationStore;
|
||||||
|
import sonia.scm.web.GitVndMediaType;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||||
|
import static sonia.scm.NotFoundException.notFound;
|
||||||
|
|
||||||
|
public class GitRepositoryConfigResource {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GitRepositoryConfigResource.class);
|
||||||
|
|
||||||
|
private final GitRepositoryConfigMapper repositoryConfigMapper;
|
||||||
|
private final RepositoryManager repositoryManager;
|
||||||
|
private final GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GitRepositoryConfigResource(GitRepositoryConfigMapper repositoryConfigMapper, RepositoryManager repositoryManager, GitRepositoryConfigStoreProvider gitRepositoryConfigStoreProvider) {
|
||||||
|
this.repositoryConfigMapper = repositoryConfigMapper;
|
||||||
|
this.repositoryManager = repositoryManager;
|
||||||
|
this.gitRepositoryConfigStoreProvider = gitRepositoryConfigStoreProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/")
|
||||||
|
@Produces(GitVndMediaType.GIT_REPOSITORY_CONFIG)
|
||||||
|
@StatusCodes({
|
||||||
|
@ResponseCode(code = 200, condition = "success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the repository config"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
public Response getRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||||
|
Repository repository = getRepository(namespace, name);
|
||||||
|
ConfigurationStore<GitRepositoryConfig> repositoryConfigStore = getStore(repository);
|
||||||
|
GitRepositoryConfig config = repositoryConfigStore.get();
|
||||||
|
GitRepositoryConfigDto dto = repositoryConfigMapper.map(config, repository);
|
||||||
|
return Response.ok(dto).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PUT
|
||||||
|
@Path("/")
|
||||||
|
@Consumes(GitVndMediaType.GIT_REPOSITORY_CONFIG)
|
||||||
|
@StatusCodes({
|
||||||
|
@ResponseCode(code = 204, condition = "update success"),
|
||||||
|
@ResponseCode(code = 401, condition = "not authenticated / invalid credentials"),
|
||||||
|
@ResponseCode(code = 403, condition = "not authorized, the current user does not have the privilege to change this repositories config"),
|
||||||
|
@ResponseCode(code = 404, condition = "not found, no repository with the specified namespace and name available/name available"),
|
||||||
|
@ResponseCode(code = 500, condition = "internal server error")
|
||||||
|
})
|
||||||
|
public Response setRepositoryConfig(@PathParam("namespace") String namespace, @PathParam("name") String name, GitRepositoryConfigDto dto) {
|
||||||
|
Repository repository = getRepository(namespace, name);
|
||||||
|
ConfigurationStore<GitRepositoryConfig> repositoryConfigStore = getStore(repository);
|
||||||
|
GitRepositoryConfig config = repositoryConfigMapper.map(dto);
|
||||||
|
repositoryConfigStore.set(config);
|
||||||
|
LOG.info("git default branch of repository {} has changed, sending clear cache event", repository.getNamespaceAndName());
|
||||||
|
return Response.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Repository getRepository(@PathParam("namespace") String namespace, @PathParam("name") String name) {
|
||||||
|
NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name);
|
||||||
|
Repository repository = repositoryManager.get(namespaceAndName);
|
||||||
|
if (repository == null) {
|
||||||
|
throw notFound(entity(namespaceAndName));
|
||||||
|
}
|
||||||
|
return repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigurationStore<GitRepositoryConfig> getStore(Repository repository) {
|
||||||
|
return gitRepositoryConfigStoreProvider.get(repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import sonia.scm.event.ScmEventBus;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.store.ConfigurationStore;
|
||||||
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class GitRepositoryConfigStoreProvider {
|
||||||
|
|
||||||
|
private final ConfigurationStoreFactory configurationStoreFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GitRepositoryConfigStoreProvider(ConfigurationStoreFactory configurationStoreFactory) {
|
||||||
|
this.configurationStoreFactory = configurationStoreFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationStore<GitRepositoryConfig> get(Repository repository) {
|
||||||
|
return new StoreWrapper(configurationStoreFactory.withType(GitRepositoryConfig.class).withName("gitConfig").forRepository(repository).build(), repository);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StoreWrapper implements ConfigurationStore<GitRepositoryConfig> {
|
||||||
|
|
||||||
|
private final ConfigurationStore<GitRepositoryConfig> delegate;
|
||||||
|
private final Repository repository;
|
||||||
|
|
||||||
|
private StoreWrapper(ConfigurationStore<GitRepositoryConfig> delegate, Repository repository) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GitRepositoryConfig get() {
|
||||||
|
GitRepositoryConfig config = delegate.get();
|
||||||
|
if (config == null) {
|
||||||
|
return new GitRepositoryConfig();
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(GitRepositoryConfig newConfig) {
|
||||||
|
GitRepositoryConfig oldConfig = get();
|
||||||
|
delegate.set(newConfig);
|
||||||
|
ScmEventBus.getInstance().post(new GitRepositoryConfigChangedEvent(repository, oldConfig, newConfig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2014, Sebastian Sdorra
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from this
|
|
||||||
* software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package sonia.scm.repository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constants for Git.
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
* @since 1.50
|
|
||||||
*/
|
|
||||||
public final class GitConstants {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default branch repository property.
|
|
||||||
*/
|
|
||||||
public static final String PROPERTY_DEFAULT_BRANCH = "git.default-branch";
|
|
||||||
|
|
||||||
private GitConstants() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Sebastian Sdorra
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.lib.Constants;
|
||||||
|
import org.eclipse.jgit.lib.Ref;
|
||||||
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The GitHeadModifier is able to modify the head of a git repository.
|
||||||
|
*
|
||||||
|
* @author Sebastian Sdorra
|
||||||
|
* @since 1.61
|
||||||
|
*/
|
||||||
|
public class GitHeadModifier {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(GitHeadModifier.class);
|
||||||
|
|
||||||
|
private final GitRepositoryHandler repositoryHandler;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public GitHeadModifier(GitRepositoryHandler repositoryHandler) {
|
||||||
|
this.repositoryHandler = repositoryHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensures that the repositories head points to the given branch. The method will return {@code false} if the
|
||||||
|
* repositories head points already to the given branch.
|
||||||
|
*
|
||||||
|
* @param repository repository to modify
|
||||||
|
* @param newHead branch which should be the new head of the repository
|
||||||
|
*
|
||||||
|
* @return {@code true} if the head has changed
|
||||||
|
*/
|
||||||
|
public boolean ensure(Repository repository, String newHead) {
|
||||||
|
try (org.eclipse.jgit.lib.Repository gitRepository = open(repository)) {
|
||||||
|
String currentHead = resolve(gitRepository);
|
||||||
|
if (!Objects.equals(currentHead, newHead)) {
|
||||||
|
return modify(gitRepository, newHead);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LOG.warn("failed to change head of repository", ex);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolve(org.eclipse.jgit.lib.Repository gitRepository) throws IOException {
|
||||||
|
Ref ref = gitRepository.getRefDatabase().getRef(Constants.HEAD);
|
||||||
|
if ( ref.isSymbolic() ) {
|
||||||
|
ref = ref.getTarget();
|
||||||
|
}
|
||||||
|
return GitUtil.getBranch(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean modify(org.eclipse.jgit.lib.Repository gitRepository, String newHead) throws IOException {
|
||||||
|
RefUpdate refUpdate = gitRepository.getRefDatabase().newUpdate(Constants.HEAD, true);
|
||||||
|
refUpdate.setForceUpdate(true);
|
||||||
|
RefUpdate.Result result = refUpdate.link(Constants.R_HEADS + newHead);
|
||||||
|
return result == RefUpdate.Result.FORCED;
|
||||||
|
}
|
||||||
|
|
||||||
|
private org.eclipse.jgit.lib.Repository open(Repository repository) throws IOException {
|
||||||
|
File directory = repositoryHandler.getDirectory(repository.getId());
|
||||||
|
return GitUtil.open(directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "config")
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class GitRepositoryConfig {
|
||||||
|
|
||||||
|
public GitRepositoryConfig() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public GitRepositoryConfig(String defaultBranch) {
|
||||||
|
this.defaultBranch = defaultBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String defaultBranch;
|
||||||
|
|
||||||
|
public String getDefaultBranch() {
|
||||||
|
return defaultBranch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultBranch(String defaultBranch) {
|
||||||
|
this.defaultBranch = defaultBranch;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,67 +31,45 @@
|
|||||||
package sonia.scm.repository;
|
package sonia.scm.repository;
|
||||||
|
|
||||||
import com.github.legman.Subscribe;
|
import com.github.legman.Subscribe;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import sonia.scm.EagerSingleton;
|
import sonia.scm.EagerSingleton;
|
||||||
import sonia.scm.HandlerEventType;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigChangedEvent;
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository listener which handles git related repository events.
|
* Repository listener which handles git related repository events.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
* @since 1.50
|
* @since 1.50
|
||||||
*/
|
*/
|
||||||
@Extension
|
@Extension
|
||||||
@EagerSingleton
|
@EagerSingleton
|
||||||
public class GitRepositoryModifyListener {
|
public class GitRepositoryModifyListener {
|
||||||
|
|
||||||
/**
|
private final GitHeadModifier headModifier;
|
||||||
* the logger for GitRepositoryModifyListener
|
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||||
*/
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(GitRepositoryModifyListener.class);
|
@Inject
|
||||||
|
public GitRepositoryModifyListener(GitHeadModifier headModifier, GitRepositoryConfigStoreProvider storeProvider) {
|
||||||
|
this.headModifier = headModifier;
|
||||||
|
this.storeProvider = storeProvider;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if
|
* Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if
|
||||||
* the default branch of a git repository was modified.
|
* the default branch of a git repository was modified.
|
||||||
*
|
*
|
||||||
* @param event repository modification event
|
* @param event repository modification event
|
||||||
*/
|
*/
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void handleEvent(RepositoryModificationEvent event){
|
public void handleEvent(GitRepositoryConfigChangedEvent event){
|
||||||
Repository repository = event.getItem();
|
Repository repository = event.getRepository();
|
||||||
|
|
||||||
if ( isModifyEvent(event) &&
|
String defaultBranch = storeProvider.get(repository).get().getDefaultBranch();
|
||||||
isGitRepository(event.getItem()) &&
|
if (defaultBranch != null) {
|
||||||
hasDefaultBranchChanged(event.getItemBeforeModification(), repository))
|
headModifier.ensure(repository, defaultBranch);
|
||||||
{
|
|
||||||
logger.info("git default branch of repository {} has changed, sending clear cache event", repository.getId());
|
|
||||||
sendClearRepositoryCacheEvent(repository);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
protected void sendClearRepositoryCacheEvent(Repository repository) {
|
|
||||||
ScmEventBus.getInstance().post(new ClearRepositoryCacheEvent(repository));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isModifyEvent(RepositoryEvent event) {
|
|
||||||
return event.getEventType() == HandlerEventType.MODIFY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isGitRepository(Repository repository) {
|
|
||||||
return GitRepositoryHandler.TYPE_NAME.equals(repository.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasDefaultBranchChanged(Repository old, Repository current) {
|
|
||||||
return !Objects.equal(
|
|
||||||
old.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH),
|
|
||||||
current.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import org.eclipse.jgit.lib.Ref;
|
|||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import sonia.scm.repository.GitConstants;
|
|
||||||
import sonia.scm.repository.GitUtil;
|
import sonia.scm.repository.GitUtil;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -110,7 +109,7 @@ public class AbstractGitCommand
|
|||||||
|
|
||||||
protected Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
|
protected Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
|
||||||
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
||||||
String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH);
|
String defaultBranchName = context.getConfig().getDefaultBranch();
|
||||||
if (!Strings.isNullOrEmpty(defaultBranchName)) {
|
if (!Strings.isNullOrEmpty(defaultBranchName)) {
|
||||||
return GitUtil.getBranchId(gitRepository, defaultBranchName);
|
return GitUtil.getBranchId(gitRepository, defaultBranchName);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ package sonia.scm.repository.spi;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.GitUtil;
|
import sonia.scm.repository.GitUtil;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
@@ -68,10 +70,11 @@ public class GitContext implements Closeable
|
|||||||
* @param directory
|
* @param directory
|
||||||
* @param repository
|
* @param repository
|
||||||
*/
|
*/
|
||||||
public GitContext(File directory, Repository repository)
|
public GitContext(File directory, Repository repository, GitRepositoryConfigStoreProvider storeProvider)
|
||||||
{
|
{
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
|
this.storeProvider = storeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
@@ -117,11 +120,25 @@ public class GitContext implements Closeable
|
|||||||
return directory;
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GitRepositoryConfig getConfig() {
|
||||||
|
GitRepositoryConfig config = storeProvider.get(repository).get();
|
||||||
|
if (config == null) {
|
||||||
|
return new GitRepositoryConfig();
|
||||||
|
} else {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setConfig(GitRepositoryConfig newConfig) {
|
||||||
|
storeProvider.get(repository).set(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
//~--- fields ---------------------------------------------------------------
|
//~--- fields ---------------------------------------------------------------
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private final File directory;
|
private final File directory;
|
||||||
private final Repository repository;
|
private final Repository repository;
|
||||||
|
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||||
|
|
||||||
/** Field description */
|
/** Field description */
|
||||||
private org.eclipse.jgit.lib.Repository gitRepository;
|
private org.eclipse.jgit.lib.Repository gitRepository;
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
ObjectId ancestorId = null;
|
ObjectId ancestorId = null;
|
||||||
|
|
||||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
||||||
ancestorId = computeCommonAncestor(request, repository, startId, branch);
|
ancestorId = repository.resolve(request.getAncestorChangeset());
|
||||||
}
|
}
|
||||||
|
|
||||||
revWalk = new RevWalk(repository);
|
revWalk = new RevWalk(repository);
|
||||||
@@ -225,16 +225,15 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
|||||||
revWalk.markStart(revWalk.lookupCommit(branch.getObjectId()));
|
revWalk.markStart(revWalk.lookupCommit(branch.getObjectId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ancestorId != null) {
|
||||||
|
revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
|
||||||
|
}
|
||||||
|
|
||||||
Iterator<RevCommit> iterator = revWalk.iterator();
|
Iterator<RevCommit> iterator = revWalk.iterator();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
RevCommit commit = iterator.next();
|
RevCommit commit = iterator.next();
|
||||||
|
|
||||||
if (commit.getId().equals(ancestorId)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((counter >= start)
|
if ((counter >= start)
|
||||||
&& ((limit < 0) || (counter < start + limit))) {
|
&& ((limit < 0) || (counter < start + limit))) {
|
||||||
changesetList.add(converter.createChangeset(commit));
|
changesetList.add(converter.createChangeset(commit));
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.Feature;
|
import sonia.scm.repository.Feature;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
@@ -73,10 +74,10 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
|||||||
|
|
||||||
//~--- constructors ---------------------------------------------------------
|
//~--- constructors ---------------------------------------------------------
|
||||||
|
|
||||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) {
|
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.context = new GitContext(handler.getDirectory(repository.getId()), repository);
|
this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
//~--- methods --------------------------------------------------------------
|
//~--- methods --------------------------------------------------------------
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
@@ -47,10 +48,12 @@ import sonia.scm.repository.Repository;
|
|||||||
public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||||
|
|
||||||
private final GitRepositoryHandler handler;
|
private final GitRepositoryHandler handler;
|
||||||
|
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler) {
|
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider) {
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
this.storeProvider = storeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -58,7 +61,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
|||||||
GitRepositoryServiceProvider provider = null;
|
GitRepositoryServiceProvider provider = null;
|
||||||
|
|
||||||
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||||
provider = new GitRepositoryServiceProvider(handler, repository);
|
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider;
|
return provider;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.eclipse.jgit.transport.ScmTransportProtocol;
|
|||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
import sonia.scm.api.v2.resources.GitConfigDtoToGitConfigMapper;
|
import sonia.scm.api.v2.resources.GitConfigDtoToGitConfigMapper;
|
||||||
import sonia.scm.api.v2.resources.GitConfigToGitConfigDtoMapper;
|
import sonia.scm.api.v2.resources.GitConfigToGitConfigDtoMapper;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigMapper;
|
||||||
import sonia.scm.plugin.Extension;
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.GitWorkdirFactory;
|
import sonia.scm.repository.GitWorkdirFactory;
|
||||||
import sonia.scm.repository.spi.SimpleGitWorkdirFactory;
|
import sonia.scm.repository.spi.SimpleGitWorkdirFactory;
|
||||||
@@ -65,6 +66,7 @@ public class GitServletModule extends ServletModule
|
|||||||
|
|
||||||
bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass());
|
bind(GitConfigDtoToGitConfigMapper.class).to(Mappers.getMapper(GitConfigDtoToGitConfigMapper.class).getClass());
|
||||||
bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
|
bind(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
|
||||||
|
bind(GitRepositoryConfigMapper.class).to(Mappers.getMapper(GitRepositoryConfigMapper.class).getClass());
|
||||||
|
|
||||||
bind(GitWorkdirFactory.class).to(SimpleGitWorkdirFactory.class);
|
bind(GitWorkdirFactory.class).to(SimpleGitWorkdirFactory.class);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sonia.scm.web;
|
|||||||
|
|
||||||
public class GitVndMediaType {
|
public class GitVndMediaType {
|
||||||
public static final String GIT_CONFIG = VndMediaType.PREFIX + "gitConfig" + VndMediaType.SUFFIX;
|
public static final String GIT_CONFIG = VndMediaType.PREFIX + "gitConfig" + VndMediaType.SUFFIX;
|
||||||
|
public static final String GIT_REPOSITORY_CONFIG = VndMediaType.PREFIX + "gitConfig" + VndMediaType.SUFFIX;
|
||||||
|
|
||||||
private GitVndMediaType() {
|
private GitVndMediaType() {
|
||||||
}
|
}
|
||||||
|
|||||||
155
scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js
Normal file
155
scm-plugins/scm-git-plugin/src/main/js/RepositoryConfig.js
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import {apiClient, BranchSelector, ErrorPage, Loading, SubmitButton} from "@scm-manager/ui-components";
|
||||||
|
import type {Branch, Repository} from "@scm-manager/ui-types";
|
||||||
|
import {translate} from "react-i18next";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
repository: Repository,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
loadingBranches: boolean,
|
||||||
|
loadingDefaultBranch: boolean,
|
||||||
|
submitPending: boolean,
|
||||||
|
error?: Error,
|
||||||
|
branches: Branch[],
|
||||||
|
selectedBranchName?: string,
|
||||||
|
defaultBranchChanged: boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
const GIT_CONFIG_CONTENT_TYPE = "application/vnd.scmm-gitConfig+json";
|
||||||
|
|
||||||
|
class RepositoryConfig extends React.Component<Props, State> {
|
||||||
|
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
loadingBranches: true,
|
||||||
|
loadingDefaultBranch: true,
|
||||||
|
submitPending: false,
|
||||||
|
branches: [],
|
||||||
|
defaultBranchChanged: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { repository } = this.props;
|
||||||
|
this.setState({ ...this.state, loadingBranches: true });
|
||||||
|
apiClient
|
||||||
|
.get(repository._links.branches.href)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(payload => payload._embedded.branches)
|
||||||
|
.then(branches =>
|
||||||
|
this.setState({ ...this.state, branches, loadingBranches: false })
|
||||||
|
)
|
||||||
|
.catch(error => this.setState({ ...this.state, error }));
|
||||||
|
|
||||||
|
this.setState({ ...this.state, loadingDefaultBranch: true });
|
||||||
|
apiClient
|
||||||
|
.get(repository._links.configuration.href)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(payload => payload.defaultBranch)
|
||||||
|
.then(selectedBranchName =>
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
selectedBranchName,
|
||||||
|
loadingDefaultBranch: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(error => this.setState({ ...this.state, error }));
|
||||||
|
}
|
||||||
|
|
||||||
|
branchSelected = (branch: Branch) => {
|
||||||
|
if (!branch) {
|
||||||
|
this.setState({ ...this.state, selectedBranchName: undefined, defaultBranchChanged: false});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({ ...this.state, selectedBranchName: branch.name, defaultBranchChanged: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
submit = (event: Event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const { repository } = this.props;
|
||||||
|
const newConfig = {
|
||||||
|
defaultBranch: this.state.selectedBranchName
|
||||||
|
};
|
||||||
|
this.setState({ ...this.state, submitPending: true });
|
||||||
|
apiClient
|
||||||
|
.put(
|
||||||
|
repository._links.configuration.href,
|
||||||
|
newConfig,
|
||||||
|
GIT_CONFIG_CONTENT_TYPE
|
||||||
|
)
|
||||||
|
.then(() =>
|
||||||
|
this.setState({
|
||||||
|
...this.state,
|
||||||
|
submitPending: false,
|
||||||
|
defaultBranchChanged: true
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(error => this.setState({ ...this.state, error }));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { t } = this.props;
|
||||||
|
const { loadingBranches, loadingDefaultBranch, submitPending, error } = this.state;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<ErrorPage
|
||||||
|
title={t("scm-git-plugin.repo-config.error.title")}
|
||||||
|
subtitle={t("scm-git-plugin.repo-config.error.subtitle")}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(loadingBranches || loadingDefaultBranch)) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{this.renderBranchChangedNotification()}
|
||||||
|
<form onSubmit={this.submit}>
|
||||||
|
<BranchSelector
|
||||||
|
label={t("scm-git-plugin.repo-config.default-branch")}
|
||||||
|
branches={this.state.branches}
|
||||||
|
selected={this.branchSelected}
|
||||||
|
selectedBranch={this.state.selectedBranchName}
|
||||||
|
/>
|
||||||
|
<SubmitButton
|
||||||
|
label={t("scm-git-plugin.repo-config.submit")}
|
||||||
|
loading={submitPending}
|
||||||
|
disabled={!this.state.selectedBranchName}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBranchChangedNotification = () => {
|
||||||
|
if (this.state.defaultBranchChanged) {
|
||||||
|
return (
|
||||||
|
<div className="notification is-primary">
|
||||||
|
<button
|
||||||
|
className="delete"
|
||||||
|
onClick={() =>
|
||||||
|
this.setState({ ...this.state, defaultBranchChanged: false })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
{this.props.t("scm-git-plugin.repo-config.success")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate("plugins")(RepositoryConfig);
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import { binder } from "@scm-manager/ui-extensions";
|
import React from "react";
|
||||||
|
import {binder} from "@scm-manager/ui-extensions";
|
||||||
import ProtocolInformation from "./ProtocolInformation";
|
import ProtocolInformation from "./ProtocolInformation";
|
||||||
import GitAvatar from "./GitAvatar";
|
import GitAvatar from "./GitAvatar";
|
||||||
|
|
||||||
import { ConfigurationBinder as cfgBinder } from "@scm-manager/ui-components";
|
import {ConfigurationBinder as cfgBinder} from "@scm-manager/ui-components";
|
||||||
import GitGlobalConfiguration from "./GitGlobalConfiguration";
|
import GitGlobalConfiguration from "./GitGlobalConfiguration";
|
||||||
import GitMergeInformation from "./GitMergeInformation";
|
import GitMergeInformation from "./GitMergeInformation";
|
||||||
|
import RepositoryConfig from "./RepositoryConfig";
|
||||||
|
|
||||||
// repository
|
// repository
|
||||||
|
|
||||||
@@ -13,10 +15,29 @@ const gitPredicate = (props: Object) => {
|
|||||||
return props.repository && props.repository.type === "git";
|
return props.repository && props.repository.type === "git";
|
||||||
};
|
};
|
||||||
|
|
||||||
binder.bind("repos.repository-details.information", ProtocolInformation, gitPredicate);
|
binder.bind(
|
||||||
binder.bind("repos.repository-merge.information", GitMergeInformation, gitPredicate);
|
"repos.repository-details.information",
|
||||||
|
ProtocolInformation,
|
||||||
|
gitPredicate
|
||||||
|
);
|
||||||
|
binder.bind(
|
||||||
|
"repos.repository-merge.information",
|
||||||
|
GitMergeInformation,
|
||||||
|
gitPredicate
|
||||||
|
);
|
||||||
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
||||||
|
|
||||||
|
cfgBinder.bindRepository(
|
||||||
|
"/configuration",
|
||||||
|
"scm-git-plugin.repo-config.link",
|
||||||
|
"configuration",
|
||||||
|
RepositoryConfig
|
||||||
|
);
|
||||||
// global config
|
// global config
|
||||||
|
|
||||||
cfgBinder.bindGlobal("/git", "scm-git-plugin.config.link", "gitConfig", GitGlobalConfiguration);
|
cfgBinder.bindGlobal(
|
||||||
|
"/git",
|
||||||
|
"scm-git-plugin.config.link",
|
||||||
|
"gitConfig",
|
||||||
|
GitGlobalConfiguration
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"scm-git-plugin": {
|
"scm-git-plugin": {
|
||||||
"information": {
|
"information": {
|
||||||
"clone" : "Clone the repository",
|
"clone": "Clone the repository",
|
||||||
"create" : "Create a new repository",
|
"create": "Create a new repository",
|
||||||
"replace" : "Push an existing repository",
|
"replace": "Push an existing repository",
|
||||||
"merge": {
|
"merge": {
|
||||||
"heading": "How to merge source branch into target branch",
|
"heading": "How to merge source branch into target branch",
|
||||||
"checkout": "1. Make sure your workspace is clean and checkout target branch",
|
"checkout": "1. Make sure your workspace is clean and checkout target branch",
|
||||||
@@ -22,6 +22,16 @@
|
|||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"disabledHelpText": "Enable or disable the Git plugin",
|
"disabledHelpText": "Enable or disable the Git plugin",
|
||||||
"submit": "Submit"
|
"submit": "Submit"
|
||||||
|
},
|
||||||
|
"repo-config": {
|
||||||
|
"link": "Configuration",
|
||||||
|
"default-branch": "Default branch",
|
||||||
|
"submit": "Submit",
|
||||||
|
"error": {
|
||||||
|
"title": "Error",
|
||||||
|
"subtitle": "Something went wrong"
|
||||||
|
},
|
||||||
|
"success": "Default branch changed!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import com.github.sdorra.shiro.ShiroRule;
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
import com.github.sdorra.shiro.SubjectAware;
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
import org.jboss.resteasy.core.Dispatcher;
|
import org.jboss.resteasy.core.Dispatcher;
|
||||||
@@ -14,22 +12,33 @@ import org.junit.Test;
|
|||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
import sonia.scm.repository.GitConfig;
|
import sonia.scm.repository.GitConfig;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.GitRepositoryHandler;
|
import sonia.scm.repository.GitRepositoryHandler;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.store.ConfigurationStore;
|
||||||
|
import sonia.scm.store.ConfigurationStoreFactory;
|
||||||
import sonia.scm.web.GitVndMediaType;
|
import sonia.scm.web.GitVndMediaType;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import static com.google.inject.util.Providers.of;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@SubjectAware(
|
@SubjectAware(
|
||||||
@@ -55,30 +64,48 @@ public class GitConfigResourceTest {
|
|||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private ScmPathInfoStore scmPathInfoStore;
|
private ScmPathInfoStore scmPathInfoStore;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RepositoryManager repositoryManager;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
|
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
|
||||||
|
@InjectMocks
|
||||||
|
private GitRepositoryConfigMapperImpl repositoryConfigMapper;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private GitRepositoryHandler repositoryHandler;
|
private GitRepositoryHandler repositoryHandler;
|
||||||
|
|
||||||
|
@Mock(answer = Answers.CALLS_REAL_METHODS)
|
||||||
|
private ConfigurationStoreFactory configurationStoreFactory;
|
||||||
|
@Spy
|
||||||
|
private ConfigurationStore<Object> configurationStore;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<Object> configurationStoreCaptor;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void prepareEnvironment() {
|
public void prepareEnvironment() {
|
||||||
GitConfig gitConfig = createConfiguration();
|
GitConfig gitConfig = createConfiguration();
|
||||||
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
when(repositoryHandler.getConfig()).thenReturn(gitConfig);
|
||||||
GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler);
|
GitRepositoryConfigResource gitRepositoryConfigResource = new GitRepositoryConfigResource(repositoryConfigMapper, repositoryManager, new GitRepositoryConfigStoreProvider(configurationStoreFactory));
|
||||||
|
GitConfigResource gitConfigResource = new GitConfigResource(dtoToConfigMapper, configToDtoMapper, repositoryHandler, of(gitRepositoryConfigResource));
|
||||||
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
dispatcher.getRegistry().addSingletonResource(gitConfigResource);
|
||||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initConfigStore() {
|
||||||
|
when(configurationStoreFactory.getStore(any())).thenReturn(configurationStore);
|
||||||
|
doNothing().when(configurationStore).set(configurationStoreCaptor.capture());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readWrite")
|
@SubjectAware(username = "readWrite")
|
||||||
public void shouldGetGitConfig() throws URISyntaxException, IOException {
|
public void shouldGetGitConfig() throws URISyntaxException {
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
|
|
||||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
|
||||||
String responseString = response.getContentAsString();
|
String responseString = response.getContentAsString();
|
||||||
ObjectNode responseJson = new ObjectMapper().readValue(responseString, ObjectNode.class);
|
|
||||||
|
|
||||||
assertTrue(responseString.contains("\"disabled\":false"));
|
assertTrue(responseString.contains("\"disabled\":false"));
|
||||||
assertTrue(responseString.contains("\"gcExpression\":\"valid Git GC Cron Expression\""));
|
assertTrue(responseString.contains("\"gcExpression\":\"valid Git GC Cron Expression\""));
|
||||||
@@ -88,7 +115,7 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readWrite")
|
@SubjectAware(username = "readWrite")
|
||||||
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException, IOException {
|
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException {
|
||||||
when(repositoryHandler.getConfig()).thenReturn(null);
|
when(repositoryHandler.getConfig()).thenReturn(null);
|
||||||
|
|
||||||
MockHttpResponse response = get();
|
MockHttpResponse response = get();
|
||||||
@@ -124,12 +151,84 @@ public class GitConfigResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@SubjectAware(username = "readOnly")
|
||||||
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, IOException {
|
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException {
|
||||||
thrown.expectMessage("Subject does not have permission [configuration:write:git]");
|
thrown.expectMessage("Subject does not have permission [configuration:write:git]");
|
||||||
|
|
||||||
put();
|
put();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "writeOnly")
|
||||||
|
public void shouldReadDefaultRepositoryConfig() throws URISyntaxException {
|
||||||
|
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X"));
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X");
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
assertThat(response.getContentAsString())
|
||||||
|
.contains("\"defaultBranch\":null")
|
||||||
|
.contains("self")
|
||||||
|
.contains("update");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "readOnly")
|
||||||
|
public void shouldNotHaveUpdateLinkForReadOnlyUser() throws URISyntaxException {
|
||||||
|
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X"));
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X");
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
assertThat(response.getContentAsString())
|
||||||
|
.contains("\"defaultBranch\":null")
|
||||||
|
.contains("self")
|
||||||
|
.doesNotContain("update");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "writeOnly")
|
||||||
|
public void shouldReadStoredRepositoryConfig() throws URISyntaxException {
|
||||||
|
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X"));
|
||||||
|
GitRepositoryConfig gitRepositoryConfig = new GitRepositoryConfig();
|
||||||
|
gitRepositoryConfig.setDefaultBranch("test");
|
||||||
|
when(configurationStore.get()).thenReturn(gitRepositoryConfig);
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X");
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||||
|
assertThat(response.getContentAsString())
|
||||||
|
.contains("\"defaultBranch\":\"test\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "writeOnly")
|
||||||
|
public void shouldStoreChangedRepositoryConfig() throws URISyntaxException {
|
||||||
|
when(repositoryManager.get(new NamespaceAndName("space", "X"))).thenReturn(new Repository("id", "git", "space", "X"));
|
||||||
|
|
||||||
|
MockHttpRequest request = MockHttpRequest
|
||||||
|
.put("/" + GitConfigResource.GIT_CONFIG_PATH_V2 + "/space/X")
|
||||||
|
.contentType(GitVndMediaType.GIT_REPOSITORY_CONFIG)
|
||||||
|
.content("{\"defaultBranch\": \"new\"}".getBytes());
|
||||||
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
|
|
||||||
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
|
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||||
|
assertThat(configurationStoreCaptor.getValue())
|
||||||
|
.isInstanceOfSatisfying(GitRepositoryConfig.class, x -> { })
|
||||||
|
.extracting("defaultBranch")
|
||||||
|
.containsExactly("new");
|
||||||
|
}
|
||||||
|
|
||||||
private MockHttpResponse get() throws URISyntaxException {
|
private MockHttpResponse get() throws URISyntaxException {
|
||||||
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2);
|
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2);
|
||||||
MockHttpResponse response = new MockHttpResponse();
|
MockHttpResponse response = new MockHttpResponse();
|
||||||
@@ -153,6 +252,4 @@ public class GitConfigResourceTest {
|
|||||||
config.setDisabled(false);
|
config.setDisabled(false);
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.io.Resources;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.util.Providers;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
|
import sonia.scm.repository.Repository;
|
||||||
|
import sonia.scm.repository.RepositoryManager;
|
||||||
|
import sonia.scm.repository.api.Command;
|
||||||
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
|
import sonia.scm.web.JsonEnricherContext;
|
||||||
|
import sonia.scm.web.VndMediaType;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class GitRepositoryConfigEnricherTest {
|
||||||
|
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
private GitRepositoryConfigEnricher linkEnricher;
|
||||||
|
private JsonNode rootNode;
|
||||||
|
@Mock
|
||||||
|
private RepositoryManager manager;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void globalSetUp() {
|
||||||
|
ScmPathInfoStore pathInfoStore = new ScmPathInfoStore();
|
||||||
|
pathInfoStore.set(() -> URI.create("/"));
|
||||||
|
Provider<ScmPathInfoStore> pathInfoStoreProvider = Providers.of(pathInfoStore);
|
||||||
|
|
||||||
|
linkEnricher = new GitRepositoryConfigEnricher(pathInfoStoreProvider, objectMapper, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ForSingleRepository {
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
URL resource = Resources.getResource("sonia/scm/repository/repository-001.json");
|
||||||
|
rootNode = objectMapper.readTree(resource);
|
||||||
|
|
||||||
|
when(manager.get(new NamespaceAndName("scmadmin", "web-resources"))).thenReturn(new Repository("id", "git", "scmadmin", "web-resources"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldEnrichGitRepositories() {
|
||||||
|
JsonEnricherContext context = new JsonEnricherContext(
|
||||||
|
URI.create("/"),
|
||||||
|
MediaType.valueOf(VndMediaType.REPOSITORY),
|
||||||
|
rootNode
|
||||||
|
);
|
||||||
|
|
||||||
|
linkEnricher.enrich(context);
|
||||||
|
|
||||||
|
String configLink = context.getResponseEntity()
|
||||||
|
.get("_links")
|
||||||
|
.get("configuration")
|
||||||
|
.get("href")
|
||||||
|
.asText();
|
||||||
|
|
||||||
|
assertThat(configLink).isEqualTo("/v2/config/git/scmadmin/web-resources");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotEnrichOtherRepositories() {
|
||||||
|
when(manager.get(new NamespaceAndName("scmadmin", "web-resources"))).thenReturn(new Repository("id", "hg", "scmadmin", "web-resources"));
|
||||||
|
|
||||||
|
JsonEnricherContext context = new JsonEnricherContext(
|
||||||
|
URI.create("/"),
|
||||||
|
MediaType.valueOf(VndMediaType.REPOSITORY),
|
||||||
|
rootNode
|
||||||
|
);
|
||||||
|
|
||||||
|
linkEnricher.enrich(context);
|
||||||
|
|
||||||
|
JsonNode configLink = context.getResponseEntity()
|
||||||
|
.get("_links")
|
||||||
|
.get("configuration");
|
||||||
|
|
||||||
|
assertThat(configLink).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class ForRepositoryCollection {
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() throws IOException {
|
||||||
|
URL resource = Resources.getResource("sonia/scm/repository/repository-collection-001.json");
|
||||||
|
rootNode = objectMapper.readTree(resource);
|
||||||
|
|
||||||
|
when(manager.get(new NamespaceAndName("scmadmin", "web-resources"))).thenReturn(new Repository("id", "git", "scmadmin", "web-resources"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldEnrichAllRepositories() {
|
||||||
|
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("configuration")
|
||||||
|
.get("href")
|
||||||
|
.asText();
|
||||||
|
|
||||||
|
assertThat(configLink).isEqualTo("/v2/config/git/scmadmin/web-resources");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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("configuration");
|
||||||
|
|
||||||
|
assertThat(hasNewPullRequestLink).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2014, Sebastian Sdorra
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package sonia.scm.repository;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.io.Files;
|
||||||
|
import org.eclipse.jgit.api.Git;
|
||||||
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class GitHeadModifierTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private GitRepositoryHandler repositoryHandler;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private GitHeadModifier modifier;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEnsure() throws IOException, GitAPIException {
|
||||||
|
Repository repository = RepositoryTestData.createHeartOfGold("git");
|
||||||
|
File headFile = create(repository, "master");
|
||||||
|
|
||||||
|
boolean result = modifier.ensure(repository, "develop");
|
||||||
|
|
||||||
|
assertEquals("ref: refs/heads/develop", Files.readFirstLine(headFile, Charsets.UTF_8));
|
||||||
|
assertTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEnsureWithSameBranch() throws IOException, GitAPIException {
|
||||||
|
Repository repository = RepositoryTestData.createHeartOfGold("git");
|
||||||
|
create(repository, "develop");
|
||||||
|
|
||||||
|
boolean result = modifier.ensure(repository, "develop");
|
||||||
|
|
||||||
|
assertFalse(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File create(Repository repository, String head) throws IOException, GitAPIException {
|
||||||
|
File directory = temporaryFolder.newFolder();
|
||||||
|
|
||||||
|
Git.init()
|
||||||
|
.setBare(true)
|
||||||
|
.setDirectory(directory)
|
||||||
|
.call();
|
||||||
|
|
||||||
|
File headFile = new File(directory, "HEAD");
|
||||||
|
Files.write(String.format("ref: refs/heads/%s\n", head), headFile, Charsets.UTF_8);
|
||||||
|
|
||||||
|
when(repositoryHandler.getDirectory(repository.getId())).thenReturn(directory);
|
||||||
|
|
||||||
|
return headFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||||
public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2014, Sebastian Sdorra
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
*
|
|
||||||
* 1. Redistributions of source code must retain the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
* this list of conditions and the following disclaimer in the documentation
|
|
||||||
* and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from this
|
|
||||||
* software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* http://bitbucket.org/sdorra/scm-manager
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package sonia.scm.repository;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
import org.junit.Before;
|
|
||||||
import sonia.scm.HandlerEventType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for {@link GitRepositoryModifyListener}.
|
|
||||||
*
|
|
||||||
* @author Sebastian Sdorra
|
|
||||||
*/
|
|
||||||
public class GitRepositoryModifyListenerTest {
|
|
||||||
|
|
||||||
private GitRepositoryModifyTestListener repositoryModifyListener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up test object.
|
|
||||||
*/
|
|
||||||
@Before
|
|
||||||
public void setUpObjectUnderTest(){
|
|
||||||
repositoryModifyListener = new GitRepositoryModifyTestListener();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests happy path.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testHandleEvent() {
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.MODIFY, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNotNull(repositoryModifyListener.repository);
|
|
||||||
assertSame(current, repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests with new default branch.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithNewDefaultBranch() {
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.MODIFY, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNotNull(repositoryModifyListener.repository);
|
|
||||||
assertSame(current, repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests with non git repositories.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testNonGitRepository(){
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("hg");
|
|
||||||
old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("hg");
|
|
||||||
current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.MODIFY, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNull(repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests without default branch.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithoutDefaultBranch(){
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.MODIFY, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNull(repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests with non modify event.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testNonModifyEvent(){
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "develop");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.CREATE, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNull(repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests with non git repositories.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testNoModification(){
|
|
||||||
Repository old = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
old.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master");
|
|
||||||
Repository current = RepositoryTestData.createHeartOfGold("git");
|
|
||||||
current.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "master");
|
|
||||||
|
|
||||||
RepositoryModificationEvent event = new RepositoryModificationEvent(HandlerEventType.MODIFY, current, old);
|
|
||||||
repositoryModifyListener.handleEvent(event);
|
|
||||||
|
|
||||||
assertNull(repositoryModifyListener.repository);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class GitRepositoryModifyTestListener extends GitRepositoryModifyListener {
|
|
||||||
|
|
||||||
private Repository repository;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void sendClearRepositoryCacheEvent(Repository repository) {
|
|
||||||
this.repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -35,6 +35,11 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStore;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -51,6 +56,7 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase
|
|||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
|
context.setConfig(new GitRepositoryConfig());
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +71,7 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase
|
|||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
context = new GitContext(repositoryDirectory, repository);
|
context = new GitContext(repositoryDirectory, repository, new GitRepositoryConfigStoreProvider(InMemoryConfigurationStoreFactory.create()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|||||||
@@ -35,9 +35,11 @@ package sonia.scm.repository.spi;
|
|||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.BlameLine;
|
import sonia.scm.repository.BlameLine;
|
||||||
import sonia.scm.repository.BlameResult;
|
import sonia.scm.repository.BlameResult;
|
||||||
import sonia.scm.repository.GitConstants;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision());
|
assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision());
|
||||||
|
|
||||||
// set default branch and test again
|
// set default branch and test again
|
||||||
repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch");
|
createContext().setConfig(new GitRepositoryConfig("test-branch"));
|
||||||
result = createCommand().getBlameResult(request);
|
result = createCommand().getBlameResult(request);
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
assertEquals(1, result.getTotal());
|
assertEquals(1, result.getTotal());
|
||||||
|
|||||||
@@ -32,9 +32,11 @@
|
|||||||
package sonia.scm.repository.spi;
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.BrowserResult;
|
import sonia.scm.repository.BrowserResult;
|
||||||
import sonia.scm.repository.FileObject;
|
import sonia.scm.repository.FileObject;
|
||||||
import sonia.scm.repository.GitConstants;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -78,7 +80,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExplicitDefaultBranch() throws IOException {
|
public void testExplicitDefaultBranch() throws IOException {
|
||||||
repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch");
|
createContext().setConfig(new GitRepositoryConfig("test-branch"));
|
||||||
|
|
||||||
FileObject root = createCommand().getBrowserResult(new BrowseCommandRequest()).getFile();
|
FileObject root = createCommand().getBrowserResult(new BrowseCommandRequest()).getFile();
|
||||||
assertNotNull(root);
|
assertNotNull(root);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.GitConstants;
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -67,7 +67,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase {
|
|||||||
assertEquals("a\nline for blame", execute(request));
|
assertEquals("a\nline for blame", execute(request));
|
||||||
|
|
||||||
// set default branch for repository and check again
|
// set default branch for repository and check again
|
||||||
repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch");
|
createContext().setConfig(new GitRepositoryConfig("test-branch"));
|
||||||
assertEquals("a and b", execute(request));
|
assertEquals("a and b", execute(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ import org.eclipse.jgit.api.errors.GitAPIException;
|
|||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -103,7 +105,7 @@ public class GitIncomingCommandTest
|
|||||||
|
|
||||||
commit(outgoing, "added a");
|
commit(outgoing, "added a");
|
||||||
|
|
||||||
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory, null), incomingRepository);
|
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())), incomingRepository);
|
||||||
PullCommandRequest req = new PullCommandRequest();
|
PullCommandRequest req = new PullCommandRequest();
|
||||||
req.setRemoteRepository(outgoingRepository);
|
req.setRemoteRepository(outgoingRepository);
|
||||||
pull.pull(req);
|
pull.pull(req);
|
||||||
@@ -187,7 +189,7 @@ public class GitIncomingCommandTest
|
|||||||
*/
|
*/
|
||||||
private GitIncomingCommand createCommand()
|
private GitIncomingCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null),
|
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||||
incomingRepository);
|
incomingRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2010, Sebastian Sdorra
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived from this
|
||||||
|
* software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* http://bitbucket.org/sdorra/scm-manager
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
package sonia.scm.repository.spi;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for {@link GitLogCommand} with an ancestor commit. This test uses the following git repository:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* * 86e9ca0 (HEAD -> b) b5
|
||||||
|
* * d69edb3 Merge branch 'master' into b
|
||||||
|
* |\
|
||||||
|
* | * 946a8db (master) f
|
||||||
|
* | * b19b9cc e
|
||||||
|
* * | 3d6109c b4
|
||||||
|
* * | 6330653 b3
|
||||||
|
* * | a49a28e Merge branch 'master' into b
|
||||||
|
* |\ \
|
||||||
|
* | |/
|
||||||
|
* | * 0235584 d
|
||||||
|
* | * 20251c5 c
|
||||||
|
* * | 5023b85 b2
|
||||||
|
* * | 201ecc1 b1
|
||||||
|
* |/
|
||||||
|
* * 36b19e4 b
|
||||||
|
* * c2190a9 a
|
||||||
|
* </pre>
|
||||||
|
* @author Sebastian Sdorra
|
||||||
|
*/
|
||||||
|
public class GitLogCommandAncestorTest extends AbstractGitCommandTestBase
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected String getZippedRepositoryResource()
|
||||||
|
{
|
||||||
|
return "sonia/scm/repository/spi/scm-git-ancestor-test.zip";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAncestor()
|
||||||
|
{
|
||||||
|
LogCommandRequest request = new LogCommandRequest();
|
||||||
|
|
||||||
|
request.setBranch("b");
|
||||||
|
request.setAncestorChangeset("master");
|
||||||
|
|
||||||
|
ChangesetPagingResult result = createCommand().getChangesets(request);
|
||||||
|
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(7, result.getTotal());
|
||||||
|
assertEquals(7, result.getChangesets().size());
|
||||||
|
|
||||||
|
assertEquals("86e9ca012202b36865373a63c12ef4f4353506cd", result.getChangesets().get(0).getId());
|
||||||
|
assertEquals("d69edb314d07ab20ad626e3101597702d3510b5d", result.getChangesets().get(1).getId());
|
||||||
|
assertEquals("3d6109c4c830e91eaf12ac6a331a5fccd670fe3c", result.getChangesets().get(2).getId());
|
||||||
|
assertEquals("63306538d06924d6b254f86541c638021c001141", result.getChangesets().get(3).getId());
|
||||||
|
assertEquals("a49a28e0beb0ab55f985598d05b8628c2231c9b6", result.getChangesets().get(4).getId());
|
||||||
|
assertEquals("5023b850c2077db857593a3c0269329c254a370d", result.getChangesets().get(5).getId());
|
||||||
|
assertEquals("201ecc1131e6b99fb0a0fe9dcbc8c044383e1a07", result.getChangesets().get(6).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private GitLogCommand createCommand()
|
||||||
|
{
|
||||||
|
return new GitLogCommand(createContext(), repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -36,9 +36,11 @@ package sonia.scm.repository.spi;
|
|||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.event.ScmEventBus;
|
||||||
import sonia.scm.repository.Changeset;
|
import sonia.scm.repository.Changeset;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
import sonia.scm.repository.GitConstants;
|
import sonia.scm.repository.ClearRepositoryCacheEvent;
|
||||||
|
import sonia.scm.repository.GitRepositoryConfig;
|
||||||
import sonia.scm.repository.Modifications;
|
import sonia.scm.repository.Modifications;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -78,7 +80,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
|||||||
assertTrue(result.getChangesets().stream().allMatch(r -> r.getBranches().isEmpty()));
|
assertTrue(result.getChangesets().stream().allMatch(r -> r.getBranches().isEmpty()));
|
||||||
|
|
||||||
// set default branch and fetch again
|
// set default branch and fetch again
|
||||||
repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch");
|
createContext().setConfig(new GitRepositoryConfig("test-branch"));
|
||||||
|
|
||||||
result = createCommand().getChangesets(new LogCommandRequest());
|
result = createCommand().getChangesets(new LogCommandRequest());
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, null), incomingRepository);
|
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, null, null), incomingRepository);
|
||||||
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, null), outgoingRepository);
|
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, null, null), outgoingRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,12 +63,12 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void pushOutgoingAndPullIncoming() throws IOException {
|
void pushOutgoingAndPullIncoming() throws IOException {
|
||||||
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, null),
|
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, null, null),
|
||||||
outgoingRepository);
|
outgoingRepository);
|
||||||
PushCommandRequest request = new PushCommandRequest();
|
PushCommandRequest request = new PushCommandRequest();
|
||||||
request.setRemoteRepository(incomingRepository);
|
request.setRemoteRepository(incomingRepository);
|
||||||
cmd.push(request);
|
cmd.push(request);
|
||||||
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, null),
|
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, null, null),
|
||||||
incomingRepository);
|
incomingRepository);
|
||||||
PullCommandRequest pullRequest = new PullCommandRequest();
|
PullCommandRequest pullRequest = new PullCommandRequest();
|
||||||
pullRequest.setRemoteRepository(incomingRepository);
|
pullRequest.setRemoteRepository(incomingRepository);
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ package sonia.scm.repository.spi;
|
|||||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
import sonia.scm.repository.ChangesetPagingResult;
|
||||||
|
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
@@ -104,7 +106,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
|||||||
commit(outgoing, "added a");
|
commit(outgoing, "added a");
|
||||||
|
|
||||||
GitPushCommand push = new GitPushCommand(handler,
|
GitPushCommand push = new GitPushCommand(handler,
|
||||||
new GitContext(outgoingDirectory, null),
|
new GitContext(outgoingDirectory, null, null),
|
||||||
outgoingRepository);
|
outgoingRepository);
|
||||||
PushCommandRequest req = new PushCommandRequest();
|
PushCommandRequest req = new PushCommandRequest();
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
|||||||
*/
|
*/
|
||||||
private GitOutgoingCommand createCommand()
|
private GitOutgoingCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null),
|
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||||
outgoingRepository);
|
outgoingRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase
|
|||||||
*/
|
*/
|
||||||
private GitPushCommand createCommand()
|
private GitPushCommand createCommand()
|
||||||
{
|
{
|
||||||
return new GitPushCommand(handler, new GitContext(outgoingDirectory, null),
|
return new GitPushCommand(handler, new GitContext(outgoingDirectory, null, null),
|
||||||
outgoingRepository);
|
outgoingRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[users]
|
[users]
|
||||||
readOnly = secret, reader
|
readOnly = secret, reader, repoRead
|
||||||
writeOnly = secret, writer
|
writeOnly = secret, writer, repoWrite
|
||||||
readWrite = secret, readerWriter
|
readWrite = secret, readerWriter
|
||||||
admin = secret, admin
|
admin = secret, admin
|
||||||
|
|
||||||
@@ -9,3 +9,5 @@ reader = configuration:read:git
|
|||||||
writer = configuration:write:git
|
writer = configuration:write:git
|
||||||
readerWriter = configuration:*:git
|
readerWriter = configuration:*:git
|
||||||
admin = *
|
admin = *
|
||||||
|
repoRead = repository:read:*
|
||||||
|
repoWrite = repository:modify:*
|
||||||
|
|||||||
@@ -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/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -37,6 +37,8 @@ package sonia.scm.store;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* In memory configuration store factory for testing purposes.
|
* In memory configuration store factory for testing purposes.
|
||||||
|
*
|
||||||
|
* Use {@link #create()} to get a store that creates the same store on each request.
|
||||||
*
|
*
|
||||||
* @author Sebastian Sdorra
|
* @author Sebastian Sdorra
|
||||||
*/
|
*/
|
||||||
@@ -44,6 +46,10 @@ public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFact
|
|||||||
|
|
||||||
private ConfigurationStore store;
|
private ConfigurationStore store;
|
||||||
|
|
||||||
|
public static ConfigurationStoreFactory create() {
|
||||||
|
return new InMemoryConfigurationStoreFactory(new InMemoryConfigurationStore());
|
||||||
|
}
|
||||||
|
|
||||||
public InMemoryConfigurationStoreFactory() {
|
public InMemoryConfigurationStoreFactory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Branch } from "@scm-manager/ui-types";
|
import type {Branch} from "@scm-manager/ui-types";
|
||||||
import DropDown from "../components/DropDown";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import { compose } from "redux";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import DropDown from "./forms/DropDown";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
zeroflex: {
|
zeroflex: {
|
||||||
@@ -25,11 +23,11 @@ const styles = {
|
|||||||
type Props = {
|
type Props = {
|
||||||
branches: Branch[], // TODO: Use generics?
|
branches: Branch[], // TODO: Use generics?
|
||||||
selected: (branch?: Branch) => void,
|
selected: (branch?: Branch) => void,
|
||||||
selectedBranch: string,
|
selectedBranch?: string,
|
||||||
|
label: string,
|
||||||
|
|
||||||
// context props
|
// context props
|
||||||
classes: Object,
|
classes: Object
|
||||||
t: string => string
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = { selectedBranch?: Branch };
|
type State = { selectedBranch?: Branch };
|
||||||
@@ -41,13 +39,12 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.branches
|
const selectedBranch = this.props.branches.find(branch => branch.name === this.props.selectedBranch);
|
||||||
.filter(branch => branch.name === this.props.selectedBranch)
|
this.setState({ selectedBranch });
|
||||||
.forEach(branch => this.setState({ selectedBranch: branch }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { branches, classes, t } = this.props;
|
const { branches, classes, label } = this.props;
|
||||||
|
|
||||||
if (branches) {
|
if (branches) {
|
||||||
return (
|
return (
|
||||||
@@ -66,7 +63,7 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
classes.minWidthOfLabel
|
classes.minWidthOfLabel
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<label className="label">{t("branch-selector.label")}</label>
|
<label className="label">{label}</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="field-body">
|
<div className="field-body">
|
||||||
<div className="field is-narrow">
|
<div className="field is-narrow">
|
||||||
@@ -93,6 +90,12 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
|
|
||||||
branchSelected = (branchName: string) => {
|
branchSelected = (branchName: string) => {
|
||||||
const { branches, selected } = this.props;
|
const { branches, selected } = this.props;
|
||||||
|
|
||||||
|
if (!branchName) {
|
||||||
|
this.setState({ selectedBranch: undefined });
|
||||||
|
selected(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const branch = branches.find(b => b.name === branchName);
|
const branch = branches.find(b => b.name === branchName);
|
||||||
|
|
||||||
selected(branch);
|
selected(branch);
|
||||||
@@ -100,7 +103,4 @@ class BranchSelector extends React.Component<Props, State> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default injectSheet(styles)(BranchSelector);
|
||||||
injectSheet(styles),
|
|
||||||
translate("repos")
|
|
||||||
)(BranchSelector);
|
|
||||||
@@ -7,5 +7,6 @@ export { default as InputField } from "./InputField.js";
|
|||||||
export { default as Select } from "./Select.js";
|
export { default as Select } from "./Select.js";
|
||||||
export { default as Textarea } from "./Textarea.js";
|
export { default as Textarea } from "./Textarea.js";
|
||||||
export { default as PasswordConfirmation } from "./PasswordConfirmation.js";
|
export { default as PasswordConfirmation } from "./PasswordConfirmation.js";
|
||||||
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon";
|
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon.js";
|
||||||
|
export { default as DropDown } from "./DropDown.js";
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export { default as HelpIcon } from "./HelpIcon";
|
|||||||
export { default as Tooltip } from "./Tooltip";
|
export { default as Tooltip } from "./Tooltip";
|
||||||
export { getPageFromMatch } from "./urls";
|
export { getPageFromMatch } from "./urls";
|
||||||
export { default as Autocomplete} from "./Autocomplete";
|
export { default as Autocomplete} from "./Autocomplete";
|
||||||
|
export { default as BranchSelector } from "./BranchSelector";
|
||||||
|
|
||||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR, CONFLICT_ERROR } from "./apiclient.js";
|
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR, CONFLICT_ERROR } from "./apiclient.js";
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,16 @@ class LoadingDiff extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
this.fetchDiff();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
if(prevProps.url !== this.props.url){
|
||||||
|
this.fetchDiff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchDiff = () => {
|
||||||
const { url } = this.props;
|
const { url } = this.props;
|
||||||
apiClient
|
apiClient
|
||||||
.get(url)
|
.get(url)
|
||||||
@@ -46,15 +56,18 @@ class LoadingDiff extends React.Component<Props, State> {
|
|||||||
error
|
error
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { diff, loading, error } = this.state;
|
const { diff, loading, error } = this.state;
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ErrorNotification error={error} />;
|
return <ErrorNotification error={error} />;
|
||||||
} else if (loading || !diff) {
|
} else if (loading) {
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
} else {
|
} else if(!diff){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
return <Diff diff={diff} />;
|
return <Diff diff={diff} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,15 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
import { Route, withRouter } from "react-router-dom";
|
import { Route, withRouter } from "react-router-dom";
|
||||||
import Changesets from "./Changesets";
|
import Changesets from "./Changesets";
|
||||||
import BranchSelector from "./BranchSelector";
|
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
import {
|
||||||
|
BranchSelector,
|
||||||
|
ErrorNotification,
|
||||||
|
Loading
|
||||||
|
} from "@scm-manager/ui-components";
|
||||||
import {
|
import {
|
||||||
fetchBranches,
|
fetchBranches,
|
||||||
getBranches,
|
getBranches,
|
||||||
@@ -32,7 +36,8 @@ type Props = {
|
|||||||
|
|
||||||
// Context props
|
// Context props
|
||||||
history: any, // TODO flow type
|
history: any, // TODO flow type
|
||||||
match: any
|
match: any,
|
||||||
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class BranchRoot extends React.Component<Props> {
|
class BranchRoot extends React.Component<Props> {
|
||||||
@@ -92,10 +97,11 @@ class BranchRoot extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBranchSelector = () => {
|
renderBranchSelector = () => {
|
||||||
const { repository, branches, selected } = this.props;
|
const { repository, branches, selected, t } = this.props;
|
||||||
if (repository._links.branches) {
|
if (repository._links.branches) {
|
||||||
return (
|
return (
|
||||||
<BranchSelector
|
<BranchSelector
|
||||||
|
label={t("branch-selector.label")}
|
||||||
branches={branches}
|
branches={branches}
|
||||||
selectedBranch={selected}
|
selectedBranch={selected}
|
||||||
selected={(b: Branch) => {
|
selected={(b: Branch) => {
|
||||||
@@ -133,6 +139,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
|||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withRouter,
|
withRouter,
|
||||||
|
translate("repos"),
|
||||||
connect(
|
connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
mapDispatchToProps
|
mapDispatchToProps
|
||||||
|
|||||||
@@ -1,32 +1,19 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {deleteRepo, fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
|
||||||
deleteRepo,
|
|
||||||
fetchRepoByName,
|
|
||||||
getFetchRepoFailure,
|
|
||||||
getRepository,
|
|
||||||
isFetchRepoPending
|
|
||||||
} from "../modules/repos";
|
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { Route, Switch } from "react-router-dom";
|
import {Route, Switch} from "react-router-dom";
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
import type {Repository} from "@scm-manager/ui-types";
|
||||||
|
|
||||||
import {
|
import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components";
|
||||||
ErrorPage,
|
import {translate} from "react-i18next";
|
||||||
Loading,
|
|
||||||
Navigation,
|
|
||||||
NavLink,
|
|
||||||
Page,
|
|
||||||
Section
|
|
||||||
} from "@scm-manager/ui-components";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import RepositoryDetails from "../components/RepositoryDetails";
|
import RepositoryDetails from "../components/RepositoryDetails";
|
||||||
import DeleteNavAction from "../components/DeleteNavAction";
|
import DeleteNavAction from "../components/DeleteNavAction";
|
||||||
import Edit from "../containers/Edit";
|
import Edit from "../containers/Edit";
|
||||||
import Permissions from "../permissions/containers/Permissions";
|
import Permissions from "../permissions/containers/Permissions";
|
||||||
|
|
||||||
import type { History } from "history";
|
import type {History} from "history";
|
||||||
import EditNavLink from "../components/EditNavLink";
|
import EditNavLink from "../components/EditNavLink";
|
||||||
|
|
||||||
import BranchRoot from "./ChangesetsRoot";
|
import BranchRoot from "./ChangesetsRoot";
|
||||||
@@ -34,8 +21,8 @@ import ChangesetView from "./ChangesetView";
|
|||||||
import PermissionsNavLink from "../components/PermissionsNavLink";
|
import PermissionsNavLink from "../components/PermissionsNavLink";
|
||||||
import Sources from "../sources/containers/Sources";
|
import Sources from "../sources/containers/Sources";
|
||||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
import {getRepositoriesLink} from "../../modules/indexResource";
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
namespace: string,
|
namespace: string,
|
||||||
@@ -200,15 +187,15 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
label={t("repository-root.sources")}
|
label={t("repository-root.sources")}
|
||||||
activeOnlyWhenExact={false}
|
activeOnlyWhenExact={false}
|
||||||
/>
|
/>
|
||||||
|
<PermissionsNavLink
|
||||||
|
permissionUrl={`${url}/permissions`}
|
||||||
|
repository={repository}
|
||||||
|
/>
|
||||||
<ExtensionPoint
|
<ExtensionPoint
|
||||||
name="repository.navigation"
|
name="repository.navigation"
|
||||||
props={extensionProps}
|
props={extensionProps}
|
||||||
renderAll={true}
|
renderAll={true}
|
||||||
/>
|
/>
|
||||||
<PermissionsNavLink
|
|
||||||
permissionUrl={`${url}/permissions`}
|
|
||||||
repository={repository}
|
|
||||||
/>
|
|
||||||
</Section>
|
</Section>
|
||||||
<Section label={t("repository-root.actions-label")}>
|
<Section label={t("repository-root.actions-label")}>
|
||||||
<DeleteNavAction repository={repository} delete={this.delete} />
|
<DeleteNavAction repository={repository} delete={this.delete} />
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { withRouter } from "react-router-dom";
|
|||||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||||
import FileTree from "../components/FileTree";
|
import FileTree from "../components/FileTree";
|
||||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||||
import BranchSelector from "../../containers/BranchSelector";
|
import BranchSelector from "../../../../../scm-ui-components/packages/ui-components/src/BranchSelector";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
fetchBranches,
|
fetchBranches,
|
||||||
getBranches,
|
getBranches,
|
||||||
@@ -32,7 +33,8 @@ type Props = {
|
|||||||
|
|
||||||
// Context props
|
// Context props
|
||||||
history: any,
|
history: any,
|
||||||
match: any
|
match: any,
|
||||||
|
t: string => string
|
||||||
};
|
};
|
||||||
|
|
||||||
class Sources extends React.Component<Props> {
|
class Sources extends React.Component<Props> {
|
||||||
@@ -109,13 +111,14 @@ class Sources extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderBranchSelector = () => {
|
renderBranchSelector = () => {
|
||||||
const { branches, revision } = this.props;
|
const { branches, revision, t } = this.props;
|
||||||
|
|
||||||
if (branches) {
|
if (branches) {
|
||||||
return (
|
return (
|
||||||
<BranchSelector
|
<BranchSelector
|
||||||
branches={branches}
|
branches={branches}
|
||||||
selectedBranch={revision}
|
selectedBranch={revision}
|
||||||
|
label={t("branch-selector.label")}
|
||||||
selected={(b: Branch) => {
|
selected={(b: Branch) => {
|
||||||
this.branchSelected(b);
|
this.branchSelected(b);
|
||||||
}}
|
}}
|
||||||
@@ -160,6 +163,7 @@ const mapDispatchToProps = dispatch => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
translate("repos"),
|
||||||
withRouter,
|
withRouter,
|
||||||
connect(
|
connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
|||||||
@@ -115,7 +115,6 @@ $fa-font-path: "webfonts";
|
|||||||
.media {
|
.media {
|
||||||
.media-content {
|
.media-content {
|
||||||
width: calc(50% - 0.75rem);
|
width: calc(50% - 0.75rem);
|
||||||
max-height: 120px;
|
|
||||||
.shorten-text {
|
.shorten-text {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@@ -112,9 +112,7 @@ import sonia.scm.util.ScmConfigurationUtil;
|
|||||||
import sonia.scm.web.UserAgentParser;
|
import sonia.scm.web.UserAgentParser;
|
||||||
import sonia.scm.web.cgi.CGIExecutorFactory;
|
import sonia.scm.web.cgi.CGIExecutorFactory;
|
||||||
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
|
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
|
||||||
import sonia.scm.web.filter.AuthenticationFilter;
|
|
||||||
import sonia.scm.web.filter.LoggingFilter;
|
import sonia.scm.web.filter.LoggingFilter;
|
||||||
import sonia.scm.web.protocol.HttpProtocolServlet;
|
|
||||||
import sonia.scm.web.security.AdministrationContext;
|
import sonia.scm.web.security.AdministrationContext;
|
||||||
import sonia.scm.web.security.DefaultAdministrationContext;
|
import sonia.scm.web.security.DefaultAdministrationContext;
|
||||||
|
|
||||||
@@ -315,8 +313,6 @@ public class ScmServletModule extends ServletModule
|
|||||||
bind(TemplateEngineFactory.class);
|
bind(TemplateEngineFactory.class);
|
||||||
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
||||||
|
|
||||||
filter(HttpProtocolServlet.PATTERN).through(AuthenticationFilter.class);
|
|
||||||
|
|
||||||
// bind events
|
// bind events
|
||||||
// bind(LastModifiedUpdateListener.class);
|
// bind(LastModifiedUpdateListener.class);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ package sonia.scm.api.rest;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.ext.ExceptionMapper;
|
import javax.ws.rs.ext.ExceptionMapper;
|
||||||
|
|
||||||
@@ -92,6 +93,7 @@ public class StatusExceptionMapper<E extends Throwable>
|
|||||||
|
|
||||||
return Response.status(status)
|
return Response.status(status)
|
||||||
.entity(exception.getMessage())
|
.entity(exception.getMessage())
|
||||||
|
.type(MediaType.TEXT_PLAIN_TYPE)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package sonia.scm.web.filter;
|
||||||
|
|
||||||
|
import sonia.scm.Priority;
|
||||||
|
import sonia.scm.PushStateDispatcher;
|
||||||
|
import sonia.scm.config.ScmConfiguration;
|
||||||
|
import sonia.scm.filter.Filters;
|
||||||
|
import sonia.scm.filter.WebElement;
|
||||||
|
import sonia.scm.util.HttpUtil;
|
||||||
|
import sonia.scm.web.UserAgent;
|
||||||
|
import sonia.scm.web.UserAgentParser;
|
||||||
|
import sonia.scm.web.WebTokenGenerator;
|
||||||
|
import sonia.scm.web.protocol.HttpProtocolServlet;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static sonia.scm.util.HttpUtil.AUTHENTICATION_REALM;
|
||||||
|
import static sonia.scm.util.HttpUtil.HEADER_WWW_AUTHENTICATE;
|
||||||
|
|
||||||
|
@Priority(Filters.PRIORITY_AUTHENTICATION)
|
||||||
|
@WebElement(value = HttpProtocolServlet.PATTERN)
|
||||||
|
public class HttpProtocolServletAuthenticationFilter extends AuthenticationFilter {
|
||||||
|
|
||||||
|
private final PushStateDispatcher dispatcher;
|
||||||
|
private final UserAgentParser userAgentParser;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public HttpProtocolServletAuthenticationFilter(
|
||||||
|
ScmConfiguration configuration,
|
||||||
|
Set<WebTokenGenerator> tokenGenerators,
|
||||||
|
PushStateDispatcher dispatcher,
|
||||||
|
UserAgentParser userAgentParser) {
|
||||||
|
super(configuration, tokenGenerators);
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
this.userAgentParser = userAgentParser;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void sendUnauthorizedError(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
|
UserAgent userAgent = userAgentParser.parse(request);
|
||||||
|
if (userAgent.isBrowser()) {
|
||||||
|
dispatcher.dispatch(request, response, request.getRequestURI());
|
||||||
|
} else {
|
||||||
|
HttpUtil.sendUnauthorized(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user