diff --git a/scm-core/src/main/java/sonia/scm/repository/ChangesetPagingResult.java b/scm-core/src/main/java/sonia/scm/repository/ChangesetPagingResult.java index 59a705e36a..ca1018b7aa 100644 --- a/scm-core/src/main/java/sonia/scm/repository/ChangesetPagingResult.java +++ b/scm-core/src/main/java/sonia/scm/repository/ChangesetPagingResult.java @@ -82,6 +82,22 @@ public class ChangesetPagingResult implements Iterable, Serializable { this.total = total; this.changesets = changesets; + this.branchName = null; + } + + /** + * Constructs a new changeset paging result for a specific branch. + * + * + * @param total total number of changesets + * @param changesets current list of fetched changesets + * @param branchName branch name this result was created for + */ + public ChangesetPagingResult(int total, List changesets, String branchName) + { + this.total = total; + this.changesets = changesets; + this.branchName = branchName; } //~--- methods -------------------------------------------------------------- @@ -158,6 +174,7 @@ public class ChangesetPagingResult implements Iterable, Serializable return MoreObjects.toStringHelper(this) .add("changesets", changesets) .add("total", total) + .add("branch", branchName) .toString(); //J+ } @@ -186,37 +203,35 @@ public class ChangesetPagingResult implements Iterable, Serializable return total; } - //~--- set methods ---------------------------------------------------------- - - /** - * Sets the current list of changesets. - * - * - * @param changesets current list of changesets - */ - public void setChangesets(List changesets) + void setChangesets(List changesets) { this.changesets = changesets; } - /** - * Sets the total number of changesets - * - * - * @param total total number of changesets - */ - public void setTotal(int total) + void setTotal(int total) { this.total = total; } + void setBranchName(String branchName) { + this.branchName = branchName; + } + + /** + * Returns the branch name this result was created for. This can either be an explicit branch ("give me all + * changesets for branch xyz") or an implicit one ("give me the changesets for the default"). + */ + public String getBranchName() { + return branchName; + } + //~--- fields --------------------------------------------------------------- - /** current list of changesets */ @XmlElement(name = "changeset") @XmlElementWrapper(name = "changesets") private List changesets; - /** total number of changesets */ private int total; + + private String branchName; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java index 6936c51269..2275fbcfd0 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitChangesetConverter.java @@ -39,7 +39,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Multimap; 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.RevWalk; import org.eclipse.jgit.treewalk.TreeWalk; @@ -51,8 +50,8 @@ import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; -import java.util.Set; //~--- JDK imports ------------------------------------------------------------ @@ -130,27 +129,9 @@ public class GitChangesetConverter implements Closeable * * @throws IOException */ - public Changeset createChangeset(RevCommit commit) throws IOException + public Changeset createChangeset(RevCommit commit) { - List branches = Lists.newArrayList(); - Set refs = repository.getAllRefsByPeeledObjectId().get(commit.getId()); - - if (Util.isNotEmpty(refs)) - { - - for (Ref ref : refs) - { - String branch = GitUtil.getBranch(ref); - - if (branch != null) - { - branches.add(branch); - } - } - - } - - return createChangeset(commit, branches); + return createChangeset(commit, Collections.emptyList()); } /** @@ -165,7 +146,6 @@ public class GitChangesetConverter implements Closeable * @throws IOException */ public Changeset createChangeset(RevCommit commit, String branch) - throws IOException { return createChangeset(commit, Lists.newArrayList(branch)); } @@ -183,7 +163,6 @@ public class GitChangesetConverter implements Closeable * @throws IOException */ public Changeset createChangeset(RevCommit commit, List branches) - throws IOException { String id = commit.getId().name(); List parentList = null; diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java index 7e145f2dd9..13340a20e7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/GitUtil.java @@ -63,8 +63,11 @@ import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.Map; +import java.util.Optional; import java.util.concurrent.TimeUnit; +import static java.util.Optional.of; + //~--- JDK imports ------------------------------------------------------------ /** @@ -345,12 +348,11 @@ public final class GitUtil * * @throws IOException */ - public static ObjectId getBranchId(org.eclipse.jgit.lib.Repository repo, + public static Ref getBranchId(org.eclipse.jgit.lib.Repository repo, String branchName) throws IOException { - ObjectId branchId = null; - + Ref ref = null; if (!branchName.startsWith(REF_HEAD)) { branchName = PREFIX_HEADS.concat(branchName); @@ -360,24 +362,19 @@ public final class GitUtil try { - Ref ref = repo.findRef(branchName); + ref = repo.findRef(branchName); - if (ref != null) - { - branchId = ref.getObjectId(); - } - else if (logger.isWarnEnabled()) + if (ref == null) { logger.warn("could not find branch for {}", branchName); } - } catch (IOException ex) { logger.warn("error occured during resolve of branch id", ex); } - return branchId; + return ref; } /** @@ -499,68 +496,48 @@ public final class GitUtil return ref; } - /** - * Method description - * - * - * @param repo - * - * @return - * - * @throws IOException - */ - public static ObjectId getRepositoryHead(org.eclipse.jgit.lib.Repository repo) - throws IOException - { - ObjectId id = null; - String head = null; - Map refs = repo.getAllRefs(); + public static ObjectId getRepositoryHead(org.eclipse.jgit.lib.Repository repo) { + return getRepositoryHeadRef(repo).map(Ref::getObjectId).orElse(null); + } - for (Map.Entry e : refs.entrySet()) - { - String key = e.getKey(); + public static Optional getRepositoryHeadRef(org.eclipse.jgit.lib.Repository repo) { + Optional foundRef = findMostAppropriateHead(repo.getAllRefs()); - if (REF_HEAD.equals(key)) - { - head = REF_HEAD; - id = e.getValue().getObjectId(); - - break; - } - else if (key.startsWith(REF_HEAD_PREFIX)) - { - id = e.getValue().getObjectId(); - head = key.substring(REF_HEAD_PREFIX.length()); - - if (REF_MASTER.equals(head)) - { - break; - } + if (foundRef.isPresent()) { + if (logger.isDebugEnabled()) { + logger.debug("use {}:{} as repository head for directory {}", + foundRef.map(GitUtil::getBranch).orElse(null), + foundRef.map(Ref::getObjectId).map(ObjectId::name).orElse(null), + repo.getDirectory()); } + } else { + logger.warn("could not find repository head in directory {}", repo.getDirectory()); } - if (id == null) - { - id = repo.resolve(Constants.HEAD); + return foundRef; + } + + private static Optional findMostAppropriateHead(Map refs) { + Ref refHead = refs.get(REF_HEAD); + if (refHead != null && refHead.isSymbolic() && isBranch(refHead.getTarget().getName())) { + return of(refHead.getTarget()); } - if (logger.isDebugEnabled()) - { - if ((head != null) && (id != null)) - { - logger.debug("use {}:{} as repository head", head, id.name()); - } - else if (id != null) - { - logger.debug("use {} as repository head", id.name()); - } - else - { - logger.warn("could not find repository head"); - } + Ref master = refs.get(REF_HEAD_PREFIX + REF_MASTER); + if (master != null) { + return of(master); } - return id; + Ref develop = refs.get(REF_HEAD_PREFIX + "develop"); + if (develop != null) { + return of(develop); + } + + return refs.entrySet() + .stream() + .filter(e -> e.getKey().startsWith(REF_HEAD_PREFIX)) + .map(Map.Entry::getValue) + .findFirst(); } /** @@ -648,7 +625,7 @@ public final class GitUtil return tagName; } - + /** * Method description * diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java index d098c30b4e..2970bbd627 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/AbstractGitCommand.java @@ -34,19 +34,20 @@ package sonia.scm.repository.spi; //~--- non-JDK imports -------------------------------------------------------- -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; -import org.eclipse.jgit.lib.Repository; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.IOException; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.Repository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sonia.scm.repository.GitConstants; import sonia.scm.repository.GitUtil; +import java.io.IOException; +import java.util.Optional; + +//~--- JDK imports ------------------------------------------------------------ + /** * * @author Sebastian Sdorra @@ -97,27 +98,29 @@ public class AbstractGitCommand } return commit; } - - protected ObjectId getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException { - ObjectId head; - if ( Strings.isNullOrEmpty(requestedBranch) ) { - head = getDefaultBranch(gitRepository); - } else { - head = GitUtil.getBranchId(gitRepository, requestedBranch); - } - return head; - } - + protected ObjectId getDefaultBranch(Repository gitRepository) throws IOException { - ObjectId head; - String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH); - if (!Strings.isNullOrEmpty(defaultBranchName)) { - head = GitUtil.getBranchId(gitRepository, defaultBranchName); + Ref ref = getBranchOrDefault(gitRepository, null); + if (ref == null) { + return null; } else { - logger.trace("no default branch configured, use repository head as default"); - head = GitUtil.getRepositoryHead(gitRepository); + return ref.getObjectId(); + } + } + + protected Ref getBranchOrDefault(Repository gitRepository, String requestedBranch) throws IOException { + if ( Strings.isNullOrEmpty(requestedBranch) ) { + String defaultBranchName = repository.getProperty(GitConstants.PROPERTY_DEFAULT_BRANCH); + if (!Strings.isNullOrEmpty(defaultBranchName)) { + return GitUtil.getBranchId(gitRepository, defaultBranchName); + } else { + logger.trace("no default branch configured, use repository head as default"); + Optional repositoryHeadRef = GitUtil.getRepositoryHeadRef(gitRepository); + return repositoryHeadRef.orElse(null); + } + } else { + return GitUtil.getBranchId(gitRepository, requestedBranch); } - return head; } //~--- fields --------------------------------------------------------------- diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index beb79cb921..4e9261f517 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -39,6 +39,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Lists; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; @@ -170,8 +171,8 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand GitChangesetConverter converter = null; RevWalk revWalk = null; - try (org.eclipse.jgit.lib.Repository gr = open()) { - if (!gr.getAllRefs().isEmpty()) { + try (org.eclipse.jgit.lib.Repository repository = open()) { + if (!repository.getAllRefs().isEmpty()) { int counter = 0; int start = request.getPagingStart(); @@ -188,18 +189,18 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand ObjectId startId = null; if (!Strings.isNullOrEmpty(request.getStartChangeset())) { - startId = gr.resolve(request.getStartChangeset()); + startId = repository.resolve(request.getStartChangeset()); } ObjectId endId = null; if (!Strings.isNullOrEmpty(request.getEndChangeset())) { - endId = gr.resolve(request.getEndChangeset()); + endId = repository.resolve(request.getEndChangeset()); } - revWalk = new RevWalk(gr); + revWalk = new RevWalk(repository); - converter = new GitChangesetConverter(gr, revWalk); + converter = new GitChangesetConverter(repository, revWalk); if (!Strings.isNullOrEmpty(request.getPath())) { revWalk.setTreeFilter( @@ -207,13 +208,13 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); } - ObjectId head = getBranchOrDefault(gr, request.getBranch()); + Ref branch = getBranchOrDefault(repository,request.getBranch()); - if (head != null) { + if (branch != null) { if (startId != null) { revWalk.markStart(revWalk.lookupCommit(startId)); } else { - revWalk.markStart(revWalk.lookupCommit(head)); + revWalk.markStart(revWalk.lookupCommit(branch.getObjectId())); } Iterator iterator = revWalk.iterator(); @@ -234,10 +235,14 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand } } - changesets = new ChangesetPagingResult(counter, changesetList); + if (branch != null) { + changesets = new ChangesetPagingResult(counter, changesetList, GitUtil.getBranch(branch.getName())); + } else { + changesets = new ChangesetPagingResult(counter, changesetList); + } } else if (logger.isWarnEnabled()) { logger.warn("the repository {} seems to be empty", - repository.getName()); + this.repository.getName()); changesets = new ChangesetPagingResult(0, Collections.EMPTY_LIST); } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index d6e6ac98d8..78db8ae686 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -1,3 +1,4 @@ + /** * Copyright (c) 2010, Sebastian Sdorra * All rights reserved. @@ -33,14 +34,17 @@ package sonia.scm.repository.spi; -//~--- non-JDK imports -------------------------------------------------------- - +import com.google.common.io.Files; import org.junit.Test; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; import sonia.scm.repository.GitConstants; import sonia.scm.repository.Modifications; +import java.io.File; +import java.io.IOException; + +import static java.nio.charset.Charset.defaultCharset; import static org.hamcrest.Matchers.contains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -48,8 +52,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -//~--- JDK imports ------------------------------------------------------------ - /** * Unit tests for {@link GitLogCommand}. * @@ -72,6 +74,8 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase assertEquals("86a6645eceefe8b9a247db5eb16e3d89a7e6e6d1", result.getChangesets().get(1).getId()); assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(2).getId()); assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(3).getId()); + assertEquals("master", result.getBranchName()); + assertTrue(result.getChangesets().stream().allMatch(r -> r.getBranches().isEmpty())); // set default branch and fetch again repository.setProperty(GitConstants.PROPERTY_DEFAULT_BRANCH, "test-branch"); @@ -79,10 +83,12 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase result = createCommand().getChangesets(new LogCommandRequest()); assertNotNull(result); + assertEquals("test-branch", result.getBranchName()); assertEquals(3, result.getTotal()); assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", result.getChangesets().get(0).getId()); assertEquals("592d797cd36432e591416e8b2b98154f4f163411", result.getChangesets().get(1).getId()); assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", result.getChangesets().get(2).getId()); + assertTrue(result.getChangesets().stream().allMatch(r -> r.getBranches().isEmpty())); } @Test @@ -210,6 +216,32 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c2.getId()); } + @Test + public void shouldFindDefaultBranchFromHEAD() throws Exception { + setRepositoryHeadReference("ref: refs/heads/test-branch"); + + ChangesetPagingResult changesets = createCommand().getChangesets(new LogCommandRequest()); + + assertEquals("test-branch", changesets.getBranchName()); + } + + @Test + public void shouldFindMasterBranchWhenHEADisNoRef() throws Exception { + setRepositoryHeadReference("592d797cd36432e591416e8b2b98154f4f163411"); + + ChangesetPagingResult changesets = createCommand().getChangesets(new LogCommandRequest()); + + assertEquals("master", changesets.getBranchName()); + } + + private void setRepositoryHeadReference(String s) throws IOException { + Files.write(s, repositoryHeadReferenceFile(), defaultCharset()); + } + + private File repositoryHeadReferenceFile() { + return new File(repositoryDirectory, "HEAD"); + } + private GitLogCommand createCommand() { return new GitLogCommand(createContext(), repository); diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgLogCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgLogCommand.java index 68d6913962..e9de7f7471 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgLogCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgLogCommand.java @@ -132,7 +132,11 @@ public class HgLogCommand extends AbstractCommand implements LogCommand List changesets = on(repository).rev(start + ":" + end).execute(); - result = new ChangesetPagingResult(total, changesets); + if (request.getBranch() == null) { + result = new ChangesetPagingResult(total, changesets); + } else { + result = new ChangesetPagingResult(total, changesets, request.getBranch()); + } } else { diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java index 6466eb6d11..89164a8d80 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/AbstractChangesetCommand.java @@ -216,10 +216,7 @@ public abstract class AbstractChangesetCommand extends AbstractCommand String branch = in.textUpTo('\n'); - if (!BRANCH_DEFAULT.equals(branch)) - { - changeset.getBranches().add(branch); - } + changeset.getBranches().add(branch); String p1 = readId(in, changeset, PROPERTY_PARENT1_REVISION); diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java index 99e9fc191a..29fc46ed57 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/HgLogCommandTest.java @@ -88,6 +88,21 @@ public class HgLogCommandTest extends AbstractHgCommandTestBase result.getChangesets().get(2).getId()); } + @Test + public void testGetDefaultBranchInfo() { + LogCommandRequest request = new LogCommandRequest(); + + request.setPath("a.txt"); + + ChangesetPagingResult result = createComamnd().getChangesets(request); + + assertNotNull(result); + assertEquals(1, + result.getChangesets().get(0).getBranches().size()); + assertEquals("default", + result.getChangesets().get(0).getBranches().get(0)); + } + @Test public void testGetAllWithLimit() { LogCommandRequest request = new LogCommandRequest(); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchChangesetCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchChangesetCollectionToDtoMapper.java index afe8ad318b..8842d176fa 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchChangesetCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchChangesetCollectionToDtoMapper.java @@ -12,12 +12,12 @@ public class BranchChangesetCollectionToDtoMapper extends ChangesetCollectionToD @Inject public BranchChangesetCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) { - super(changesetToChangesetDtoMapper); + super(changesetToChangesetDtoMapper, resourceLinks); this.resourceLinks = resourceLinks; } public CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, Repository repository, String branch) { - return this.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository, branch)); + return this.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository, branch), branch); } private String createSelfLink(Repository repository, String branch) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchReferenceDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchReferenceDto.java new file mode 100644 index 0000000000..129d8d8bfa --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchReferenceDto.java @@ -0,0 +1,19 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor @AllArgsConstructor @Getter @Setter +public class BranchReferenceDto extends HalRepresentation { + private String name; + + @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package + protected HalRepresentation add(Links links) { + return super.add(links); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java index 24ee9b0ce1..87f3454e0f 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapper.java @@ -12,10 +12,13 @@ public class ChangesetCollectionToDtoMapper extends ChangesetCollectionToDtoMapp @Inject public ChangesetCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) { - super(changesetToChangesetDtoMapper); + super(changesetToChangesetDtoMapper, resourceLinks); this.resourceLinks = resourceLinks; } + public CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, Repository repository, String branchName) { + return super.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository), branchName); + } public CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, Repository repository) { return super.map(pageNumber, pageSize, pageResult, repository, () -> createSelfLink(repository)); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperBase.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperBase.java index e29a0a92b2..a34b7d44d6 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperBase.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperBase.java @@ -1,5 +1,6 @@ package sonia.scm.api.v2.resources; +import de.otto.edison.hal.Links; import sonia.scm.PageResult; import sonia.scm.repository.Changeset; import sonia.scm.repository.Repository; @@ -10,14 +11,28 @@ import java.util.function.Supplier; class ChangesetCollectionToDtoMapperBase extends PagedCollectionToDtoMapper { private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper; + private final ResourceLinks resourceLinks; - ChangesetCollectionToDtoMapperBase(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper) { + ChangesetCollectionToDtoMapperBase(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) { super("changesets"); this.changesetToChangesetDtoMapper = changesetToChangesetDtoMapper; + this.resourceLinks = resourceLinks; } CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, Repository repository, Supplier selfLinkSupplier) { return super.map(pageNumber, pageSize, pageResult, selfLinkSupplier.get(), Optional.empty(), changeset -> changesetToChangesetDtoMapper.map(changeset, repository)); } -} + CollectionDto map(int pageNumber, int pageSize, PageResult pageResult, Repository repository, Supplier selfLinkSupplier, String branchName) { + CollectionDto collectionDto = this.map(pageNumber, pageSize, pageResult, repository, selfLinkSupplier); + collectionDto.withEmbedded("branch", createBranchReferenceDto(repository, branchName)); + return collectionDto; + } + + private BranchReferenceDto createBranchReferenceDto(Repository repository, String branchName) { + BranchReferenceDto branchReferenceDto = new BranchReferenceDto(); + branchReferenceDto.setName(branchName); + branchReferenceDto.add(Links.linkingTo().self(resourceLinks.branch().self(repository.getNamespaceAndName(), branchName)).build()); + return branchReferenceDto; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java index 717b8d7198..396d055963 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ChangesetRootResource.java @@ -65,7 +65,11 @@ public class ChangesetRootResource { .getChangesets(); if (changesets != null && changesets.getChangesets() != null) { PageResult pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal()); - return Response.ok(changesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository)).build(); + if (changesets.getBranchName() != null) { + return Response.ok(changesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository, changesets.getBranchName())).build(); + } else { + return Response.ok(changesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository)).build(); + } } else { return Response.ok().build(); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionDto.java index c10e18267c..b59d697c2e 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/CollectionDto.java @@ -15,4 +15,9 @@ class CollectionDto extends HalRepresentation { CollectionDto(Links links, Embedded embedded) { super(links, embedded); } + + @Override + protected HalRepresentation withEmbedded(String rel, HalRepresentation embeddedItem) { + return super.withEmbedded(rel, embeddedItem); + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryCollectionToDtoMapper.java index af7fb2ed83..57e5667c65 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/FileHistoryCollectionToDtoMapper.java @@ -13,7 +13,7 @@ public class FileHistoryCollectionToDtoMapper extends ChangesetCollectionToDtoMa @Inject public FileHistoryCollectionToDtoMapper(ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper, ResourceLinks resourceLinks) { - super(changesetToChangesetDtoMapper); + super(changesetToChangesetDtoMapper, resourceLinks); this.resourceLinks = resourceLinks; } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java new file mode 100644 index 0000000000..69695279e6 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ChangesetCollectionToDtoMapperTest.java @@ -0,0 +1,61 @@ +package sonia.scm.api.v2.resources; + +import org.assertj.core.api.Assertions; +import org.junit.Test; +import sonia.scm.PageResult; +import sonia.scm.repository.Changeset; +import sonia.scm.repository.Repository; + +import java.net.URI; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ChangesetCollectionToDtoMapperTest { + + public static final Repository REPOSITORY = new Repository("", "git", "space", "name"); + public static final Changeset CHANGESET = new Changeset(); + private final ChangesetToChangesetDtoMapper changesetToChangesetDtoMapper = mock(ChangesetToChangesetDtoMapper.class); + + private final ChangesetCollectionToDtoMapper changesetCollectionToDtoMapper = new ChangesetCollectionToDtoMapper(changesetToChangesetDtoMapper, ResourceLinksMock.createMock(URI.create("/"))); + + @Test + public void shouldMapCollectionEntries() { + ChangesetDto expectedChangesetDto = new ChangesetDto(); + when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto); + + CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY); + + assertThat(collectionDto.getEmbedded().hasItem("changesets")).isTrue(); + assertThat(collectionDto.getEmbedded().getItemsBy("changesets")).containsExactly(expectedChangesetDto); + assertThat(collectionDto.getEmbedded().hasItem("branch")).isFalse(); + } + + @Test + public void shouldNotEmbedBranchIfNotSpecified() { + ChangesetDto expectedChangesetDto = new ChangesetDto(); + when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto); + + CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY); + + assertThat(collectionDto.getEmbedded().hasItem("branch")).isFalse(); + } + + @Test + public void shouldEmbedBranchIfSpecified() { + ChangesetDto expectedChangesetDto = new ChangesetDto(); + when(changesetToChangesetDtoMapper.map(CHANGESET, REPOSITORY)).thenReturn(expectedChangesetDto); + + CollectionDto collectionDto = changesetCollectionToDtoMapper.map(0, 1, new PageResult<>(asList(CHANGESET), 1), REPOSITORY, "someBranch"); + + assertThat(collectionDto.getEmbedded().hasItem("branch")).isTrue(); + assertThat(collectionDto.getEmbedded().getItemsBy("branch")) + .hasSize(1) + .first().matches(b -> b.getLinks().getLinkBy("self").isPresent()) + .extracting(b -> b.getLinks().getLinkBy("self").get().getHref()).first().isEqualTo("/v2/repositories/space/name/branches/someBranch"); + assertThat(collectionDto.getEmbedded().getItemsBy("branch")) + .first().extracting("name").first().isEqualTo("someBranch"); + } +}