implement modification api in git, svn and hg. implement the endpoint

This commit is contained in:
Mohamed Karray
2018-09-13 11:57:10 +02:00
parent 6b6b558823
commit 4697c55f96
52 changed files with 1231 additions and 426 deletions

View File

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

View File

@@ -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.
*

View File

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

View File

@@ -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")

View File

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

View File

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

View File

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

View File

@@ -61,5 +61,11 @@ public enum Command
/**
* @since 1.43
*/
BUNDLE, UNBUNDLE;
BUNDLE, UNBUNDLE,
/**
* @since 2.0
*/
MODIFICATIONS
}

View File

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

View File

@@ -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.
*

View File

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

View File

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

View File

@@ -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
*

View File

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

View File

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

View File

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

View File

@@ -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
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
*

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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())
{

View File

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

View File

@@ -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>
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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