mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35:45 +01:00
rollback changes on branches overview
This commit is contained in:
@@ -23,14 +23,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Links } from "./hal";
|
import { Links } from "./hal";
|
||||||
import { Person } from ".";
|
|
||||||
|
|
||||||
export type Branch = {
|
export type Branch = {
|
||||||
name: string;
|
name: string;
|
||||||
revision: string;
|
revision: string;
|
||||||
defaultBranch?: boolean;
|
defaultBranch?: boolean;
|
||||||
lastModified?: Date;
|
|
||||||
lastModifier?: Person;
|
|
||||||
_links: Links;
|
_links: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,23 +25,13 @@ import React, { FC } from "react";
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { Branch } from "@scm-manager/ui-types";
|
import { Branch } from "@scm-manager/ui-types";
|
||||||
import DefaultBranchTag from "./DefaultBranchTag";
|
import DefaultBranchTag from "./DefaultBranchTag";
|
||||||
import { DateFromNow } from "@scm-manager/ui-components";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import styled from "styled-components";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
branch: Branch;
|
branch: Branch;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Modified = styled.span`
|
|
||||||
margin-left: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const BranchRow: FC<Props> = ({ baseUrl, branch }) => {
|
const BranchRow: FC<Props> = ({ baseUrl, branch }) => {
|
||||||
const [t] = useTranslation("repos");
|
|
||||||
|
|
||||||
const to = `${baseUrl}/${encodeURIComponent(branch.name)}/info`;
|
const to = `${baseUrl}/${encodeURIComponent(branch.name)}/info`;
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr>
|
||||||
@@ -49,12 +39,6 @@ const BranchRow: FC<Props> = ({ baseUrl, branch }) => {
|
|||||||
<Link to={to} title={branch.name}>
|
<Link to={to} title={branch.name}>
|
||||||
{branch.name}
|
{branch.name}
|
||||||
<DefaultBranchTag defaultBranch={branch.defaultBranch} />
|
<DefaultBranchTag defaultBranch={branch.defaultBranch} />
|
||||||
{branch?.lastModified && branch.lastModifier && (
|
|
||||||
<Modified className="has-text-grey is-ellipsis-overflow">
|
|
||||||
{t("branches.overview.lastModified")} <DateFromNow date={branch.lastModified} />{" "}
|
|
||||||
{t("branches.overview.lastModifier")} {branch.lastModifier?.name}
|
|
||||||
</Modified>
|
|
||||||
)}
|
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
|||||||
const mapDispatchToProps = (dispatch: any) => {
|
const mapDispatchToProps = (dispatch: any) => {
|
||||||
return {
|
return {
|
||||||
fetchBranches: (repository: Repository) => {
|
fetchBranches: (repository: Repository) => {
|
||||||
dispatch(fetchBranches(repository, true));
|
dispatch(fetchBranches(repository));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ class CreateBranch extends React.Component<Props> {
|
|||||||
const mapDispatchToProps = (dispatch: any) => {
|
const mapDispatchToProps = (dispatch: any) => {
|
||||||
return {
|
return {
|
||||||
fetchBranches: (repository: Repository) => {
|
fetchBranches: (repository: Repository) => {
|
||||||
dispatch(fetchBranches(repository, false));
|
dispatch(fetchBranches(repository));
|
||||||
},
|
},
|
||||||
createBranch: (
|
createBranch: (
|
||||||
createLink: string,
|
createLink: string,
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const CONTENT_TYPE_BRANCH_REQUEST = "application/vnd.scmm-branchRequest+json;v=2
|
|||||||
|
|
||||||
// Fetching branches
|
// Fetching branches
|
||||||
|
|
||||||
export function fetchBranches(repository: Repository, fullInformation: boolean) {
|
export function fetchBranches(repository: Repository) {
|
||||||
if (!repository._links.branches) {
|
if (!repository._links.branches) {
|
||||||
return {
|
return {
|
||||||
type: FETCH_BRANCHES_SUCCESS,
|
type: FETCH_BRANCHES_SUCCESS,
|
||||||
@@ -64,12 +64,8 @@ export function fetchBranches(repository: Repository, fullInformation: boolean)
|
|||||||
|
|
||||||
return function(dispatch: any) {
|
return function(dispatch: any) {
|
||||||
dispatch(fetchBranchesPending(repository));
|
dispatch(fetchBranchesPending(repository));
|
||||||
let link = (repository._links.branches as Link).href;
|
|
||||||
if (fullInformation) {
|
|
||||||
link += "?fullInformation=true";
|
|
||||||
}
|
|
||||||
return apiClient
|
return apiClient
|
||||||
.get(link)
|
.get((repository._links.branches as Link).href)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
dispatch(fetchBranchesSuccess(data, repository));
|
dispatch(fetchBranchesSuccess(data, repository));
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ class CodeOverview extends React.Component<Props> {
|
|||||||
const mapDispatchToProps = (dispatch: any) => {
|
const mapDispatchToProps = (dispatch: any) => {
|
||||||
return {
|
return {
|
||||||
fetchBranches: (repo: Repository) => {
|
fetchBranches: (repo: Repository) => {
|
||||||
dispatch(fetchBranches(repo, false));
|
dispatch(fetchBranches(repo));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,14 +52,14 @@ public class BranchCollectionToDtoMapper {
|
|||||||
this.branchToDtoMapper = branchToDtoMapper;
|
this.branchToDtoMapper = branchToDtoMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HalRepresentation map(Repository repository, Collection<Branch> branches, boolean fullInformation) {
|
public HalRepresentation map(Repository repository, Collection<Branch> branches) {
|
||||||
return new HalRepresentation(
|
return new HalRepresentation(
|
||||||
createLinks(repository),
|
createLinks(repository),
|
||||||
embedDtos(getBranchDtoList(repository.getNamespace(), repository.getName(), branches, fullInformation)));
|
embedDtos(getBranchDtoList(repository.getNamespace(), repository.getName(), branches)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BranchDto> getBranchDtoList(String namespace, String name, Collection<Branch> branches, boolean fullInformation) {
|
public List<BranchDto> getBranchDtoList(String namespace, String name, Collection<Branch> branches) {
|
||||||
return branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name), fullInformation)).collect(toList());
|
return branches.stream().map(branch -> branchToDtoMapper.map(branch, new NamespaceAndName(namespace, name))).collect(toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Links createLinks(Repository repository) {
|
private Links createLinks(Repository repository) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import de.otto.edison.hal.Embedded;
|
import de.otto.edison.hal.Embedded;
|
||||||
import de.otto.edison.hal.HalRepresentation;
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
import de.otto.edison.hal.Links;
|
import de.otto.edison.hal.Links;
|
||||||
@@ -35,11 +34,11 @@ import org.hibernate.validator.constraints.Length;
|
|||||||
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
|
@SuppressWarnings("java:S2160") // we do not need this for dto
|
||||||
public class BranchDto extends HalRepresentation {
|
public class BranchDto extends HalRepresentation {
|
||||||
|
|
||||||
private static final String VALID_CHARACTERS_AT_START_AND_END = "\\w-,;\\]{}@&+=$#`|<>";
|
private static final String VALID_CHARACTERS_AT_START_AND_END = "\\w-,;\\]{}@&+=$#`|<>";
|
||||||
@@ -52,10 +51,6 @@ public class BranchDto extends HalRepresentation {
|
|||||||
private String name;
|
private String name;
|
||||||
private String revision;
|
private String revision;
|
||||||
private boolean defaultBranch;
|
private boolean defaultBranch;
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
|
||||||
private Instant lastModified;
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
|
||||||
private PersonDto lastModifier;
|
|
||||||
|
|
||||||
BranchDto(Links links, Embedded embedded) {
|
BranchDto(Links links, Embedded embedded) {
|
||||||
super(links, embedded);
|
super(links, embedded);
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public class BranchRootResource {
|
|||||||
.stream()
|
.stream()
|
||||||
.filter(branch -> branchName.equals(branch.getName()))
|
.filter(branch -> branchName.equals(branch.getName()))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.map(branch -> branchToDtoMapper.map(branch, namespaceAndName, fullInformation))
|
.map(branch -> branchToDtoMapper.map(branch, namespaceAndName))
|
||||||
.map(Response::ok)
|
.map(Response::ok)
|
||||||
.orElseThrow(() -> notFound(entity("branch", branchName).in(namespaceAndName)))
|
.orElseThrow(() -> notFound(entity("branch", branchName).in(namespaceAndName)))
|
||||||
.build();
|
.build();
|
||||||
@@ -300,12 +300,11 @@ public class BranchRootResource {
|
|||||||
))
|
))
|
||||||
public Response getAll(
|
public Response getAll(
|
||||||
@PathParam("namespace") String namespace,
|
@PathParam("namespace") String namespace,
|
||||||
@PathParam("name") String name,
|
@PathParam("name") String name
|
||||||
@QueryParam("fullInformation") @DefaultValue("false") boolean fullInformation
|
|
||||||
) throws IOException {
|
) throws IOException {
|
||||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||||
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
Branches branches = repositoryService.getBranchesCommand().getBranches();
|
||||||
return Response.ok(branchCollectionToDtoMapper.map(repositoryService.getRepository(), branches.getBranches(), fullInformation)).build();
|
return Response.ok(branchCollectionToDtoMapper.map(repositoryService.getRepository(), branches.getBranches())).build();
|
||||||
} catch (CommandNotSupportedException ex) {
|
} catch (CommandNotSupportedException ex) {
|
||||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,19 +30,11 @@ import org.mapstruct.Context;
|
|||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
import org.mapstruct.Mapping;
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.ObjectFactory;
|
import org.mapstruct.ObjectFactory;
|
||||||
import sonia.scm.ContextEntry;
|
|
||||||
import sonia.scm.repository.Branch;
|
import sonia.scm.repository.Branch;
|
||||||
import sonia.scm.repository.Changeset;
|
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.Person;
|
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
|
||||||
import sonia.scm.web.EdisonHalAppender;
|
import sonia.scm.web.EdisonHalAppender;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import static de.otto.edison.hal.Link.linkBuilder;
|
import static de.otto.edison.hal.Link.linkBuilder;
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
@@ -53,16 +45,11 @@ public abstract class BranchToBranchDtoMapper extends HalAppenderMapper {
|
|||||||
@Inject
|
@Inject
|
||||||
private ResourceLinks resourceLinks;
|
private ResourceLinks resourceLinks;
|
||||||
|
|
||||||
@Inject
|
|
||||||
private RepositoryServiceFactory serviceFactory;
|
|
||||||
|
|
||||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||||
public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName, boolean fullInformation);
|
public abstract BranchDto map(Branch branch, @Context NamespaceAndName namespaceAndName);
|
||||||
|
|
||||||
abstract PersonDto map(Person person);
|
|
||||||
|
|
||||||
@ObjectFactory
|
@ObjectFactory
|
||||||
BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch, boolean fullInformation) {
|
BranchDto createDto(@Context NamespaceAndName namespaceAndName, Branch branch) {
|
||||||
Links.Builder linksBuilder = linkingTo()
|
Links.Builder linksBuilder = linkingTo()
|
||||||
.self(resourceLinks.branch().self(namespaceAndName, branch.getName()))
|
.self(resourceLinks.branch().self(namespaceAndName, branch.getName()))
|
||||||
.single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build())
|
.single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, branch.getName())).build())
|
||||||
@@ -71,22 +58,7 @@ public abstract class BranchToBranchDtoMapper extends HalAppenderMapper {
|
|||||||
|
|
||||||
Embedded.Builder embeddedBuilder = Embedded.embeddedBuilder();
|
Embedded.Builder embeddedBuilder = Embedded.embeddedBuilder();
|
||||||
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName);
|
applyEnrichers(new EdisonHalAppender(linksBuilder, embeddedBuilder), branch, namespaceAndName);
|
||||||
BranchDto branchDto = new BranchDto(linksBuilder.build(), embeddedBuilder.build());
|
|
||||||
|
|
||||||
if (fullInformation) {
|
return 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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return branchDto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ public abstract class DefaultChangesetToChangesetDtoMapper extends HalAppenderMa
|
|||||||
}
|
}
|
||||||
if (repositoryService.isSupported(Command.BRANCHES)) {
|
if (repositoryService.isSupported(Command.BRANCHES)) {
|
||||||
embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name,
|
embeddedBuilder.with("branches", branchCollectionToDtoMapper.getBranchDtoList(namespace, name,
|
||||||
getListOfObjects(source.getBranches(), branchName -> Branch.normalBranch(branchName, source.getId())), false));
|
getListOfObjects(source.getBranches(), branchName -> Branch.normalBranch(branchName, source.getId()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (repositoryService.isSupported(Command.DIFF_RESULT)) {
|
if (repositoryService.isSupported(Command.DIFF_RESULT)) {
|
||||||
|
|||||||
@@ -24,29 +24,16 @@
|
|||||||
|
|
||||||
package sonia.scm.api.v2.resources;
|
package sonia.scm.api.v2.resources;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Answers;
|
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import sonia.scm.repository.Branch;
|
import sonia.scm.repository.Branch;
|
||||||
import sonia.scm.repository.Changeset;
|
|
||||||
import sonia.scm.repository.ChangesetPagingResult;
|
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.PersonTestData;
|
|
||||||
import sonia.scm.repository.api.LogCommandBuilder;
|
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class BranchToBranchDtoMapperTest {
|
class BranchToBranchDtoMapperTest {
|
||||||
@@ -56,13 +43,6 @@ class BranchToBranchDtoMapperTest {
|
|||||||
@SuppressWarnings("unused") // Is injected
|
@SuppressWarnings("unused") // Is injected
|
||||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||||
|
|
||||||
@Mock
|
|
||||||
private RepositoryServiceFactory serviceFactory;
|
|
||||||
@Mock
|
|
||||||
private RepositoryService repositoryService;
|
|
||||||
@Mock(answer = Answers.RETURNS_SELF)
|
|
||||||
private LogCommandBuilder logCommandBuilder;
|
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private BranchToBranchDtoMapperImpl mapper;
|
private BranchToBranchDtoMapperImpl mapper;
|
||||||
|
|
||||||
@@ -79,27 +59,7 @@ class BranchToBranchDtoMapperTest {
|
|||||||
|
|
||||||
Branch branch = Branch.normalBranch("master", "42");
|
Branch branch = Branch.normalBranch("master", "42");
|
||||||
|
|
||||||
BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold"), false);
|
BranchDto dto = mapper.map(branch, new NamespaceAndName("hitchhiker", "heart-of-gold"));
|
||||||
assertThat(dto.getLinks().getLinkBy("ka").get().getHref()).isEqualTo("http://hitchhiker/heart-of-gold/master");
|
assertThat(dto.getLinks().getLinkBy("ka").get().getHref()).isEqualTo("http://hitchhiker/heart-of-gold/master");
|
||||||
assertThat(dto.getLastModified()).isNull();
|
|
||||||
assertThat(dto.getLastModifier()).isNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void shouldMapLastChangeDateAndLastModifier() throws IOException {
|
|
||||||
long creationTime = 1000000000;
|
|
||||||
Changeset changeset = new Changeset("1", 1L, PersonTestData.ZAPHOD);
|
|
||||||
changeset.setDate(creationTime);
|
|
||||||
|
|
||||||
when(serviceFactory.create(any(NamespaceAndName.class))).thenReturn(repositoryService);
|
|
||||||
when(repositoryService.getLogCommand()).thenReturn(logCommandBuilder);
|
|
||||||
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"), true);
|
|
||||||
|
|
||||||
assertThat(dto.getLastModified()).isEqualTo(Instant.ofEpochMilli(creationTime));
|
|
||||||
assertThat(dto.getLastModifier().getName()).isEqualTo(PersonTestData.ZAPHOD.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user