diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ac3fca88b..61e92420ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +### Added +- Tags overview for repository [#1331](https://github.com/scm-manager/scm-manager/pull/1331) + ## [2.5.0] - 2020-09-10 ### Added - Tags now have date information attached ([#1305](https://github.com/scm-manager/scm-manager/pull/1305)) diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx index 0e2a01622b..190a338fbb 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/BranchesOverview.tsx @@ -117,7 +117,7 @@ const mapStateToProps = (state: any, ownProps: Props) => { const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repository: Repository) => { - dispatch(fetchBranches(repository)); + dispatch(fetchBranches(repository, true)); } }; }; diff --git a/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx b/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx index 9e8c27fc0f..b457b26448 100644 --- a/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx +++ b/scm-ui/ui-webapp/src/repos/branches/containers/CreateBranch.tsx @@ -119,7 +119,7 @@ class CreateBranch extends React.Component { const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repository: Repository) => { - dispatch(fetchBranches(repository)); + dispatch(fetchBranches(repository, false)); }, createBranch: ( createLink: string, diff --git a/scm-ui/ui-webapp/src/repos/branches/modules/branches.ts b/scm-ui/ui-webapp/src/repos/branches/modules/branches.ts index 9f079c2922..744d26d163 100644 --- a/scm-ui/ui-webapp/src/repos/branches/modules/branches.ts +++ b/scm-ui/ui-webapp/src/repos/branches/modules/branches.ts @@ -24,7 +24,7 @@ import { FAILURE_SUFFIX, PENDING_SUFFIX, RESET_SUFFIX, SUCCESS_SUFFIX } from "../../../modules/types"; import { apiClient } from "@scm-manager/ui-components"; -import { Action, Branch, BranchRequest, Repository } from "@scm-manager/ui-types"; +import { Action, Branch, BranchRequest, Repository, Link } from "@scm-manager/ui-types"; import { isPending } from "../../../modules/pending"; import { getFailure } from "../../../modules/failure"; @@ -50,7 +50,7 @@ const CONTENT_TYPE_BRANCH_REQUEST = "application/vnd.scmm-branchRequest+json;v=2 // Fetching branches -export function fetchBranches(repository: Repository) { +export function fetchBranches(repository: Repository, fullInformation: boolean) { if (!repository._links.branches) { return { type: FETCH_BRANCHES_SUCCESS, @@ -64,8 +64,12 @@ export function fetchBranches(repository: Repository) { return function(dispatch: any) { dispatch(fetchBranchesPending(repository)); + let link = (repository._links.branches as Link).href; + if (fullInformation) { + link += "?fullInformation=true"; + } return apiClient - .get(repository._links.branches.href) + .get(link) .then(response => response.json()) .then(data => { dispatch(fetchBranchesSuccess(data, repository)); @@ -77,7 +81,7 @@ export function fetchBranches(repository: Repository) { } export function fetchBranch(repository: Repository, name: string) { - let link = repository._links.branches.href; + let link = (repository._links.branches as Link).href; if (!link.endsWith("/")) { link += "/"; } @@ -125,8 +129,8 @@ export function createBranch( // Selectors -function collectBranches(repoState) { - return repoState.list._embedded.branches.map(name => repoState.byName[name]); +function collectBranches(repoState: any) { + return repoState.list._embedded.branches.map((name: string) => repoState.byName[name]); } const memoizedBranchCollector = memoizeOne(collectBranches); diff --git a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx index 6bf77d6352..84ffc8a6c1 100644 --- a/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx +++ b/scm-ui/ui-webapp/src/repos/codeSection/containers/CodeOverview.tsx @@ -108,7 +108,7 @@ class CodeOverview extends React.Component { const mapDispatchToProps = (dispatch: any) => { return { fetchBranches: (repo: Repository) => { - dispatch(fetchBranches(repo)); + dispatch(fetchBranches(repo, false)); } }; }; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchCollectionToDtoMapper.java index f640331381..1207aedeff 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchCollectionToDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchCollectionToDtoMapper.java @@ -52,14 +52,14 @@ public class BranchCollectionToDtoMapper { this.branchToDtoMapper = branchToDtoMapper; } - public HalRepresentation map(Repository repository, Collection branches) { + public HalRepresentation map(Repository repository, Collection branches, boolean fullInformation) { return new HalRepresentation( createLinks(repository), - embedDtos(getBranchDtoList(repository.getNamespace(), repository.getName(), branches))); + embedDtos(getBranchDtoList(repository.getNamespace(), repository.getName(), branches, fullInformation))); } - public List getBranchDtoList(String namespace, String name, Collection branches) { - return branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name))).collect(toList()); + public List getBranchDtoList(String namespace, String name, Collection branches, boolean fullInformation) { + return branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name), fullInformation)).collect(toList()); } private Links createLinks(Repository repository) { diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 5502217da4..23cbe8dc34 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.api.v2.resources; import com.google.common.base.Strings; @@ -120,7 +120,12 @@ public class BranchRootResource { schema = @Schema(implementation = ErrorDto.class) ) ) - public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("branch") String branchName) throws IOException { + public Response get( + @PathParam("namespace") String namespace, + @PathParam("name") String name, + @PathParam("branch") String branchName, + @QueryParam("fullInformation") @DefaultValue("false") boolean fullInformation + ) throws IOException { NamespaceAndName namespaceAndName = new NamespaceAndName(namespace, name); try (RepositoryService repositoryService = serviceFactory.create(namespaceAndName)) { Branches branches = repositoryService.getBranchesCommand().getBranches(); @@ -128,7 +133,7 @@ public class BranchRootResource { .stream() .filter(branch -> branchName.equals(branch.getName())) .findFirst() - .map(branch -> branchToDtoMapper.map(branch, namespaceAndName)) + .map(branch -> branchToDtoMapper.map(branch, namespaceAndName, fullInformation)) .map(Response::ok) .orElseThrow(() -> notFound(entity("branch", branchName).in(namespaceAndName))) .build(); @@ -293,10 +298,14 @@ public class BranchRootResource { mediaType = VndMediaType.ERROR_TYPE, schema = @Schema(implementation = ErrorDto.class) )) - public Response getAll(@PathParam("namespace") String namespace, @PathParam("name") String name) throws IOException { + public Response getAll( + @PathParam("namespace") String namespace, + @PathParam("name") String name, + @QueryParam("fullInformation") @DefaultValue("false") boolean fullInformation + ) throws IOException { try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { Branches branches = repositoryService.getBranchesCommand().getBranches(); - return Response.ok(branchCollectionToDtoMapper.map(repositoryService.getRepository(), branches.getBranches())).build(); + return Response.ok(branchCollectionToDtoMapper.map(repositoryService.getRepository(), branches.getBranches(), fullInformation)).build(); } catch (CommandNotSupportedException ex) { return Response.status(Response.Status.BAD_REQUEST).build(); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index e49a42d321..7b091d8cf5 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -57,12 +57,12 @@ public abstract class BranchToBranchDtoMapper extends HalAppenderMapper { private RepositoryServiceFactory serviceFactory; @Mapping(target = "attributes", ignore = true) // We do not map HAL attributes - public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName); + public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName, boolean fullInformation); abstract PersonDto map(Person person); @ObjectFactory - BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch) { + BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch, boolean fullInformation) { Links.Builder linksBuilder = linkingTo() .self(resourceLinks.branch().self(namespaceAndName, branch.getName())) .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build()) @@ -73,16 +73,18 @@ public abstract class BranchToBranchDtoMapper extends HalAppenderMapper { applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName); BranchDto branchDto = new BranchDto(linksBuilder.build(), embeddedBuilder.build()); - try (RepositoryService service = serviceFactory.create(namespaceAndName)) { - Changeset latestChangeset = service.getLogCommand().setBranch(branch.getName()).getChangesets().getChangesets().get(0); - branchDto.setLastModified(Instant.ofEpochMilli(latestChangeset.getDate())); - branchDto.setLastModifier(map(latestChangeset.getAuthor())); - } catch (IOException e) { - throw new InternalRepositoryException( - ContextEntry.ContextBuilder.entity(Branch.class, branch.getName()), - "Could not read latest changeset for branch", - e - ); + if (fullInformation) { + try (RepositoryService service = serviceFactory.create(namespaceAndName)) { + Changeset latestChangeset = service.getLogCommand().setBranch(branch.getName()).getChangesets().getChangesets().get(0); + branchDto.setLastModified(Instant.ofEpochMilli(latestChangeset.getDate())); + branchDto.setLastModifier(map(latestChangeset.getAuthor())); + } catch (IOException e) { + throw new InternalRepositoryException( + ContextEntry.ContextBuilder.entity(Branch.class, branch.getName()), + "Could not read latest changeset for branch", + e + ); + } } return branchDto; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java index c05547b295..2d1c036f07 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/DefaultChangesetToChangesetDtoMapper.java @@ -133,7 +133,7 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMa } if (repositoryService.isSupported(Command.BRANCHES)) { embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name, - getListOfObjects(source.getBranches(), branchName -> Branch.normalBranch(branchName, source.getId())))); + getListOfObjects(source.getBranches(), branchName -> Branch.normalBranch(branchName, source.getId())), false)); } if (repositoryService.isSupported(Command.DIFF_RESULT)) { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java index 1a64c46110..1b5d663dc2 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapperTest.java @@ -79,8 +79,10 @@ class BranchToBranchDtoMapperTest { Branch branch = Branch.normalBranch("master", "42"); - BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold")); + BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold"), false); assertThat(dto.getLinks().getLinkBy("ka").get().getHref()).isEqualTo("http://hitchhiker/heart-of-gold/master"); + assertThat(dto.getLastModified()).isNull(); + assertThat(dto.getLastModifier()).isNull(); } @Test @@ -94,7 +96,7 @@ class BranchToBranchDtoMapperTest { when(logCommandBuilder.getChangesets()).thenReturn(new ChangesetPagingResult(1, ImmutableList.of(changeset))); Branch branch = Branch.normalBranch("master", "42"); - BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold")); + BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold"), true); assertThat(dto.getLastModified()).isEqualTo(Instant.ofEpochMilli(creationTime)); assertThat(dto.getLastModifier().getName()).isEqualTo(PersonTestData.ZAPHOD.getName());