mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 19:15:52 +01:00
implement modification api in git, svn and hg. implement the endpoint
This commit is contained in:
@@ -32,6 +32,13 @@
|
||||
<artifactId>scm-annotations</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- logging -->
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ import sonia.scm.util.ValidationUtil;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@@ -84,12 +83,6 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* List of files changed by this changeset
|
||||
*/
|
||||
@XmlElement(name = "modifications")
|
||||
private Modifications modifications;
|
||||
|
||||
/**
|
||||
* parent changeset ids
|
||||
*/
|
||||
@@ -137,7 +130,6 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
&& Objects.equal(parents, other.parents)
|
||||
&& Objects.equal(tags, other.tags)
|
||||
&& Objects.equal(branches, other.branches)
|
||||
&& Objects.equal(modifications, other.modifications)
|
||||
&& Objects.equal(properties, other.properties);
|
||||
//J+
|
||||
}
|
||||
@@ -152,7 +144,7 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(id, date, author, description, parents, tags,
|
||||
branches, modifications, properties);
|
||||
branches, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,11 +176,6 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
out.append("branches: ").append(Util.toString(branches)).append("\n");
|
||||
out.append("tags: ").append(Util.toString(tags)).append("\n");
|
||||
|
||||
if (modifications != null)
|
||||
{
|
||||
out.append("modifications: \n").append(modifications);
|
||||
}
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
@@ -285,21 +272,6 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file modifications, which was done with this changeset.
|
||||
*
|
||||
*
|
||||
* @return file modifications
|
||||
*/
|
||||
public Modifications getModifications()
|
||||
{
|
||||
if (modifications == null)
|
||||
{
|
||||
modifications = new Modifications();
|
||||
}
|
||||
|
||||
return modifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ids of the parent changesets.
|
||||
@@ -402,17 +374,6 @@ public class Changeset extends BasicPropertiesAware implements ModelObject {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the file modification of the changeset.
|
||||
*
|
||||
*
|
||||
* @param modifications file modifications
|
||||
*/
|
||||
public void setModifications(Modifications modifications)
|
||||
{
|
||||
this.modifications = modifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parents of the changeset.
|
||||
*
|
||||
|
||||
@@ -36,15 +36,13 @@ package sonia.scm.repository;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.apache.commons.lang.StringEscapeUtils;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -200,4 +198,10 @@ public final class EscapeUtil
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static void escape(Modifications modifications) {
|
||||
modifications.setAdded(escapeList(modifications.getAdded()));
|
||||
modifications.setModified(escapeList(modifications.getModified()));
|
||||
modifications.setRemoved(escapeList(modifications.getRemoved()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ public class Modifications implements Serializable
|
||||
* Constructs ...
|
||||
*
|
||||
*/
|
||||
public Modifications() {}
|
||||
public Modifications() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
@@ -218,6 +219,10 @@ public class Modifications implements Serializable
|
||||
return removed;
|
||||
}
|
||||
|
||||
public String getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -253,8 +258,14 @@ public class Modifications implements Serializable
|
||||
this.removed = removed;
|
||||
}
|
||||
|
||||
public void setRevision(String revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
private String revision;
|
||||
|
||||
/** list of added files */
|
||||
@XmlElement(name = "added")
|
||||
@XmlElementWrapper(name = "added")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
|
||||
|
||||
/**
|
||||
* A pre processor for {@link Modifications} objects. A pre processor is able to
|
||||
* modify the object before it is delivered to the user interface.
|
||||
*
|
||||
* @author Mohamed Karray
|
||||
* @since 2.0
|
||||
*/
|
||||
@ExtensionPoint
|
||||
public interface ModificationsPreProcessor extends PreProcessor<Modifications> {
|
||||
|
||||
/**
|
||||
* Process the given modifications.
|
||||
*
|
||||
* @param modifications modifications to process
|
||||
*/
|
||||
@Override
|
||||
void process(Modifications modifications);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
|
||||
|
||||
/**
|
||||
* This factory create a {@link ModificationsPreProcessor}
|
||||
*
|
||||
* @author Mohamed Karray
|
||||
* @since 2.0
|
||||
*/
|
||||
@ExtensionPoint
|
||||
public interface ModificationsPreProcessorFactory extends PreProcessorFactory<Modifications> {
|
||||
|
||||
/**
|
||||
* Create a new {@link ModificationsPreProcessor} for the given repository.
|
||||
*
|
||||
*
|
||||
* @param repository repository
|
||||
*
|
||||
* @return {@link ModificationsPreProcessor} for the given repository
|
||||
*/
|
||||
@Override
|
||||
ModificationsPreProcessor createPreProcessor(Repository repository);
|
||||
|
||||
}
|
||||
@@ -36,17 +36,15 @@ package sonia.scm.repository;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -73,14 +71,18 @@ public class PreProcessorUtil
|
||||
* @param fileObjectPreProcessorFactorySet
|
||||
* @param blameLinePreProcessorSet
|
||||
* @param blameLinePreProcessorFactorySet
|
||||
* @param modificationsPreProcessorFactorySet
|
||||
* @param modificationsPreProcessorSet
|
||||
*/
|
||||
@Inject
|
||||
public PreProcessorUtil(Set<ChangesetPreProcessor> changesetPreProcessorSet,
|
||||
Set<ChangesetPreProcessorFactory> changesetPreProcessorFactorySet,
|
||||
Set<FileObjectPreProcessor> fileObjectPreProcessorSet,
|
||||
Set<FileObjectPreProcessorFactory> fileObjectPreProcessorFactorySet,
|
||||
Set<BlameLinePreProcessor> blameLinePreProcessorSet,
|
||||
Set<BlameLinePreProcessorFactory> blameLinePreProcessorFactorySet)
|
||||
Set<ChangesetPreProcessorFactory> changesetPreProcessorFactorySet,
|
||||
Set<FileObjectPreProcessor> fileObjectPreProcessorSet,
|
||||
Set<FileObjectPreProcessorFactory> fileObjectPreProcessorFactorySet,
|
||||
Set<BlameLinePreProcessor> blameLinePreProcessorSet,
|
||||
Set<BlameLinePreProcessorFactory> blameLinePreProcessorFactorySet,
|
||||
Set<ModificationsPreProcessorFactory> modificationsPreProcessorFactorySet,
|
||||
Set<ModificationsPreProcessor> modificationsPreProcessorSet)
|
||||
{
|
||||
this.changesetPreProcessorSet = changesetPreProcessorSet;
|
||||
this.changesetPreProcessorFactorySet = changesetPreProcessorFactorySet;
|
||||
@@ -88,6 +90,8 @@ public class PreProcessorUtil
|
||||
this.fileObjectPreProcessorFactorySet = fileObjectPreProcessorFactorySet;
|
||||
this.blameLinePreProcessorSet = blameLinePreProcessorSet;
|
||||
this.blameLinePreProcessorFactorySet = blameLinePreProcessorFactorySet;
|
||||
this.modificationsPreProcessorFactorySet = modificationsPreProcessorFactorySet;
|
||||
this.modificationsPreProcessorSet = modificationsPreProcessorSet;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -108,13 +112,7 @@ public class PreProcessorUtil
|
||||
}
|
||||
|
||||
EscapeUtil.escape(blameLine);
|
||||
|
||||
PreProcessorHandler<BlameLine> handler =
|
||||
new PreProcessorHandler<BlameLine>(blameLinePreProcessorFactorySet,
|
||||
blameLinePreProcessorSet, repository);
|
||||
|
||||
handler.callPreProcessors(blameLine);
|
||||
handler.callPreProcessorFactories(blameLine);
|
||||
handlePreProcess(repository,blameLine,blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,13 +150,7 @@ public class PreProcessorUtil
|
||||
{
|
||||
EscapeUtil.escape(blameResult);
|
||||
}
|
||||
|
||||
PreProcessorHandler<BlameLine> handler =
|
||||
new PreProcessorHandler<BlameLine>(blameLinePreProcessorFactorySet,
|
||||
blameLinePreProcessorSet, repository);
|
||||
|
||||
handler.callPreProcessors(blameResult.getBlameLines());
|
||||
handler.callPreProcessorFactories(blameResult.getBlameLines());
|
||||
handlePreProcessForIterable(repository, blameResult.getBlameLines(),blameLinePreProcessorFactorySet, blameLinePreProcessorSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,26 +175,20 @@ public class PreProcessorUtil
|
||||
*
|
||||
* @since 1.35
|
||||
*/
|
||||
public void prepareForReturn(Repository repository, Changeset changeset,
|
||||
boolean escape)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("prepare changeset {} of repository {} for return",
|
||||
changeset.getId(), repository.getName());
|
||||
}
|
||||
|
||||
if (escape)
|
||||
{
|
||||
public void prepareForReturn(Repository repository, Changeset changeset, boolean escape){
|
||||
logger.trace("prepare changeset {} of repository {} for return", changeset.getId(), repository.getName());
|
||||
if (escape) {
|
||||
EscapeUtil.escape(changeset);
|
||||
}
|
||||
handlePreProcess(repository, changeset, changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
||||
}
|
||||
|
||||
PreProcessorHandler<Changeset> handler =
|
||||
new PreProcessorHandler<Changeset>(changesetPreProcessorFactorySet,
|
||||
changesetPreProcessorSet, repository);
|
||||
|
||||
handler.callPreProcessors(changeset);
|
||||
handler.callPreProcessorFactories(changeset);
|
||||
public void prepareForReturn(Repository repository, Modifications modifications, boolean escape) {
|
||||
logger.trace("prepare modifications {} of repository {} for return", modifications, repository.getName());
|
||||
if (escape) {
|
||||
EscapeUtil.escape(modifications);
|
||||
}
|
||||
handlePreProcess(repository, modifications, modificationsPreProcessorFactorySet, modificationsPreProcessorSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,13 +226,7 @@ public class PreProcessorUtil
|
||||
{
|
||||
EscapeUtil.escape(result);
|
||||
}
|
||||
|
||||
PreProcessorHandler<FileObject> handler =
|
||||
new PreProcessorHandler<FileObject>(fileObjectPreProcessorFactorySet,
|
||||
fileObjectPreProcessorSet, repository);
|
||||
|
||||
handler.callPreProcessors(result);
|
||||
handler.callPreProcessorFactories(result);
|
||||
handlePreProcessForIterable(repository, result,fileObjectPreProcessorFactorySet, fileObjectPreProcessorSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,13 +252,7 @@ public class PreProcessorUtil
|
||||
{
|
||||
EscapeUtil.escape(result);
|
||||
}
|
||||
|
||||
PreProcessorHandler<Changeset> handler =
|
||||
new PreProcessorHandler<Changeset>(changesetPreProcessorFactorySet,
|
||||
changesetPreProcessorSet, repository);
|
||||
|
||||
handler.callPreProcessors(result);
|
||||
handler.callPreProcessorFactories(result);
|
||||
handlePreProcessForIterable(repository,result,changesetPreProcessorFactorySet, changesetPreProcessorSet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,6 +268,23 @@ public class PreProcessorUtil
|
||||
prepareForReturn(repository, result, true);
|
||||
}
|
||||
|
||||
|
||||
private <T, F extends PreProcessorFactory<T>, P extends PreProcessor<T>> void handlePreProcess(Repository repository, T processedObject,
|
||||
Collection<F> factories,
|
||||
Collection<P> preProcessors) {
|
||||
PreProcessorHandler<T> handler = new PreProcessorHandler<T>(factories, preProcessors, repository);
|
||||
handler.callPreProcessors(processedObject);
|
||||
handler.callPreProcessorFactories(processedObject);
|
||||
}
|
||||
|
||||
private <T, I extends Iterable<T>, F extends PreProcessorFactory<T>, P extends PreProcessor<T>> void handlePreProcessForIterable(Repository repository, I processedObjects,
|
||||
Collection<F> factories,
|
||||
Collection<P> preProcessors) {
|
||||
PreProcessorHandler<T> handler = new PreProcessorHandler<T>(factories, preProcessors, repository);
|
||||
handler.callPreProcessors(processedObjects);
|
||||
handler.callPreProcessorFactories(processedObjects);
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -454,6 +445,10 @@ public class PreProcessorUtil
|
||||
/** Field description */
|
||||
private final Collection<ChangesetPreProcessor> changesetPreProcessorSet;
|
||||
|
||||
private final Collection<ModificationsPreProcessorFactory> modificationsPreProcessorFactorySet;
|
||||
|
||||
private final Collection<ModificationsPreProcessor> modificationsPreProcessorSet;
|
||||
|
||||
/** Field description */
|
||||
private final Collection<FileObjectPreProcessorFactory> fileObjectPreProcessorFactorySet;
|
||||
|
||||
|
||||
@@ -61,5 +61,11 @@ public enum Command
|
||||
/**
|
||||
* @since 1.43
|
||||
*/
|
||||
BUNDLE, UNBUNDLE;
|
||||
BUNDLE, UNBUNDLE,
|
||||
|
||||
/**
|
||||
* @since 2.0
|
||||
*/
|
||||
MODIFICATIONS
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package sonia.scm.repository.api;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.PreProcessorUtil;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryCacheKey;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.spi.ModificationsCommand;
|
||||
import sonia.scm.repository.spi.ModificationsCommandRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Get the modifications applied to files in a revision.
|
||||
* <p>
|
||||
* Modifications are for example: Add, Update and Delete
|
||||
*
|
||||
* @author Mohamed Karray
|
||||
* @since 2.0
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
@Accessors(fluent = true)
|
||||
public final class ModificationsCommandBuilder {
|
||||
static final String CACHE_NAME = "sonia.cache.cmd.modifications";
|
||||
|
||||
private final ModificationsCommand modificationsCommand;
|
||||
|
||||
private final ModificationsCommandRequest request = new ModificationsCommandRequest();
|
||||
|
||||
private final Repository repository;
|
||||
|
||||
private final Cache<ModificationsCommandBuilder.CacheKey, Modifications> cache;
|
||||
|
||||
private final PreProcessorUtil preProcessorUtil;
|
||||
|
||||
@Setter
|
||||
private boolean disableEscaping = false;
|
||||
|
||||
@Setter
|
||||
private boolean disableCache = false;
|
||||
|
||||
@Setter
|
||||
private boolean disablePreProcessors = false;
|
||||
|
||||
public ModificationsCommandBuilder revision(String revision){
|
||||
request.setRevision(revision);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset each parameter to its default value.
|
||||
*
|
||||
*
|
||||
* @return {@code this}
|
||||
*/
|
||||
public ModificationsCommandBuilder reset() {
|
||||
request.reset();
|
||||
this.disableCache = false;
|
||||
this.disablePreProcessors = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Modifications getModifications() throws IOException, RevisionNotFoundException {
|
||||
Modifications modifications;
|
||||
if (disableCache) {
|
||||
log.info("Get modifications for {} with disabled cache", request);
|
||||
modifications = modificationsCommand.getModifications(request);
|
||||
} else {
|
||||
ModificationsCommandBuilder.CacheKey key = new ModificationsCommandBuilder.CacheKey(repository.getId(), request);
|
||||
if (cache.contains(key)) {
|
||||
modifications = cache.get(key);
|
||||
log.debug("Get modifications for {} from the cache", request);
|
||||
} else {
|
||||
log.info("Get modifications for {} with enabled cache", request);
|
||||
modifications = modificationsCommand.getModifications(request);
|
||||
if (modifications != null) {
|
||||
cache.put(key, modifications);
|
||||
log.debug("Modifications for {} added to the cache with key {}", request, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!disablePreProcessors && (modifications != null)) {
|
||||
preProcessorUtil.prepareForReturn(repository, modifications, !disableEscaping);
|
||||
}
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
@EqualsAndHashCode
|
||||
@ToString
|
||||
class CacheKey implements RepositoryCacheKey {
|
||||
private final String repositoryId;
|
||||
private final ModificationsCommandRequest request;
|
||||
|
||||
@Override
|
||||
public String getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -250,6 +250,18 @@ public final class RepositoryService implements Closeable {
|
||||
repository, preProcessorUtil);
|
||||
}
|
||||
|
||||
/**
|
||||
* The modification command shows file modifications in a revision.
|
||||
*
|
||||
* @return instance of {@link ModificationsCommandBuilder}
|
||||
* @throws CommandNotSupportedException if the command is not supported
|
||||
* by the implementation of the repository service provider.
|
||||
*/
|
||||
public ModificationsCommandBuilder getModificationsCommand() {
|
||||
logger.debug("create modifications command for repository {}", repository.getName());
|
||||
return new ModificationsCommandBuilder(provider.getModificationsCommand(),repository, cacheManager.getCache(ModificationsCommandBuilder.CACHE_NAME), preProcessorUtil);
|
||||
}
|
||||
|
||||
/**
|
||||
* The outgoing command show {@link Changeset}s not found in a remote repository.
|
||||
*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -29,60 +29,25 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
|
||||
package sonia.scm.repository;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.wc.admin.ISVNChangeEntryHandler;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Command to get the modifications applied to files in a revision.
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
* Modifications are for example: Add, Update, Delete
|
||||
*
|
||||
* @author Mohamed Karray
|
||||
* @since 2.0
|
||||
*/
|
||||
public class SvnModificationHandler implements ISVNChangeEntryHandler
|
||||
{
|
||||
public interface ModificationsCommand {
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param changeset
|
||||
*/
|
||||
public SvnModificationHandler(Changeset changeset)
|
||||
{
|
||||
this.changeset = changeset;
|
||||
}
|
||||
Modifications getModifications(String revision) throws IOException, RevisionNotFoundException;
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException;
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param entry
|
||||
*
|
||||
* @throws SVNException
|
||||
*/
|
||||
@Override
|
||||
public void handleEntry(SVNChangeEntry entry) throws SVNException
|
||||
{
|
||||
Modifications modification = changeset.getModifications();
|
||||
|
||||
if (modification == null)
|
||||
{
|
||||
modification = new Modifications();
|
||||
changeset.setModifications(modification);
|
||||
}
|
||||
|
||||
SvnUtil.appendModification(modification, entry);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private Changeset changeset;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class ModificationsCommandRequest implements Resetable {
|
||||
private String revision;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
revision = null;
|
||||
}
|
||||
}
|
||||
@@ -39,14 +39,13 @@ import sonia.scm.repository.Feature;
|
||||
import sonia.scm.repository.api.Command;
|
||||
import sonia.scm.repository.api.CommandNotSupportedException;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -173,6 +172,16 @@ public abstract class RepositoryServiceProvider implements Closeable
|
||||
throw new CommandNotSupportedException(Command.LOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the corresponding {@link ModificationsCommand} implemented from the Plugins
|
||||
*
|
||||
* @return the corresponding {@link ModificationsCommand} implemented from the Plugins
|
||||
* @throws CommandNotSupportedException if there is no Implementation
|
||||
*/
|
||||
public ModificationsCommand getModificationsCommand() {
|
||||
throw new CommandNotSupportedException(Command.MODIFICATIONS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -19,6 +19,7 @@ public class VndMediaType {
|
||||
public static final String PERMISSION = PREFIX + "permission" + SUFFIX;
|
||||
public static final String CHANGESET = PREFIX + "changeset" + SUFFIX;
|
||||
public static final String CHANGESET_COLLECTION = PREFIX + "changesetCollection" + SUFFIX;
|
||||
public static final String MODIFICATIONS = PREFIX + "modifications" + SUFFIX;;
|
||||
public static final String TAG = PREFIX + "tag" + SUFFIX;
|
||||
public static final String TAG_COLLECTION = PREFIX + "tagCollection" + SUFFIX;
|
||||
public static final String BRANCH = PREFIX + "branch" + SUFFIX;
|
||||
|
||||
@@ -37,32 +37,25 @@ package sonia.scm.repository;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -224,13 +217,6 @@ public class GitChangesetConverter implements Closeable
|
||||
changeset.setParents(parentList);
|
||||
}
|
||||
|
||||
Modifications modifications = createModifications(treeWalk, commit);
|
||||
|
||||
if (modifications != null)
|
||||
{
|
||||
changeset.setModifications(modifications);
|
||||
}
|
||||
|
||||
Collection<String> tagCollection = tags.get(commit.getId());
|
||||
|
||||
if (Util.isNotEmpty(tagCollection))
|
||||
@@ -245,108 +231,7 @@ public class GitChangesetConverter implements Closeable
|
||||
return changeset;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: copy and rename
|
||||
*
|
||||
*
|
||||
* @param modifications
|
||||
* @param entry
|
||||
*/
|
||||
private void appendModification(Modifications modifications, DiffEntry entry)
|
||||
{
|
||||
switch (entry.getChangeType())
|
||||
{
|
||||
case ADD :
|
||||
modifications.getAdded().add(entry.getNewPath());
|
||||
|
||||
break;
|
||||
|
||||
case MODIFY :
|
||||
modifications.getModified().add(entry.getNewPath());
|
||||
|
||||
break;
|
||||
|
||||
case DELETE :
|
||||
modifications.getRemoved().add(entry.getOldPath());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param treeWalk
|
||||
* @param commit
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit)
|
||||
throws IOException
|
||||
{
|
||||
Modifications modifications = null;
|
||||
|
||||
treeWalk.reset();
|
||||
treeWalk.setRecursive(true);
|
||||
|
||||
if (commit.getParentCount() > 0)
|
||||
{
|
||||
RevCommit parent = commit.getParent(0);
|
||||
RevTree tree = parent.getTree();
|
||||
|
||||
if ((tree == null) && (revWalk != null))
|
||||
{
|
||||
revWalk.parseHeaders(parent);
|
||||
tree = parent.getTree();
|
||||
}
|
||||
|
||||
if (tree != null)
|
||||
{
|
||||
treeWalk.addTree(tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("no parent tree at position 0 for commit {}",
|
||||
commit.getName());
|
||||
}
|
||||
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("no parent available for commit {}", commit.getName());
|
||||
}
|
||||
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
|
||||
treeWalk.addTree(commit.getTree());
|
||||
|
||||
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
|
||||
|
||||
for (DiffEntry e : entries)
|
||||
{
|
||||
if (!e.getOldId().equals(e.getNewId()))
|
||||
{
|
||||
if (modifications == null)
|
||||
{
|
||||
modifications = new Modifications();
|
||||
}
|
||||
|
||||
appendModification(modifications, e);
|
||||
}
|
||||
}
|
||||
|
||||
return modifications;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevTree;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class GitModificationsCommand extends AbstractGitCommand implements ModificationsCommand {
|
||||
|
||||
protected GitModificationsCommand(GitContext context, Repository repository) {
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
private Modifications createModifications(TreeWalk treeWalk, RevCommit commit, RevWalk revWalk, String revision)
|
||||
throws IOException, UnsupportedModificationTypeException {
|
||||
treeWalk.reset();
|
||||
treeWalk.setRecursive(true);
|
||||
if (commit.getParentCount() > 0) {
|
||||
RevCommit parent = commit.getParent(0);
|
||||
RevTree tree = parent.getTree();
|
||||
if ((tree == null) && (revWalk != null)) {
|
||||
revWalk.parseHeaders(parent);
|
||||
tree = parent.getTree();
|
||||
}
|
||||
if (tree != null) {
|
||||
treeWalk.addTree(tree);
|
||||
} else {
|
||||
log.trace("no parent tree at position 0 for commit {}", commit.getName());
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
} else {
|
||||
log.trace("no parent available for commit {}", commit.getName());
|
||||
treeWalk.addTree(new EmptyTreeIterator());
|
||||
}
|
||||
treeWalk.addTree(commit.getTree());
|
||||
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
|
||||
Modifications modifications = new Modifications();
|
||||
for (DiffEntry e : entries) {
|
||||
if (!e.getOldId().equals(e.getNewId())) {
|
||||
appendModification(modifications, e);
|
||||
}
|
||||
}
|
||||
modifications.setRevision(revision);
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modifications getModifications(String revision) {
|
||||
org.eclipse.jgit.lib.Repository gitRepository = null;
|
||||
RevWalk revWalk = null;
|
||||
try {
|
||||
gitRepository = open();
|
||||
if (!gitRepository.getAllRefs().isEmpty()) {
|
||||
revWalk = new RevWalk(gitRepository);
|
||||
ObjectId id = GitUtil.getRevisionId(gitRepository, revision);
|
||||
RevCommit commit = revWalk.parseCommit(id);
|
||||
TreeWalk treeWalk = new TreeWalk(gitRepository);
|
||||
return createModifications(treeWalk, commit, revWalk, revision);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("could not open repository", ex);
|
||||
throw new InternalRepositoryException(ex);
|
||||
|
||||
} catch (UnsupportedModificationTypeException ex) {
|
||||
log.error("Unsupported modification type", ex);
|
||||
throw new InternalRepositoryException(ex);
|
||||
|
||||
} finally {
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.close(gitRepository);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modifications getModifications(ModificationsCommandRequest request) {
|
||||
return getModifications(request.getRevision());
|
||||
}
|
||||
|
||||
private void appendModification(Modifications modifications, DiffEntry entry) throws UnsupportedModificationTypeException {
|
||||
DiffEntry.ChangeType type = entry.getChangeType();
|
||||
if (type == DiffEntry.ChangeType.ADD) {
|
||||
modifications.getAdded().add(entry.getNewPath());
|
||||
} else if (type == DiffEntry.ChangeType.MODIFY) {
|
||||
modifications.getModified().add(entry.getNewPath());
|
||||
} else if (type == DiffEntry.ChangeType.DELETE) {
|
||||
modifications.getRemoved().add(entry.getOldPath());
|
||||
} else {
|
||||
throw new UnsupportedModificationTypeException(MessageFormat.format("The modification type: {0} is not supported.", type));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,17 +36,15 @@ package sonia.scm.repository.spi;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.Command;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -188,6 +186,11 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
return new GitLogCommand(context, repository);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModificationsCommand getModificationsCommand() {
|
||||
return new GitModificationsCommand(context,repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
|
||||
public class UnsupportedModificationTypeException extends InternalRepositoryException {
|
||||
public UnsupportedModificationTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -85,14 +85,14 @@ public class AbstractRemoteCommandTestBase
|
||||
outgoingDirectory = tempFolder.newFile("outgoing");
|
||||
outgoingDirectory.delete();
|
||||
|
||||
incomgingRepository = new Repository("1", "git", "space", "incoming");
|
||||
incomingRepository = new Repository("1", "git", "space", "incoming");
|
||||
outgoingRepository = new Repository("2", "git", "space", "outgoing");
|
||||
|
||||
incoming = Git.init().setDirectory(incomingDirectory).setBare(false).call();
|
||||
outgoing = Git.init().setDirectory(outgoingDirectory).setBare(false).call();
|
||||
|
||||
handler = mock(GitRepositoryHandler.class);
|
||||
when(handler.getDirectory(incomgingRepository)).thenReturn(
|
||||
when(handler.getDirectory(incomingRepository)).thenReturn(
|
||||
incomingDirectory);
|
||||
when(handler.getDirectory(outgoingRepository)).thenReturn(
|
||||
outgoingDirectory);
|
||||
@@ -211,7 +211,7 @@ public class AbstractRemoteCommandTestBase
|
||||
protected GitRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
protected Repository incomgingRepository;
|
||||
protected Repository incomingRepository;
|
||||
|
||||
/** Field description */
|
||||
protected Git incoming;
|
||||
|
||||
@@ -105,7 +105,7 @@ public class GitIncomingCommandTest
|
||||
|
||||
commit(outgoing, "added a");
|
||||
|
||||
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory), incomgingRepository);
|
||||
GitPullCommand pull = new GitPullCommand(handler, new GitContext(incomingDirectory), incomingRepository);
|
||||
PullCommandRequest req = new PullCommandRequest();
|
||||
req.setRemoteRepository(outgoingRepository);
|
||||
pull.pull(req);
|
||||
@@ -192,6 +192,6 @@ public class GitIncomingCommandTest
|
||||
private GitIncomingCommand createCommand()
|
||||
{
|
||||
return new GitIncomingCommand(handler, new GitContext(incomingDirectory),
|
||||
incomgingRepository);
|
||||
incomingRepository);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,21 +168,23 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase
|
||||
Changeset c = command.getChangeset("435df2f061add3589cb3");
|
||||
|
||||
assertNotNull(c);
|
||||
assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c.getId());
|
||||
String revision = "435df2f061add3589cb326cc64be9b9c3897ceca";
|
||||
assertEquals(revision, c.getId());
|
||||
assertEquals("added a and b files", c.getDescription());
|
||||
checkDate(c.getDate());
|
||||
assertEquals("Douglas Adams", c.getAuthor().getName());
|
||||
assertEquals("douglas.adams@hitchhiker.com", c.getAuthor().getMail());
|
||||
assertEquals("added a and b files", c.getDescription());
|
||||
|
||||
Modifications mods = c.getModifications();
|
||||
GitModificationsCommand gitModificationsCommand = new GitModificationsCommand(createContext(), repository);
|
||||
Modifications modifications = gitModificationsCommand.getModifications(revision);
|
||||
|
||||
assertNotNull(mods);
|
||||
assertTrue("modified list should be empty", mods.getModified().isEmpty());
|
||||
assertTrue("removed list should be empty", mods.getRemoved().isEmpty());
|
||||
assertFalse("added list should not be empty", mods.getAdded().isEmpty());
|
||||
assertEquals(2, mods.getAdded().size());
|
||||
assertThat(mods.getAdded(), contains("a.txt", "b.txt"));
|
||||
assertNotNull(modifications);
|
||||
assertTrue("modified list should be empty", modifications.getModified().isEmpty());
|
||||
assertTrue("removed list should be empty", modifications.getRemoved().isEmpty());
|
||||
assertFalse("added list should not be empty", modifications.getAdded().isEmpty());
|
||||
assertEquals(2, modifications.getAdded().size());
|
||||
assertThat(modifications.getAdded(), contains("a.txt", "b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.repository.Modifications;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
|
||||
public class GitModificationsCommandTest extends AbstractRemoteCommandTestBase {
|
||||
|
||||
private GitModificationsCommand incomingModificationsCommand;
|
||||
private GitModificationsCommand outgoingModificationsCommand;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
incomingModificationsCommand = new GitModificationsCommand(new GitContext(incomingDirectory), incomingRepository);
|
||||
outgoingModificationsCommand = new GitModificationsCommand(new GitContext(outgoingDirectory), outgoingRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadAddedFiles() throws Exception {
|
||||
write(outgoing, outgoingDirectory, "a.txt", "bal bla");
|
||||
RevCommit addedFileCommit = commit(outgoing, "add file");
|
||||
String revision = addedFileCommit.getName();
|
||||
Consumer<Modifications> assertModifications = assertAddedFiles("a.txt");
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
pushOutgoingAndPullIncoming();
|
||||
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadModifiedFiles() throws Exception {
|
||||
write(outgoing, outgoingDirectory, "a.txt", "bal bla");
|
||||
commit(outgoing, "add file");
|
||||
write(outgoing, outgoingDirectory, "a.txt", "modified content");
|
||||
RevCommit modifiedFileCommit = commit(outgoing, "modify file");
|
||||
String revision = modifiedFileCommit.getName();
|
||||
Consumer<Modifications> assertModifications = assertModifiedFiles("a.txt");
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
pushOutgoingAndPullIncoming();
|
||||
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadRemovedFiles() throws Exception {
|
||||
String fileName = "a.txt";
|
||||
write(outgoing, outgoingDirectory, fileName, "bal bla");
|
||||
commit(outgoing, "add file");
|
||||
File file = new File(outgoingDirectory, fileName);
|
||||
file.delete();
|
||||
outgoing.add().setUpdate(true).addFilepattern(".").call();
|
||||
RevCommit removedFileCommit = commit(outgoing, "remove file");
|
||||
String revision = removedFileCommit.getName();
|
||||
Consumer<Modifications> assertModifications = assertRemovedFiles(fileName);
|
||||
pushOutgoingAndPullIncoming();
|
||||
assertModifications.accept(incomingModificationsCommand.getModifications(revision));
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
void pushOutgoingAndPullIncoming() throws IOException {
|
||||
GitPushCommand cmd = new GitPushCommand(handler, new GitContext(outgoingDirectory),
|
||||
outgoingRepository);
|
||||
PushCommandRequest request = new PushCommandRequest();
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
cmd.push(request);
|
||||
GitPullCommand pullCommand = new GitPullCommand(handler, new GitContext(incomingDirectory),
|
||||
incomingRepository);
|
||||
PullCommandRequest pullRequest = new PullCommandRequest();
|
||||
pullRequest.setRemoteRepository(incomingRepository);
|
||||
pullCommand.pull(pullRequest);
|
||||
}
|
||||
|
||||
Consumer<Modifications> assertRemovedFiles(String fileName) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(fileName);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Consumer<Modifications> assertModifiedFiles(String file) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(file);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(0);
|
||||
};
|
||||
}
|
||||
|
||||
Consumer<Modifications> assertAddedFiles(String file) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(file);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(0);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
GitOutgoingCommand cmd = createCommand();
|
||||
OutgoingCommandRequest request = new OutgoingCommandRequest();
|
||||
|
||||
request.setRemoteRepository(incomgingRepository);
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
|
||||
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);
|
||||
|
||||
@@ -98,7 +98,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
* @throws RepositoryException
|
||||
*/
|
||||
@Test
|
||||
public void testGetOutgoingChangesetsWithAllreadyPushedChanges()
|
||||
public void testGetOutgoingChangesetsWithAlreadyPushedChanges()
|
||||
throws IOException, GitAPIException
|
||||
{
|
||||
write(outgoing, outgoingDirectory, "a.txt", "content of a.txt");
|
||||
@@ -110,7 +110,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
outgoingRepository);
|
||||
PushCommandRequest req = new PushCommandRequest();
|
||||
|
||||
req.setRemoteRepository(incomgingRepository);
|
||||
req.setRemoteRepository(incomingRepository);
|
||||
push.push(req);
|
||||
|
||||
write(outgoing, outgoingDirectory, "b.txt", "content of b.txt");
|
||||
@@ -120,7 +120,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
GitOutgoingCommand cmd = createCommand();
|
||||
OutgoingCommandRequest request = new OutgoingCommandRequest();
|
||||
|
||||
request.setRemoteRepository(incomgingRepository);
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
|
||||
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);
|
||||
|
||||
@@ -144,7 +144,7 @@ public class GitOutgoingCommandTest extends AbstractRemoteCommandTestBase
|
||||
GitOutgoingCommand cmd = createCommand();
|
||||
OutgoingCommandRequest request = new OutgoingCommandRequest();
|
||||
|
||||
request.setRemoteRepository(incomgingRepository);
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
|
||||
ChangesetPagingResult cpr = cmd.getOutgoingChangesets(request);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ public class GitPushCommandTest extends AbstractRemoteCommandTestBase
|
||||
GitPushCommand cmd = createCommand();
|
||||
PushCommandRequest request = new PushCommandRequest();
|
||||
|
||||
request.setRemoteRepository(incomgingRepository);
|
||||
request.setRemoteRepository(incomingRepository);
|
||||
|
||||
PushResponse response = cmd.push(request);
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.spi.javahg.HgLogChangesetCommand;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public class HgModificationsCommand extends AbstractCommand implements ModificationsCommand {
|
||||
|
||||
HgModificationsCommand(HgCommandContext context, Repository repository) {
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Modifications getModifications(String revision) {
|
||||
com.aragost.javahg.Repository repository = open();
|
||||
HgLogChangesetCommand hgLogChangesetCommand = HgLogChangesetCommand.on(repository, getContext().getConfig());
|
||||
int hgRevision = hgLogChangesetCommand.rev(revision).singleRevision();
|
||||
Modifications modifications = hgLogChangesetCommand.rev(MessageFormat.format("{0}:{0}", hgRevision)).extractModifications();
|
||||
modifications.setRevision(revision);
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modifications getModifications(ModificationsCommandRequest request) {
|
||||
return getModifications(request.getRevision());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -41,21 +41,18 @@ import com.aragost.javahg.internals.AbstractCommand;
|
||||
import com.aragost.javahg.internals.HgInputStream;
|
||||
import com.aragost.javahg.internals.RuntimeIOException;
|
||||
import com.aragost.javahg.internals.Utils;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Person;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -251,33 +248,14 @@ public abstract class AbstractChangesetCommand extends AbstractCommand
|
||||
changeset.getProperties().put(PROPERTY_CLOSE, "true");
|
||||
}
|
||||
|
||||
Modifications modifications = changeset.getModifications();
|
||||
|
||||
String line = in.textUpTo('\n');
|
||||
|
||||
while (line.length() > 0)
|
||||
{
|
||||
|
||||
if (line.startsWith("a "))
|
||||
{
|
||||
modifications.getAdded().add(line.substring(2));
|
||||
}
|
||||
else if (line.startsWith("m "))
|
||||
{
|
||||
modifications.getModified().add(line.substring(2));
|
||||
}
|
||||
else if (line.startsWith("d "))
|
||||
{
|
||||
modifications.getRemoved().add(line.substring(2));
|
||||
}
|
||||
else if (line.startsWith("t "))
|
||||
while (line.length() > 0) {
|
||||
if (line.startsWith("t "))
|
||||
{
|
||||
changeset.getTags().add(line.substring(2));
|
||||
}
|
||||
|
||||
line = in.textUpTo('\n');
|
||||
}
|
||||
|
||||
String message = in.textUpTo('\0');
|
||||
|
||||
changeset.setDescription(message);
|
||||
@@ -285,6 +263,36 @@ public abstract class AbstractChangesetCommand extends AbstractCommand
|
||||
return changeset;
|
||||
}
|
||||
|
||||
protected Modifications readModificationsFromStream(HgInputStream in) {
|
||||
try {
|
||||
boolean found = in.find(CHANGESET_PATTERN);
|
||||
if (found) {
|
||||
while (!in.match(CHANGESET_PATTERN)) {
|
||||
return extractModifications(in);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Modifications extractModifications(HgInputStream in) throws IOException {
|
||||
Modifications modifications = new Modifications();
|
||||
String line = in.textUpTo('\n');
|
||||
while (line.length() > 0) {
|
||||
if (line.startsWith("a ")) {
|
||||
modifications.getAdded().add(line.substring(2));
|
||||
} else if (line.startsWith("m ")) {
|
||||
modifications.getModified().add(line.substring(2));
|
||||
} else if (line.startsWith("d ")) {
|
||||
modifications.getRemoved().add(line.substring(2));
|
||||
}
|
||||
line = in.textUpTo('\n');
|
||||
}
|
||||
return modifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -38,14 +38,14 @@ package sonia.scm.repository.spi.javahg;
|
||||
import com.aragost.javahg.Repository;
|
||||
import com.aragost.javahg.internals.HgInputStream;
|
||||
import com.aragost.javahg.internals.Utils;
|
||||
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.HgConfig;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import sonia.scm.repository.Modifications;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -106,11 +106,22 @@ public class HgLogChangesetCommand extends AbstractChangesetCommand
|
||||
*/
|
||||
public List<Changeset> execute(String... files)
|
||||
{
|
||||
cmdAppend("--style", CHANGESET_EAGER_STYLE_PATH);
|
||||
return readListFromStream(getHgInputStream(files, CHANGESET_EAGER_STYLE_PATH));
|
||||
}
|
||||
|
||||
HgInputStream stream = launchStream(files);
|
||||
/**
|
||||
* Extract Modifications from the Repository files
|
||||
*
|
||||
* @param files repo files
|
||||
* @return modifications
|
||||
*/
|
||||
public Modifications extractModifications(String... files) {
|
||||
return readModificationsFromStream(getHgInputStream(files, CHANGESET_EAGER_STYLE_PATH));
|
||||
}
|
||||
|
||||
return readListFromStream(stream);
|
||||
HgInputStream getHgInputStream(String[] files, String changesetStylePath) {
|
||||
cmdAppend("--style", changesetStylePath);
|
||||
return launchStream(files);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,11 +149,7 @@ public class HgLogChangesetCommand extends AbstractChangesetCommand
|
||||
*/
|
||||
public List<Integer> loadRevisions(String... files)
|
||||
{
|
||||
cmdAppend("--style", CHANGESET_LAZY_STYLE_PATH);
|
||||
|
||||
HgInputStream stream = launchStream(files);
|
||||
|
||||
return loadRevisionsFromStream(stream);
|
||||
return loadRevisionsFromStream(getHgInputStream(files, CHANGESET_LAZY_STYLE_PATH));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,11 +32,11 @@ package sonia.scm.repository.client.spi;
|
||||
|
||||
import com.aragost.javahg.Repository;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.io.IOException;
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Person;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Mercurial implementation of the {@link CommitCommand}.
|
||||
*
|
||||
@@ -70,9 +70,6 @@ public class HgCommitCommand implements CommitCommand
|
||||
|
||||
changeset.setBranches(Lists.newArrayList(c.getBranch()));
|
||||
changeset.setTags(c.tags());
|
||||
changeset.setModifications(
|
||||
new Modifications(c.getAddedFiles(), c.getModifiedFiles(), c.getDeletedFiles())
|
||||
);
|
||||
return changeset;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ import org.junit.Test;
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -133,27 +136,28 @@ public class HgLogCommandTest extends AbstractHgCommandTestBase
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommit() {
|
||||
public void testGetCommit() throws IOException, RevisionNotFoundException {
|
||||
HgLogCommand command = createComamnd();
|
||||
String revision = "a9bacaf1b7fa0cebfca71fed4e59ed69a6319427";
|
||||
Changeset c =
|
||||
command.getChangeset("a9bacaf1b7fa0cebfca71fed4e59ed69a6319427");
|
||||
command.getChangeset(revision);
|
||||
|
||||
assertNotNull(c);
|
||||
assertEquals("a9bacaf1b7fa0cebfca71fed4e59ed69a6319427", c.getId());
|
||||
assertEquals(revision, c.getId());
|
||||
assertEquals("added a and b files", c.getDescription());
|
||||
checkDate(c.getDate());
|
||||
assertEquals("Douglas Adams", c.getAuthor().getName());
|
||||
assertEquals("douglas.adams@hitchhiker.com", c.getAuthor().getMail());
|
||||
assertEquals("added a and b files", c.getDescription());
|
||||
ModificationsCommand modificationsCommand = new HgModificationsCommand(cmdContext, repository);
|
||||
Modifications modifications = modificationsCommand.getModifications(revision);
|
||||
|
||||
Modifications mods = c.getModifications();
|
||||
|
||||
assertNotNull(mods);
|
||||
assertTrue("modified list should be empty", mods.getModified().isEmpty());
|
||||
assertTrue("removed list should be empty", mods.getRemoved().isEmpty());
|
||||
assertFalse("added list should not be empty", mods.getAdded().isEmpty());
|
||||
assertEquals(2, mods.getAdded().size());
|
||||
assertThat(mods.getAdded(), contains("a.txt", "b.txt"));
|
||||
assertNotNull(modifications);
|
||||
assertTrue("modified list should be empty", modifications.getModified().isEmpty());
|
||||
assertTrue("removed list should be empty", modifications.getRemoved().isEmpty());
|
||||
assertFalse("added list should not be empty", modifications.getAdded().isEmpty());
|
||||
assertEquals(2, modifications.getAdded().size());
|
||||
assertThat(modifications.getAdded(), contains("a.txt", "b.txt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import com.aragost.javahg.Changeset;
|
||||
import com.aragost.javahg.commands.RemoveCommand;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.repository.HgTestUtil;
|
||||
import sonia.scm.repository.Modifications;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
|
||||
public class HgModificationsCommandTest extends IncomingOutgoingTestBase {
|
||||
|
||||
|
||||
private HgModificationsCommand outgoingModificationsCommand;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
HgCommandContext outgoingContext = new HgCommandContext(HgTestUtil.createHookManager(), handler, outgoingRepository, outgoingDirectory);
|
||||
outgoingModificationsCommand = new HgModificationsCommand(outgoingContext, outgoingRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadAddedFiles() throws Exception {
|
||||
String fileName = "a.txt";
|
||||
writeNewFile(outgoing, outgoingDirectory, fileName, "bal bla");
|
||||
Changeset changeset = commit(outgoing, "added a.txt");
|
||||
String revision = String.valueOf(changeset.getRevision());
|
||||
Consumer<Modifications> assertModifications = assertAddedFile(fileName);
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadModifiedFiles() throws Exception {
|
||||
String fileName = "a.txt";
|
||||
writeNewFile(outgoing, outgoingDirectory, fileName, "bal bla");
|
||||
commit(outgoing, "added a.txt");
|
||||
writeNewFile(outgoing, outgoingDirectory, fileName, "new content");
|
||||
Changeset changeset = commit(outgoing, "modified a.txt");
|
||||
String revision = String.valueOf(changeset.getRevision());
|
||||
Consumer<Modifications> assertModifications = assertModifiedFiles(fileName);
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadRemovedFiles() throws Exception {
|
||||
String fileName = "a.txt";
|
||||
writeNewFile(outgoing, outgoingDirectory, fileName, "bal bla");
|
||||
commit(outgoing, "added a.txt");
|
||||
File file = new File(outgoingDirectory, fileName);
|
||||
file.delete();
|
||||
RemoveCommand.on(outgoing).execute(file);
|
||||
Changeset changeset = commit(outgoing, "removed a.txt");
|
||||
String revision = String.valueOf(changeset.getRevision());
|
||||
Consumer<Modifications> assertModifications = assertRemovedFiles(fileName);
|
||||
assertModifications.accept(outgoingModificationsCommand.getModifications(revision));
|
||||
}
|
||||
|
||||
|
||||
Consumer<Modifications> assertRemovedFiles(String fileName) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(fileName);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Consumer<Modifications> assertModifiedFiles(String file) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(file);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(0);
|
||||
};
|
||||
}
|
||||
|
||||
Consumer<Modifications> assertAddedFile(String addedFile) {
|
||||
return (modifications) -> {
|
||||
assertThat(modifications).isNotNull();
|
||||
assertThat(modifications.getAdded())
|
||||
.as("added files modifications")
|
||||
.hasSize(1)
|
||||
.containsOnly(addedFile);
|
||||
assertThat(modifications.getModified())
|
||||
.as("modified files modifications")
|
||||
.hasSize(0);
|
||||
assertThat(modifications.getRemoved())
|
||||
.as("removed files modifications")
|
||||
.hasSize(0);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,6 @@ import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
|
||||
import org.tmatesoft.svn.core.internal.util.SVNXMLUtil;
|
||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
import org.tmatesoft.svn.core.wc.SVNClientManager;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNChangeEntry;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
@@ -103,30 +102,37 @@ public final class SvnUtil
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* TODO: type replaced
|
||||
*
|
||||
*
|
||||
* @param modifications
|
||||
* @param entry
|
||||
*/
|
||||
public static void appendModification(Modifications modifications,
|
||||
SVNLogEntryPath entry)
|
||||
{
|
||||
appendModification(modifications, entry.getType(), entry.getPath());
|
||||
public static long parseRevision(String v) throws RevisionNotFoundException {
|
||||
long result = -1l;
|
||||
|
||||
if (!Strings.isNullOrEmpty(v))
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Long.parseLong(v);
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
throw new RevisionNotFoundException(v);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param modifications
|
||||
* @param entry
|
||||
*/
|
||||
public static void appendModification(Modifications modifications,
|
||||
SVNChangeEntry entry)
|
||||
{
|
||||
appendModification(modifications, entry.getType(), entry.getPath());
|
||||
|
||||
public static Modifications createModifications(SVNLogEntry entry, String revision) {
|
||||
Modifications modifications = new Modifications();
|
||||
modifications.setRevision(revision);
|
||||
Map<String, SVNLogEntryPath> changeMap = entry.getChangedPaths();
|
||||
|
||||
if (Util.isNotEmpty(changeMap)) {
|
||||
|
||||
for (SVNLogEntryPath e : changeMap.values()) {
|
||||
appendModification(modifications, e.getType(), e.getPath());
|
||||
}
|
||||
}
|
||||
return modifications;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,19 +216,6 @@ public final class SvnUtil
|
||||
{
|
||||
changeset.getParents().add(String.valueOf(revision - 1));
|
||||
}
|
||||
|
||||
Map<String, SVNLogEntryPath> changeMap = entry.getChangedPaths();
|
||||
|
||||
if (Util.isNotEmpty(changeMap))
|
||||
{
|
||||
Modifications modifications = changeset.getModifications();
|
||||
|
||||
for (SVNLogEntryPath e : changeMap.values())
|
||||
{
|
||||
appendModification(modifications, e);
|
||||
}
|
||||
}
|
||||
|
||||
return changeset;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ import sonia.scm.util.Util;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static sonia.scm.repository.SvnUtil.parseRevision;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
public class SvnLogCommand extends AbstractSvnCommand implements LogCommand
|
||||
@@ -144,25 +146,6 @@ public class SvnLogCommand extends AbstractSvnCommand implements LogCommand
|
||||
return changesets;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
private long parseRevision(String v) throws RevisionNotFoundException {
|
||||
long result = -1l;
|
||||
|
||||
if (!Strings.isNullOrEmpty(v))
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Long.parseLong(v);
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
throw new RevisionNotFoundException(v);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNLogEntry;
|
||||
import org.tmatesoft.svn.core.io.SVNRepository;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
@Slf4j
|
||||
public class SvnModificationsCommand extends AbstractSvnCommand implements ModificationsCommand {
|
||||
|
||||
SvnModificationsCommand(SvnContext context, Repository repository) {
|
||||
super(context, repository);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Modifications getModifications(String revision) throws IOException, RevisionNotFoundException {
|
||||
Modifications modifications = null;
|
||||
log.debug("get modifications {}", revision);
|
||||
try {
|
||||
long revisionNumber = SvnUtil.parseRevision(revision);
|
||||
SVNRepository repo = open();
|
||||
Collection<SVNLogEntry> entries = repo.log(null, null, revisionNumber,
|
||||
revisionNumber, true, true);
|
||||
if (Util.isNotEmpty(entries)) {
|
||||
modifications = SvnUtil.createModifications(entries.iterator().next(), revision);
|
||||
}
|
||||
} catch (SVNException ex) {
|
||||
throw new InternalRepositoryException("could not open repository", ex);
|
||||
}
|
||||
return modifications;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Modifications getModifications(ModificationsCommandRequest request) throws IOException, RevisionNotFoundException {
|
||||
return getModifications(request.getRevision());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -37,22 +37,19 @@ package sonia.scm.repository.spi;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.tmatesoft.svn.core.SVNLogEntry;
|
||||
import org.tmatesoft.svn.core.wc.ISVNOptions;
|
||||
import org.tmatesoft.svn.core.wc.SVNClientManager;
|
||||
import org.tmatesoft.svn.core.wc.SVNWCUtil;
|
||||
import org.tmatesoft.svn.core.wc.admin.SVNLookClient;
|
||||
|
||||
import sonia.scm.repository.Changeset;
|
||||
import sonia.scm.repository.RepositoryHookType;
|
||||
import sonia.scm.repository.SvnModificationHandler;
|
||||
import sonia.scm.repository.SvnUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.File;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -123,10 +120,6 @@ public class SvnPreReceiveHookChangesetProvier
|
||||
{
|
||||
changeset = SvnUtil.createChangeset(entry);
|
||||
changeset.setId(SvnUtil.createTransactionEntryId(transaction));
|
||||
|
||||
clientManager.doGetChanged(repositoryDirectory, transaction,
|
||||
new SvnModificationHandler(changeset), true);
|
||||
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
|
||||
@@ -40,6 +40,8 @@ import sonia.scm.repository.ChangesetPagingResult;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -128,7 +130,7 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCommit() throws RevisionNotFoundException {
|
||||
public void testGetCommit() throws RevisionNotFoundException, IOException {
|
||||
Changeset c = createCommand().getChangeset("3");
|
||||
|
||||
assertNotNull(c);
|
||||
@@ -137,15 +139,15 @@ public class SvnLogCommandTest extends AbstractSvnCommandTestBase
|
||||
checkDate(c.getDate());
|
||||
assertEquals("perfect", c.getAuthor().getName());
|
||||
assertNull("douglas.adams@hitchhiker.com", c.getAuthor().getMail());
|
||||
SvnModificationsCommand modificationsCommand = new SvnModificationsCommand(createContext(), repository);
|
||||
Modifications modifications = modificationsCommand.getModifications("3");
|
||||
|
||||
Modifications mods = c.getModifications();
|
||||
|
||||
assertNotNull(mods);
|
||||
assertEquals(1, mods.getModified().size());
|
||||
assertEquals(1, mods.getRemoved().size());
|
||||
assertTrue("added list should be empty", mods.getAdded().isEmpty());
|
||||
assertEquals("a.txt", mods.getModified().get(0));
|
||||
assertEquals("b.txt", mods.getRemoved().get(0));
|
||||
assertNotNull(modifications);
|
||||
assertEquals(1, modifications.getModified().size());
|
||||
assertEquals(1, modifications.getRemoved().size());
|
||||
assertTrue("added list should be empty", modifications.getAdded().isEmpty());
|
||||
assertEquals("a.txt", modifications.getModified().get(0));
|
||||
assertEquals("b.txt", modifications.getRemoved().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -45,7 +45,8 @@ import javax.ws.rs.ext.Provider;
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.36
|
||||
*/
|
||||
@Provider @Slf4j
|
||||
@Provider
|
||||
@Slf4j
|
||||
public class IllegalArgumentExceptionMapper
|
||||
implements ExceptionMapper<IllegalArgumentException>
|
||||
{
|
||||
|
||||
@@ -65,7 +65,8 @@ public abstract class ChangesetToChangesetDtoMapper extends BaseMapper<Changeset
|
||||
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.changeset().self(repository.getNamespace(), repository.getName(), target.getId()))
|
||||
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())));
|
||||
.single(link("diff", resourceLinks.diff().self(namespace, name, target.getId())))
|
||||
.single(link("modifications", resourceLinks.modifications().self(namespace, name, target.getId())));
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import sonia.scm.api.rest.StatusExceptionMapper;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class InternalRepositoryExceptionMapper extends StatusExceptionMapper<InternalRepositoryException> {
|
||||
|
||||
public InternalRepositoryExceptionMapper() {
|
||||
super(InternalRepositoryException.class, Response.Status.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class MapperModule extends AbstractModule {
|
||||
bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass());
|
||||
|
||||
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
||||
bind(ModificationsToDtoMapper.class).to(Mappers.getMapper(ModificationsToDtoMapper.class).getClass());
|
||||
|
||||
// no mapstruct required
|
||||
bind(UIPluginDtoMapper.class);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ModificationsDto extends HalRepresentation {
|
||||
|
||||
|
||||
private String revision;
|
||||
/**
|
||||
* list of added files
|
||||
*/
|
||||
private List<String> added;
|
||||
|
||||
/**
|
||||
* list of modified files
|
||||
*/
|
||||
private List<String> modified;
|
||||
|
||||
/**
|
||||
* list of removed files
|
||||
*/
|
||||
private List<String> removed;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||
protected HalRepresentation add(Links links) {
|
||||
return super.add(links);
|
||||
}
|
||||
|
||||
@SuppressWarnings("squid:S1185") // We want to have this method available in this package
|
||||
protected HalRepresentation withEmbedded(String rel, List<? extends HalRepresentation> halRepresentations) {
|
||||
return super.withEmbedded(rel, halRepresentations);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import com.webcohesion.enunciate.metadata.rs.TypeHint;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.RepositoryNotFoundException;
|
||||
import sonia.scm.repository.RevisionNotFoundException;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ModificationsRootResource {
|
||||
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
private final ModificationsToDtoMapper modificationsToDtoMapper;
|
||||
|
||||
@Inject
|
||||
public ModificationsRootResource(RepositoryServiceFactory serviceFactory, ModificationsToDtoMapper modificationsToDtoMapper) {
|
||||
this.serviceFactory = serviceFactory;
|
||||
this.modificationsToDtoMapper = modificationsToDtoMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file modifications related to a revision.
|
||||
* file modifications are for example: Modified, Added or Removed.
|
||||
*
|
||||
* @param namespace
|
||||
* @param name
|
||||
* @param revision
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws RevisionNotFoundException
|
||||
* @throws RepositoryNotFoundException
|
||||
*/
|
||||
@GET
|
||||
@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 changeset"),
|
||||
@ResponseCode(code = 404, condition = "not found, no changeset with the specified id is available in the repository"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
@Produces(VndMediaType.MODIFICATIONS)
|
||||
@TypeHint(ModificationsDto.class)
|
||||
@Path("{revision}")
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision) throws IOException, RevisionNotFoundException, RepositoryNotFoundException , InternalRepositoryException {
|
||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||
Modifications modifications = repositoryService.getModificationsCommand()
|
||||
.revision(revision)
|
||||
.getModifications();
|
||||
ModificationsDto output = modificationsToDtoMapper.map(modifications, repositoryService.getRepository());
|
||||
if (modifications != null ) {
|
||||
return Response.ok(output).build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static de.otto.edison.hal.Links.linkingTo;
|
||||
|
||||
@Mapper
|
||||
public abstract class ModificationsToDtoMapper extends BaseMapper<Modifications, ModificationsDto> {
|
||||
|
||||
@Inject
|
||||
private ResourceLinks resourceLinks;
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract ModificationsDto map(Modifications modifications, @Context Repository repository);
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(@MappingTarget ModificationsDto target, @Context Repository repository) {
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.modifications().self(repository.getNamespace(), repository.getName(), target.getRevision()));
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public class RepositoryResource {
|
||||
private final Provider<ContentResource> contentResource;
|
||||
private final Provider<PermissionRootResource> permissionRootResource;
|
||||
private final Provider<DiffRootResource> diffRootResource;
|
||||
private final Provider<ModificationsRootResource> modificationsRootResource;
|
||||
|
||||
@Inject
|
||||
public RepositoryResource(
|
||||
@@ -50,7 +51,7 @@ public class RepositoryResource {
|
||||
Provider<ChangesetRootResource> changesetRootResource,
|
||||
Provider<SourceRootResource> sourceRootResource, Provider<ContentResource> contentResource,
|
||||
Provider<PermissionRootResource> permissionRootResource,
|
||||
Provider<DiffRootResource> diffRootResource) {
|
||||
Provider<DiffRootResource> diffRootResource, Provider<ModificationsRootResource> modificationsRootResource) {
|
||||
this.dtoToRepositoryMapper = dtoToRepositoryMapper;
|
||||
this.manager = manager;
|
||||
this.repositoryToDtoMapper = repositoryToDtoMapper;
|
||||
@@ -62,6 +63,7 @@ public class RepositoryResource {
|
||||
this.contentResource = contentResource;
|
||||
this.permissionRootResource = permissionRootResource;
|
||||
this.diffRootResource = diffRootResource;
|
||||
this.modificationsRootResource = modificationsRootResource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,6 +182,9 @@ public class RepositoryResource {
|
||||
return permissionRootResource.get();
|
||||
}
|
||||
|
||||
@Path("modifications/")
|
||||
public ModificationsRootResource modifications() {return modificationsRootResource.get(); }
|
||||
|
||||
private Optional<Response> handleNotArchived(Throwable throwable) {
|
||||
if (throwable instanceof RepositoryIsNotArchivedException) {
|
||||
return Optional.of(Response.status(Response.Status.PRECONDITION_FAILED).build());
|
||||
|
||||
@@ -307,6 +307,21 @@ class ResourceLinks {
|
||||
}
|
||||
}
|
||||
|
||||
public ModificationsLinks modifications() {
|
||||
return new ModificationsLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
static class ModificationsLinks {
|
||||
private final LinkBuilder modificationsLinkBuilder;
|
||||
|
||||
ModificationsLinks(UriInfo uriInfo) {
|
||||
modificationsLinkBuilder = new LinkBuilder(uriInfo, RepositoryRootResource.class, RepositoryResource.class, ModificationsRootResource.class);
|
||||
}
|
||||
String self(String namespace, String name, String revision) {
|
||||
return modificationsLinkBuilder.method("getRepositoryResource").parameters(namespace, name).method("modifications").parameters().method("get").parameters(revision).href();
|
||||
}
|
||||
}
|
||||
|
||||
public SourceLinks source() {
|
||||
return new SourceLinks(uriInfoStore.get());
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class BranchRootResourceTest {
|
||||
changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, resourceLinks);
|
||||
BranchCollectionToDtoMapper branchCollectionToDtoMapper = new BranchCollectionToDtoMapper(branchToDtoMapper, resourceLinks);
|
||||
branchRootResource = new BranchRootResource(serviceFactory, branchToDtoMapper, branchCollectionToDtoMapper, changesetCollectionToDtoMapper);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null, null)), null);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(new RepositoryResource(null, null, null, null, MockProvider.of(branchRootResource), null, null, null, null, null, null)), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class ChangesetRootResourceTest {
|
||||
changesetRootResource = new ChangesetRootResource(serviceFactory, changesetCollectionToDtoMapper, changesetToChangesetDtoMapper);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||
.of(new RepositoryResource(null, null, null, null, null,
|
||||
MockProvider.of(changesetRootResource), null, null, null, null)), null);
|
||||
MockProvider.of(changesetRootResource), null, null, null, null, null)), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
|
||||
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.apache.shiro.util.ThreadState;
|
||||
import org.assertj.core.util.Lists;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.api.rest.AuthorizationExceptionMapper;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Modifications;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.ModificationsCommandBuilder;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import java.net.URI;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Slf4j
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class ModificationsResourceTest {
|
||||
|
||||
|
||||
public static final String MODIFICATIONS_PATH = "space/repo/modifications/";
|
||||
public static final String MODIFICATIONS_URL = "/" + RepositoryRootResource.REPOSITORIES_PATH_V2 + MODIFICATIONS_PATH;
|
||||
private final Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
|
||||
|
||||
private final URI baseUri = URI.create("/");
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@Mock
|
||||
private RepositoryServiceFactory serviceFactory;
|
||||
|
||||
@Mock
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
@Mock
|
||||
private ModificationsCommandBuilder modificationsCommandBuilder;
|
||||
|
||||
@InjectMocks
|
||||
private ModificationsToDtoMapperImpl modificationsToDtoMapper;
|
||||
|
||||
private ModificationsRootResource modificationsRootResource;
|
||||
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() throws Exception {
|
||||
modificationsRootResource = new ModificationsRootResource(serviceFactory, modificationsToDtoMapper);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||
.of(new RepositoryResource(null, null, null, null, null,
|
||||
null, null, null, null, null, MockProvider.of(modificationsRootResource))), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
|
||||
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
|
||||
when(repositoryService.getRepository()).thenReturn(new Repository("repoId", "git", "space", "repo"));
|
||||
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(InternalRepositoryExceptionMapper.class);
|
||||
when(repositoryService.getModificationsCommand()).thenReturn(modificationsCommandBuilder);
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
when(subject.isPermitted(any(String.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupContext() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet404OnMissingModifications() throws Exception {
|
||||
when(modificationsCommandBuilder.revision(any())).thenReturn(modificationsCommandBuilder);
|
||||
when(modificationsCommandBuilder.getModifications()).thenReturn(null);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(MODIFICATIONS_URL + "not_existing_revision")
|
||||
.accept(VndMediaType.MODIFICATIONS);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
assertEquals(404, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet500OnModificationsCommandError() throws Exception {
|
||||
when(modificationsCommandBuilder.revision(any())).thenReturn(modificationsCommandBuilder);
|
||||
when(modificationsCommandBuilder.getModifications()).thenThrow(InternalRepositoryException.class);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(MODIFICATIONS_URL + "revision")
|
||||
.accept(VndMediaType.MODIFICATIONS);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
assertEquals(500, response.getStatus());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void shouldGetModifications() throws Exception {
|
||||
Modifications modifications = new Modifications();
|
||||
String revision = "revision";
|
||||
String addedFile_1 = "a.txt";
|
||||
String addedFile_2 = "b.txt";
|
||||
String modifiedFile_1 = "d.txt";
|
||||
String modifiedFile_2 = "c.txt";
|
||||
String removedFile_1 = "e.txt";
|
||||
String removedFile_2 = "f.txt";
|
||||
modifications.setRevision(revision);
|
||||
modifications.setAdded(Lists.newArrayList(addedFile_1, addedFile_2));
|
||||
modifications.setModified(Lists.newArrayList(modifiedFile_1, modifiedFile_2));
|
||||
modifications.setRemoved(Lists.newArrayList(removedFile_1, removedFile_2));
|
||||
when(modificationsCommandBuilder.getModifications()).thenReturn(modifications);
|
||||
when(modificationsCommandBuilder.revision(eq(revision))).thenReturn(modificationsCommandBuilder);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(MODIFICATIONS_URL + revision)
|
||||
.accept(VndMediaType.MODIFICATIONS);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
assertEquals(200, response.getStatus());
|
||||
log.info("the content: ", response.getContentAsString());
|
||||
assertTrue(response.getContentAsString().contains(String.format("\"revision\":\"%s\"", revision)));
|
||||
assertTrue(response.getContentAsString().contains(MessageFormat.format("\"added\":[\"{0}\",\"{1}\"]", addedFile_1, addedFile_2)));
|
||||
assertTrue(response.getContentAsString().contains(MessageFormat.format("\"modified\":[\"{0}\",\"{1}\"]", modifiedFile_1, modifiedFile_2)));
|
||||
assertTrue(response.getContentAsString().contains(MessageFormat.format("\"removed\":[\"{0}\",\"{1}\"]", removedFile_1, removedFile_2)));
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,7 @@ public class PermissionRootResourceTest {
|
||||
permissionCollectionToDtoMapper = new PermissionCollectionToDtoMapper(permissionToPermissionDtoMapper, resourceLinks);
|
||||
permissionRootResource = new PermissionRootResource(permissionDtoToPermissionMapper, permissionToPermissionDtoMapper, permissionCollectionToDtoMapper, resourceLinks, repositoryManager);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource), null)), null);
|
||||
.of(new RepositoryResource(null, null, null, null, null, null, null, null, MockProvider.of(permissionRootResource), null, null)), null);
|
||||
dispatcher = createDispatcher(repositoryRootResource);
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class RepositoryRootResourceTest {
|
||||
@Before
|
||||
public void prepareEnvironment() {
|
||||
initMocks(this);
|
||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null, null);
|
||||
RepositoryResource repositoryResource = new RepositoryResource(repositoryToDtoMapper, dtoToRepositoryMapper, repositoryManager, null, null, null, null, null, null, null, null);
|
||||
RepositoryCollectionToDtoMapper repositoryCollectionToDtoMapper = new RepositoryCollectionToDtoMapper(repositoryToDtoMapper, resourceLinks);
|
||||
RepositoryCollectionResource repositoryCollectionResource = new RepositoryCollectionResource(repositoryManager, repositoryCollectionToDtoMapper, dtoToRepositoryMapper, resourceLinks);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider.of(repositoryResource), MockProvider.of(repositoryCollectionResource));
|
||||
|
||||
@@ -27,6 +27,7 @@ public class ResourceLinksMock {
|
||||
when(resourceLinks.config()).thenReturn(new ResourceLinks.ConfigLinks(uriInfo));
|
||||
when(resourceLinks.branch()).thenReturn(new ResourceLinks.BranchLinks(uriInfo));
|
||||
when(resourceLinks.diff()).thenReturn(new ResourceLinks.DiffLinks(uriInfo));
|
||||
when(resourceLinks.modifications()).thenReturn(new ResourceLinks.ModificationsLinks(uriInfo));
|
||||
when(resourceLinks.repositoryType()).thenReturn(new ResourceLinks.RepositoryTypeLinks(uriInfo));
|
||||
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo));
|
||||
when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.mock.MockDispatcherFactory;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.Before;
|
||||
@@ -74,6 +73,7 @@ public class SourceRootResourceTest {
|
||||
MockProvider.of(sourceRootResource),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null)),
|
||||
null);
|
||||
dispatcher = createDispatcher(repositoryRootResource);
|
||||
|
||||
@@ -74,7 +74,7 @@ public class TagRootResourceTest {
|
||||
tagRootResource = new TagRootResource(serviceFactory, tagCollectionToDtoMapper, tagToTagDtoMapper);
|
||||
RepositoryRootResource repositoryRootResource = new RepositoryRootResource(MockProvider
|
||||
.of(new RepositoryResource(null, null, null, MockProvider.of(tagRootResource), null,
|
||||
null, null, null, null, null)), null);
|
||||
null, null, null, null, null, null)), null);
|
||||
dispatcher.getRegistry().addSingletonResource(repositoryRootResource);
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(repositoryService);
|
||||
when(serviceFactory.create(any(Repository.class))).thenReturn(repositoryService);
|
||||
|
||||
Reference in New Issue
Block a user