mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 02:55:56 +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.
|
||||
*
|
||||
*
|
||||
* @param obejct configuration object to store
|
||||
* @param object configuration object to store
|
||||
*/
|
||||
void set(T object);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ public class AuthenticationFilter extends HttpFilter
|
||||
}
|
||||
else if (subject.isAuthenticated())
|
||||
{
|
||||
logger.trace("user is allready authenticated");
|
||||
logger.trace("user is already authenticated");
|
||||
processChain(request, response, chain, subject);
|
||||
}
|
||||
else if (isAnonymousAccessEnabled())
|
||||
|
||||
@@ -9,13 +9,17 @@ import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.web.GitVndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 GitConfigToGitConfigDtoMapper configToDtoMapper;
|
||||
private final GitRepositoryHandler repositoryHandler;
|
||||
private final Provider<GitRepositoryConfigResource> gitRepositoryConfigResource;
|
||||
|
||||
@Inject
|
||||
public GitConfigResource(GitConfigDtoToGitConfigMapper dtoToConfigMapper, GitConfigToGitConfigDtoMapper configToDtoMapper,
|
||||
GitRepositoryHandler repositoryHandler) {
|
||||
GitRepositoryHandler repositoryHandler, Provider<GitRepositoryConfigResource> gitRepositoryConfigResource) {
|
||||
this.dtoToConfigMapper = dtoToConfigMapper;
|
||||
this.configToDtoMapper = configToDtoMapper;
|
||||
this.repositoryHandler = repositoryHandler;
|
||||
this.gitRepositoryConfigResource = gitRepositoryConfigResource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,4 +94,9 @@ public class GitConfigResource {
|
||||
|
||||
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;
|
||||
|
||||
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.HandlerEventType;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigChangedEvent;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.plugin.Extension;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Repository listener which handles git related repository events.
|
||||
*
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.50
|
||||
*/
|
||||
@Extension
|
||||
@EagerSingleton
|
||||
public class GitRepositoryModifyListener {
|
||||
|
||||
/**
|
||||
* the logger for GitRepositoryModifyListener
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(GitRepositoryModifyListener.class);
|
||||
|
||||
|
||||
private final GitHeadModifier headModifier;
|
||||
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||
|
||||
@Inject
|
||||
public GitRepositoryModifyListener(GitHeadModifier headModifier, GitRepositoryConfigStoreProvider storeProvider) {
|
||||
this.headModifier = headModifier;
|
||||
this.storeProvider = storeProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives {@link RepositoryModificationEvent} and fires a {@link ClearRepositoryCacheEvent} if
|
||||
* the default branch of a git repository was modified.
|
||||
*
|
||||
*
|
||||
* @param event repository modification event
|
||||
*/
|
||||
@Subscribe
|
||||
public void handleEvent(RepositoryModificationEvent event){
|
||||
Repository repository = event.getItem();
|
||||
|
||||
if ( isModifyEvent(event) &&
|
||||
isGitRepository(event.getItem()) &&
|
||||
hasDefaultBranchChanged(event.getItemBeforeModification(), repository))
|
||||
{
|
||||
logger.info("git default branch of repository {} has changed, sending clear cache event", repository.getId());
|
||||
sendClearRepositoryCacheEvent(repository);
|
||||
public void handleEvent(GitRepositoryConfigChangedEvent event){
|
||||
Repository repository = event.getRepository();
|
||||
|
||||
String defaultBranch = storeProvider.get(repository).get().getDefaultBranch();
|
||||
if (defaultBranch != null) {
|
||||
headModifier.ensure(repository, defaultBranch);
|
||||
}
|
||||
}
|
||||
|
||||
@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.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.GitConstants;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -110,7 +109,7 @@ public class AbstractGitCommand
|
||||
|
||||
protected Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException {
|
||||
if ( Strings.isNullOrEmpty(requestedBranch) ) {
|
||||
String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH);
|
||||
String defaultBranchName = context.getConfig().getDefaultBranch();
|
||||
if (!Strings.isNullOrEmpty(defaultBranchName)) {
|
||||
return GitUtil.getBranchId(gitRepository, defaultBranchName);
|
||||
} else {
|
||||
|
||||
@@ -37,6 +37,8 @@ package sonia.scm.repository.spi;
|
||||
import org.slf4j.Logger;
|
||||
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.Repository;
|
||||
|
||||
@@ -68,10 +70,11 @@ public class GitContext implements Closeable
|
||||
* @param directory
|
||||
* @param repository
|
||||
*/
|
||||
public GitContext(File directory, Repository repository)
|
||||
public GitContext(File directory, Repository repository, GitRepositoryConfigStoreProvider storeProvider)
|
||||
{
|
||||
this.directory = directory;
|
||||
this.repository = repository;
|
||||
this.storeProvider = storeProvider;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -117,11 +120,25 @@ public class GitContext implements Closeable
|
||||
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 ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final File directory;
|
||||
private final Repository repository;
|
||||
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||
|
||||
/** Field description */
|
||||
private org.eclipse.jgit.lib.Repository gitRepository;
|
||||
|
||||
@@ -205,7 +205,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
||||
ObjectId ancestorId = null;
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) {
|
||||
ancestorId = computeCommonAncestor(request, repository, startId, branch);
|
||||
ancestorId = repository.resolve(request.getAncestorChangeset());
|
||||
}
|
||||
|
||||
revWalk = new RevWalk(repository);
|
||||
@@ -225,16 +225,15 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand
|
||||
revWalk.markStart(revWalk.lookupCommit(branch.getObjectId()));
|
||||
}
|
||||
|
||||
if (ancestorId != null) {
|
||||
revWalk.markUninteresting(revWalk.lookupCommit(ancestorId));
|
||||
}
|
||||
|
||||
Iterator<RevCommit> iterator = revWalk.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
RevCommit commit = iterator.next();
|
||||
|
||||
if (commit.getId().equals(ancestorId)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((counter >= start)
|
||||
&& ((limit < 0) || (counter < start + limit))) {
|
||||
changesetList.add(converter.createChangeset(commit));
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.repository.Feature;
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -73,10 +74,10 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository) {
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider) {
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
this.context = new GitContext(handler.getDirectory(repository.getId()), repository);
|
||||
this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
@@ -35,6 +35,7 @@ package sonia.scm.repository.spi;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -47,10 +48,12 @@ import sonia.scm.repository.Repository;
|
||||
public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
|
||||
private final GitRepositoryHandler handler;
|
||||
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||
|
||||
@Inject
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler) {
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider) {
|
||||
this.handler = handler;
|
||||
this.storeProvider = storeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,7 +61,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
GitRepositoryServiceProvider provider = null;
|
||||
|
||||
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||
provider = new GitRepositoryServiceProvider(handler, repository);
|
||||
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider);
|
||||
}
|
||||
|
||||
return provider;
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.eclipse.jgit.transport.ScmTransportProtocol;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import sonia.scm.api.v2.resources.GitConfigDtoToGitConfigMapper;
|
||||
import sonia.scm.api.v2.resources.GitConfigToGitConfigDtoMapper;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigMapper;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.GitWorkdirFactory;
|
||||
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(GitConfigToGitConfigDtoMapper.class).to(Mappers.getMapper(GitConfigToGitConfigDtoMapper.class).getClass());
|
||||
bind(GitRepositoryConfigMapper.class).to(Mappers.getMapper(GitRepositoryConfigMapper.class).getClass());
|
||||
|
||||
bind(GitWorkdirFactory.class).to(SimpleGitWorkdirFactory.class);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package sonia.scm.web;
|
||||
|
||||
public class GitVndMediaType {
|
||||
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() {
|
||||
}
|
||||
|
||||
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
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import React from "react";
|
||||
import {binder} from "@scm-manager/ui-extensions";
|
||||
import ProtocolInformation from "./ProtocolInformation";
|
||||
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 GitMergeInformation from "./GitMergeInformation";
|
||||
import RepositoryConfig from "./RepositoryConfig";
|
||||
|
||||
// repository
|
||||
|
||||
@@ -13,10 +15,29 @@ const gitPredicate = (props: Object) => {
|
||||
return props.repository && props.repository.type === "git";
|
||||
};
|
||||
|
||||
binder.bind("repos.repository-details.information", ProtocolInformation, gitPredicate);
|
||||
binder.bind("repos.repository-merge.information", GitMergeInformation, gitPredicate);
|
||||
binder.bind(
|
||||
"repos.repository-details.information",
|
||||
ProtocolInformation,
|
||||
gitPredicate
|
||||
);
|
||||
binder.bind(
|
||||
"repos.repository-merge.information",
|
||||
GitMergeInformation,
|
||||
gitPredicate
|
||||
);
|
||||
binder.bind("repos.repository-avatar", GitAvatar, gitPredicate);
|
||||
|
||||
cfgBinder.bindRepository(
|
||||
"/configuration",
|
||||
"scm-git-plugin.repo-config.link",
|
||||
"configuration",
|
||||
RepositoryConfig
|
||||
);
|
||||
// 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": {
|
||||
"information": {
|
||||
"clone" : "Clone the repository",
|
||||
"create" : "Create a new repository",
|
||||
"replace" : "Push an existing repository",
|
||||
"clone": "Clone the repository",
|
||||
"create": "Create a new repository",
|
||||
"replace": "Push an existing repository",
|
||||
"merge": {
|
||||
"heading": "How to merge source branch into target branch",
|
||||
"checkout": "1. Make sure your workspace is clean and checkout target branch",
|
||||
@@ -22,6 +22,16 @@
|
||||
"disabled": "Disabled",
|
||||
"disabledHelpText": "Enable or disable the Git plugin",
|
||||
"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;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.github.sdorra.shiro.ShiroRule;
|
||||
import com.github.sdorra.shiro.SubjectAware;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
@@ -14,22 +12,33 @@ import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import sonia.scm.repository.GitConfig;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
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 javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import static com.google.inject.util.Providers.of;
|
||||
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.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SubjectAware(
|
||||
@@ -55,30 +64,48 @@ public class GitConfigResourceTest {
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private ScmPathInfoStore scmPathInfoStore;
|
||||
|
||||
@Mock
|
||||
private RepositoryManager repositoryManager;
|
||||
|
||||
@InjectMocks
|
||||
private GitConfigToGitConfigDtoMapperImpl configToDtoMapper;
|
||||
@InjectMocks
|
||||
private GitRepositoryConfigMapperImpl repositoryConfigMapper;
|
||||
|
||||
@Mock
|
||||
private GitRepositoryHandler repositoryHandler;
|
||||
|
||||
@Mock(answer = Answers.CALLS_REAL_METHODS)
|
||||
private ConfigurationStoreFactory configurationStoreFactory;
|
||||
@Spy
|
||||
private ConfigurationStore<Object> configurationStore;
|
||||
@Captor
|
||||
private ArgumentCaptor<Object> configurationStoreCaptor;
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
GitConfig gitConfig = createConfiguration();
|
||||
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);
|
||||
when(scmPathInfoStore.get().getApiRestUri()).thenReturn(baseUri);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void initConfigStore() {
|
||||
when(configurationStoreFactory.getStore(any())).thenReturn(configurationStore);
|
||||
doNothing().when(configurationStore).set(configurationStoreCaptor.capture());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readWrite")
|
||||
public void shouldGetGitConfig() throws URISyntaxException, IOException {
|
||||
public void shouldGetGitConfig() throws URISyntaxException {
|
||||
MockHttpResponse response = get();
|
||||
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
|
||||
String responseString = response.getContentAsString();
|
||||
ObjectNode responseJson = new ObjectMapper().readValue(responseString, ObjectNode.class);
|
||||
|
||||
assertTrue(responseString.contains("\"disabled\":false"));
|
||||
assertTrue(responseString.contains("\"gcExpression\":\"valid Git GC Cron Expression\""));
|
||||
@@ -88,7 +115,7 @@ public class GitConfigResourceTest {
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readWrite")
|
||||
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException, IOException {
|
||||
public void shouldGetGitConfigEvenWhenItsEmpty() throws URISyntaxException {
|
||||
when(repositoryHandler.getConfig()).thenReturn(null);
|
||||
|
||||
MockHttpResponse response = get();
|
||||
@@ -124,12 +151,84 @@ public class GitConfigResourceTest {
|
||||
|
||||
@Test
|
||||
@SubjectAware(username = "readOnly")
|
||||
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, IOException {
|
||||
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException {
|
||||
thrown.expectMessage("Subject does not have permission [configuration:write:git]");
|
||||
|
||||
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 {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + GitConfigResource.GIT_CONFIG_PATH_V2);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
@@ -153,6 +252,4 @@ public class GitConfigResourceTest {
|
||||
config.setDisabled(false);
|
||||
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
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class GitRepositoryHandlerTest extends SimpleRepositoryHandlerTestBase {
|
||||
|
||||
@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 --------------------------------------------------------
|
||||
|
||||
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()
|
||||
{
|
||||
if (context != null) {
|
||||
context.setConfig(new GitRepositoryConfig());
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
@@ -65,7 +71,7 @@ public class AbstractGitCommandTestBase extends ZippedRepositoryTestBase
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
context = new GitContext(repositoryDirectory, repository);
|
||||
context = new GitContext(repositoryDirectory, repository, new GitRepositoryConfigStoreProvider(InMemoryConfigurationStoreFactory.create()));
|
||||
}
|
||||
|
||||
return context;
|
||||
|
||||
@@ -35,9 +35,11 @@ package sonia.scm.repository.spi;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.junit.Test;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.repository.BlameLine;
|
||||
import sonia.scm.repository.BlameResult;
|
||||
import sonia.scm.repository.GitConstants;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -73,7 +75,7 @@ public class GitBlameCommandTest extends AbstractGitCommandTestBase
|
||||
assertEquals("fcd0ef1831e4002ac43ea539f4094334c79ea9ec", result.getLine(1).getRevision());
|
||||
|
||||
// set default branch and test again
|
||||
repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch");
|
||||
createContext().setConfig(new GitRepositoryConfig("test-branch"));
|
||||
result = createCommand().getBlameResult(request);
|
||||
assertNotNull(result);
|
||||
assertEquals(1, result.getTotal());
|
||||
|
||||
@@ -32,9 +32,11 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.junit.Test;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
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.util.Collection;
|
||||
@@ -78,7 +80,7 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
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();
|
||||
assertNotNull(root);
|
||||
|
||||
@@ -38,7 +38,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.GitConstants;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -67,7 +67,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase {
|
||||
assertEquals("a\nline for blame", execute(request));
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,9 @@ import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -103,7 +105,7 @@ public class GitIncomingCommandTest
|
||||
|
||||
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();
|
||||
req.setRemoteRepository(outgoingRepository);
|
||||
pull.pull(req);
|
||||
@@ -187,7 +189,7 @@ public class GitIncomingCommandTest
|
||||
*/
|
||||
private GitIncomingCommand createCommand()
|
||||
{
|
||||
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null),
|
||||
return new GitIncomingCommand(handler, new GitContext(incomingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||
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 org.junit.Test;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.repository.Changeset;
|
||||
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 java.io.File;
|
||||
@@ -78,7 +80,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
||||
assertTrue(result.getChangesets().stream().allMatch(r -> r.getBranches().isEmpty()));
|
||||
|
||||
// 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());
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, null), incomingRepository);
|
||||
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, null), outgoingRepository);
|
||||
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory, null, null), incomingRepository);
|
||||
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory, null, null), outgoingRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,12 +63,12 @@ public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
|
||||
}
|
||||
|
||||
void pushOutgoingAndPullIncoming() throws IOException {
|
||||
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, null),
|
||||
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory, null, null),
|
||||
outgoingRepository);
|
||||
PushCommandRequest request = new PushCommandRequest();
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
cmd.push(request);
|
||||
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, null),
|
||||
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory, null, null),
|
||||
incomingRepository);
|
||||
PullCommandRequest pullRequest = new PullCommandRequest();
|
||||
pullRequest.setRemoteRepository(incomingRepository);
|
||||
|
||||
@@ -38,7 +38,9 @@ package sonia.scm.repository.spi;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.store.InMemoryConfigurationStoreFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -104,7 +106,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
commit(outgoing, "added a");
|
||||
|
||||
GitPushCommand push = new GitPushCommand(handler,
|
||||
new GitContext(outgoingDirectory, null),
|
||||
new GitContext(outgoingDirectory, null, null),
|
||||
outgoingRepository);
|
||||
PushCommandRequest req = new PushCommandRequest();
|
||||
|
||||
@@ -158,7 +160,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
*/
|
||||
private GitOutgoingCommand createCommand()
|
||||
{
|
||||
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null),
|
||||
return new GitOutgoingCommand(handler, new GitContext(outgoingDirectory, null, new GitRepositoryConfigStoreProvider(new InMemoryConfigurationStoreFactory())),
|
||||
outgoingRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase
|
||||
*/
|
||||
private GitPushCommand createCommand()
|
||||
{
|
||||
return new GitPushCommand(handler, new GitContext(outgoingDirectory, null),
|
||||
return new GitPushCommand(handler, new GitContext(outgoingDirectory, null, null),
|
||||
outgoingRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[users]
|
||||
readOnly = secret, reader
|
||||
writeOnly = secret, writer
|
||||
readOnly = secret, reader, repoRead
|
||||
writeOnly = secret, writer, repoWrite
|
||||
readWrite = secret, readerWriter
|
||||
admin = secret, admin
|
||||
|
||||
@@ -9,3 +9,5 @@ reader = configuration:read:git
|
||||
writer = configuration:write:git
|
||||
readerWriter = configuration:*:git
|
||||
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.
|
||||
*
|
||||
* Use {@link #create()} to get a store that creates the same store on each request.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@@ -44,6 +46,10 @@ public class InMemoryConfigurationStoreFactory implements ConfigurationStoreFact
|
||||
|
||||
private ConfigurationStore store;
|
||||
|
||||
public static ConfigurationStoreFactory create() {
|
||||
return new InMemoryConfigurationStoreFactory(new InMemoryConfigurationStore());
|
||||
}
|
||||
|
||||
public InMemoryConfigurationStoreFactory() {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import type { Branch } from "@scm-manager/ui-types";
|
||||
import DropDown from "../components/DropDown";
|
||||
import { translate } from "react-i18next";
|
||||
import type {Branch} from "@scm-manager/ui-types";
|
||||
import injectSheet from "react-jss";
|
||||
import { compose } from "redux";
|
||||
import classNames from "classnames";
|
||||
import DropDown from "./forms/DropDown";
|
||||
|
||||
const styles = {
|
||||
zeroflex: {
|
||||
@@ -25,11 +23,11 @@ const styles = {
|
||||
type Props = {
|
||||
branches: Branch[], // TODO: Use generics?
|
||||
selected: (branch?: Branch) => void,
|
||||
selectedBranch: string,
|
||||
selectedBranch?: string,
|
||||
label: string,
|
||||
|
||||
// context props
|
||||
classes: Object,
|
||||
t: string => string
|
||||
classes: Object
|
||||
};
|
||||
|
||||
type State = { selectedBranch?: Branch };
|
||||
@@ -41,13 +39,12 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.branches
|
||||
.filter(branch => branch.name === this.props.selectedBranch)
|
||||
.forEach(branch => this.setState({ selectedBranch: branch }));
|
||||
const selectedBranch = this.props.branches.find(branch => branch.name === this.props.selectedBranch);
|
||||
this.setState({ selectedBranch });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { branches, classes, t } = this.props;
|
||||
const { branches, classes, label } = this.props;
|
||||
|
||||
if (branches) {
|
||||
return (
|
||||
@@ -66,7 +63,7 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
classes.minWidthOfLabel
|
||||
)}
|
||||
>
|
||||
<label className="label">{t("branch-selector.label")}</label>
|
||||
<label className="label">{label}</label>
|
||||
</div>
|
||||
<div className="field-body">
|
||||
<div className="field is-narrow">
|
||||
@@ -93,6 +90,12 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
|
||||
branchSelected = (branchName: string) => {
|
||||
const { branches, selected } = this.props;
|
||||
|
||||
if (!branchName) {
|
||||
this.setState({ selectedBranch: undefined });
|
||||
selected(undefined);
|
||||
return;
|
||||
}
|
||||
const branch = branches.find(b => b.name === branchName);
|
||||
|
||||
selected(branch);
|
||||
@@ -100,7 +103,4 @@ class BranchSelector extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
export default compose(
|
||||
injectSheet(styles),
|
||||
translate("repos")
|
||||
)(BranchSelector);
|
||||
export default injectSheet(styles)(BranchSelector);
|
||||
@@ -7,5 +7,6 @@ export { default as InputField } from "./InputField.js";
|
||||
export { default as Select } from "./Select.js";
|
||||
export { default as Textarea } from "./Textarea.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 { getPageFromMatch } from "./urls";
|
||||
export { default as Autocomplete} from "./Autocomplete";
|
||||
export { default as BranchSelector } from "./BranchSelector";
|
||||
|
||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR, CONFLICT_ERROR } from "./apiclient.js";
|
||||
|
||||
|
||||
@@ -30,6 +30,16 @@ class LoadingDiff extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.fetchDiff();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if(prevProps.url !== this.props.url){
|
||||
this.fetchDiff();
|
||||
}
|
||||
}
|
||||
|
||||
fetchDiff = () => {
|
||||
const { url } = this.props;
|
||||
apiClient
|
||||
.get(url)
|
||||
@@ -46,15 +56,18 @@ class LoadingDiff extends React.Component<Props, State> {
|
||||
error
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { diff, loading, error } = this.state;
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
} else if (loading || !diff) {
|
||||
} else if (loading) {
|
||||
return <Loading />;
|
||||
} else {
|
||||
} else if(!diff){
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return <Diff diff={diff} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
|
||||
import React from "react";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import { Route, withRouter } from "react-router-dom";
|
||||
import Changesets from "./Changesets";
|
||||
import BranchSelector from "./BranchSelector";
|
||||
import { connect } from "react-redux";
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import {
|
||||
BranchSelector,
|
||||
ErrorNotification,
|
||||
Loading
|
||||
} from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
@@ -32,7 +36,8 @@ type Props = {
|
||||
|
||||
// Context props
|
||||
history: any, // TODO flow type
|
||||
match: any
|
||||
match: any,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class BranchRoot extends React.Component<Props> {
|
||||
@@ -92,10 +97,11 @@ class BranchRoot extends React.Component<Props> {
|
||||
}
|
||||
|
||||
renderBranchSelector = () => {
|
||||
const { repository, branches, selected } = this.props;
|
||||
const { repository, branches, selected, t } = this.props;
|
||||
if (repository._links.branches) {
|
||||
return (
|
||||
<BranchSelector
|
||||
label={t("branch-selector.label")}
|
||||
branches={branches}
|
||||
selectedBranch={selected}
|
||||
selected={(b: Branch) => {
|
||||
@@ -133,6 +139,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
translate("repos"),
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
deleteRepo,
|
||||
fetchRepoByName,
|
||||
getFetchRepoFailure,
|
||||
getRepository,
|
||||
isFetchRepoPending
|
||||
} from "../modules/repos";
|
||||
import {deleteRepo, fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import {connect} from "react-redux";
|
||||
import {Route, Switch} from "react-router-dom";
|
||||
import type {Repository} from "@scm-manager/ui-types";
|
||||
|
||||
import {
|
||||
ErrorPage,
|
||||
Loading,
|
||||
Navigation,
|
||||
NavLink,
|
||||
Page,
|
||||
Section
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components";
|
||||
import {translate} from "react-i18next";
|
||||
import RepositoryDetails from "../components/RepositoryDetails";
|
||||
import DeleteNavAction from "../components/DeleteNavAction";
|
||||
import Edit from "../containers/Edit";
|
||||
import Permissions from "../permissions/containers/Permissions";
|
||||
|
||||
import type { History } from "history";
|
||||
import type {History} from "history";
|
||||
import EditNavLink from "../components/EditNavLink";
|
||||
|
||||
import BranchRoot from "./ChangesetsRoot";
|
||||
@@ -34,8 +21,8 @@ import ChangesetView from "./ChangesetView";
|
||||
import PermissionsNavLink from "../components/PermissionsNavLink";
|
||||
import Sources from "../sources/containers/Sources";
|
||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import {getRepositoriesLink} from "../../modules/indexResource";
|
||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
namespace: string,
|
||||
@@ -200,15 +187,15 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
label={t("repository-root.sources")}
|
||||
activeOnlyWhenExact={false}
|
||||
/>
|
||||
<PermissionsNavLink
|
||||
permissionUrl={`${url}/permissions`}
|
||||
repository={repository}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="repository.navigation"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
/>
|
||||
<PermissionsNavLink
|
||||
permissionUrl={`${url}/permissions`}
|
||||
repository={repository}
|
||||
/>
|
||||
</Section>
|
||||
<Section label={t("repository-root.actions-label")}>
|
||||
<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 FileTree from "../components/FileTree";
|
||||
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 {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
@@ -32,7 +33,8 @@ type Props = {
|
||||
|
||||
// Context props
|
||||
history: any,
|
||||
match: any
|
||||
match: any,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Sources extends React.Component<Props> {
|
||||
@@ -109,13 +111,14 @@ class Sources extends React.Component<Props> {
|
||||
}
|
||||
|
||||
renderBranchSelector = () => {
|
||||
const { branches, revision } = this.props;
|
||||
const { branches, revision, t } = this.props;
|
||||
|
||||
if (branches) {
|
||||
return (
|
||||
<BranchSelector
|
||||
branches={branches}
|
||||
selectedBranch={revision}
|
||||
label={t("branch-selector.label")}
|
||||
selected={(b: Branch) => {
|
||||
this.branchSelected(b);
|
||||
}}
|
||||
@@ -160,6 +163,7 @@ const mapDispatchToProps = dispatch => {
|
||||
};
|
||||
|
||||
export default compose(
|
||||
translate("repos"),
|
||||
withRouter,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
|
||||
@@ -115,7 +115,6 @@ $fa-font-path: "webfonts";
|
||||
.media {
|
||||
.media-content {
|
||||
width: calc(50% - 0.75rem);
|
||||
max-height: 120px;
|
||||
.shorten-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
@@ -112,9 +112,7 @@ import sonia.scm.util.ScmConfigurationUtil;
|
||||
import sonia.scm.web.UserAgentParser;
|
||||
import sonia.scm.web.cgi.CGIExecutorFactory;
|
||||
import sonia.scm.web.cgi.DefaultCGIExecutorFactory;
|
||||
import sonia.scm.web.filter.AuthenticationFilter;
|
||||
import sonia.scm.web.filter.LoggingFilter;
|
||||
import sonia.scm.web.protocol.HttpProtocolServlet;
|
||||
import sonia.scm.web.security.AdministrationContext;
|
||||
import sonia.scm.web.security.DefaultAdministrationContext;
|
||||
|
||||
@@ -315,8 +313,6 @@ public class ScmServletModule extends ServletModule
|
||||
bind(TemplateEngineFactory.class);
|
||||
bind(ObjectMapper.class).toProvider(ObjectMapperProvider.class);
|
||||
|
||||
filter(HttpProtocolServlet.PATTERN).through(AuthenticationFilter.class);
|
||||
|
||||
// bind events
|
||||
// bind(LastModifiedUpdateListener.class);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ package sonia.scm.api.rest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
|
||||
@@ -92,6 +93,7 @@ public class StatusExceptionMapper<E extends Throwable>
|
||||
|
||||
return Response.status(status)
|
||||
.entity(exception.getMessage())
|
||||
.type(MediaType.TEXT_PLAIN_TYPE)
|
||||
.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