mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-07 22:15:45 +01:00
Add statistics for diffs (added, deleted, modified)
Extend the diff result from the diff command to include modified, added and deleted file count and add DiffStats component to ui-components. Pushed-by: Rene Pfeuffer<rene.pfeuffer@cloudogu.com> Pushed-by: Tarik Gürsoy<tarik.guersoy@cloudogu.com> Co-authored-by: René Pfeuffer<rene.pfeuffer@cloudogu.com> Co-authored-by: Tarik Gürsoy<tarik.guersoy@cloudogu.com>
This commit is contained in:
4
gradle/changelog/showmodifiedfiles_component_added.yml
Normal file
4
gradle/changelog/showmodifiedfiles_component_added.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
- type: added
|
||||||
|
description: Show number of modified, deleted and added files in diffs
|
||||||
|
- type: fixed
|
||||||
|
description: Show diffs in compare view
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
package sonia.scm.repository.api;
|
package sonia.scm.repository.api;
|
||||||
|
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static java.util.Optional.empty;
|
import static java.util.Optional.empty;
|
||||||
@@ -49,4 +51,29 @@ public interface DiffResult extends Iterable<DiffFile> {
|
|||||||
default IgnoreWhitespaceLevel getIgnoreWhitespace() {
|
default IgnoreWhitespaceLevel getIgnoreWhitespace() {
|
||||||
return IgnoreWhitespaceLevel.NONE;
|
return IgnoreWhitespaceLevel.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function returns statistics if they are supported.
|
||||||
|
* @since 3.4.0
|
||||||
|
*/
|
||||||
|
default Optional<DiffStatistics> getStatistics() {
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value
|
||||||
|
class DiffStatistics {
|
||||||
|
/**
|
||||||
|
* number of added files in a diff
|
||||||
|
*/
|
||||||
|
int added;
|
||||||
|
/**
|
||||||
|
* number of modified files in a diff
|
||||||
|
*/
|
||||||
|
int modified;
|
||||||
|
/**
|
||||||
|
* number of deleted files in a diff
|
||||||
|
*/
|
||||||
|
int deleted;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,4 +188,26 @@ public class GitDiffResult implements DiffResult {
|
|||||||
public IgnoreWhitespaceLevel getIgnoreWhitespace() {
|
public IgnoreWhitespaceLevel getIgnoreWhitespace() {
|
||||||
return ignoreWhitespaceLevel;
|
return ignoreWhitespaceLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<DiffStatistics> getStatistics() {
|
||||||
|
int addCounter = 0;
|
||||||
|
int modifiedCounter = 0;
|
||||||
|
int deletedCounter = 0;
|
||||||
|
for (DiffEntry diffEntry : diffEntries) {
|
||||||
|
switch (diffEntry.getChangeType()) {
|
||||||
|
case ADD:
|
||||||
|
++addCounter;
|
||||||
|
break;
|
||||||
|
case MODIFY:
|
||||||
|
++modifiedCounter;
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
++deletedCounter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DiffStatistics stats = new DiffStatistics(addCounter, modifiedCounter, deletedCounter);
|
||||||
|
return Optional.of(stats);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,14 @@ public class GitDiffResultCommandTest extends AbstractGitCommandTestBase {
|
|||||||
assertThat(hunks).isExhausted();
|
assertThat(hunks).isExhausted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldComputeStatistics() throws IOException {
|
||||||
|
DiffResult diffResult = createDiffResult("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4");
|
||||||
|
assertThat(diffResult.getStatistics()).get().extracting("deleted").isEqualTo(1);
|
||||||
|
assertThat(diffResult.getStatistics()).get().extracting("modified").isEqualTo(1);
|
||||||
|
assertThat(diffResult.getStatistics()).get().extracting("added").isEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldNotIgnoreWhiteSpace() throws IOException {
|
public void shouldNotIgnoreWhiteSpace() throws IOException {
|
||||||
GitDiffResultCommand gitDiffResultCommand = new GitDiffResultCommand(createContext());
|
GitDiffResultCommand gitDiffResultCommand = new GitDiffResultCommand(createContext());
|
||||||
|
|||||||
56
scm-ui/ui-components/src/repos/DiffStatistics.tsx
Normal file
56
scm-ui/ui-components/src/repos/DiffStatistics.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import { Trans, useTranslation } from "react-i18next";
|
||||||
|
import { Statistics } from "@scm-manager/ui-types";
|
||||||
|
import { Tag } from "@scm-manager/ui-components";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
type DiffStatisticsProps = { data: Statistics | undefined };
|
||||||
|
|
||||||
|
const DiffStatisticsContainer = styled.div`
|
||||||
|
float: left;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const DiffStatistics: FC<DiffStatisticsProps> = ({ data }) => {
|
||||||
|
const [t] = useTranslation("repos");
|
||||||
|
return !data ? (
|
||||||
|
<div></div>
|
||||||
|
) : (
|
||||||
|
<DiffStatisticsContainer>
|
||||||
|
<Trans
|
||||||
|
t={t}
|
||||||
|
i18nKey="changesets.showModifiedFiles"
|
||||||
|
values={{ newFiles: data.added, modified: data.modified, deleted: data.deleted }}
|
||||||
|
components={{ tag: <Tag size={"normal"} rounded={true} className={"mx-1"} /> }}
|
||||||
|
></Trans>
|
||||||
|
</DiffStatisticsContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DiffStatistics;
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React, { FC } from "react";
|
import React, { FC, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { NotFoundError, useDiff } from "@scm-manager/ui-api";
|
import { NotFoundError, useDiff } from "@scm-manager/ui-api";
|
||||||
import ErrorNotification from "../ErrorNotification";
|
import ErrorNotification from "../ErrorNotification";
|
||||||
@@ -30,12 +30,13 @@ import Notification from "../Notification";
|
|||||||
import Button from "../buttons/Button";
|
import Button from "../buttons/Button";
|
||||||
import Diff from "./Diff";
|
import Diff from "./Diff";
|
||||||
import { DiffObjectProps } from "./DiffTypes";
|
import { DiffObjectProps } from "./DiffTypes";
|
||||||
|
import DiffStatistics from "./DiffStatistics";
|
||||||
|
import { DiffDropDown } from "../index";
|
||||||
|
|
||||||
type Props = DiffObjectProps & {
|
type Props = DiffObjectProps & {
|
||||||
url: string;
|
url: string;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
refetchOnWindowFocus?: boolean;
|
refetchOnWindowFocus?: boolean;
|
||||||
ignoreWhitespace?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotificationProps = {
|
type NotificationProps = {
|
||||||
@@ -45,6 +46,7 @@ type NotificationProps = {
|
|||||||
|
|
||||||
const PartialNotification: FC<NotificationProps> = ({ fetchNextPage, isFetchingNextPage }) => {
|
const PartialNotification: FC<NotificationProps> = ({ fetchNextPage, isFetchingNextPage }) => {
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Notification className="mt-5" type="info">
|
<Notification className="mt-5" type="info">
|
||||||
<div className="columns is-centered">
|
<div className="columns is-centered">
|
||||||
@@ -55,14 +57,27 @@ const PartialNotification: FC<NotificationProps> = ({ fetchNextPage, isFetchingN
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const LoadingDiff: FC<Props> = ({ url, limit, refetchOnWindowFocus, ignoreWhitespace, ...props }) => {
|
const LoadingDiff: FC<Props> = ({ url, limit, refetchOnWindowFocus, ...props }) => {
|
||||||
|
const [ignoreWhitespace, setIgnoreWhitespace] = useState(false);
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const evaluateWhiteSpace = () => {
|
||||||
|
return ignoreWhitespace ? "ALL" : "NONE"
|
||||||
|
}
|
||||||
const { error, isLoading, data, fetchNextPage, isFetchingNextPage } = useDiff(url, {
|
const { error, isLoading, data, fetchNextPage, isFetchingNextPage } = useDiff(url, {
|
||||||
limit,
|
limit,
|
||||||
refetchOnWindowFocus,
|
refetchOnWindowFocus,
|
||||||
ignoreWhitespace,
|
ignoreWhitespace: evaluateWhiteSpace(),
|
||||||
});
|
});
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
|
|
||||||
|
const ignoreWhitespaces = () => {
|
||||||
|
setIgnoreWhitespace(!ignoreWhitespace);
|
||||||
|
};
|
||||||
|
|
||||||
|
const collapseDiffs = () => {
|
||||||
|
setCollapsed(!collapsed);
|
||||||
|
};
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error instanceof NotFoundError) {
|
if (error instanceof NotFoundError) {
|
||||||
return <Notification type="info">{t("changesets.noChangesets")}</Notification>;
|
return <Notification type="info">{t("changesets.noChangesets")}</Notification>;
|
||||||
@@ -75,7 +90,16 @@ const LoadingDiff: FC<Props> = ({ url, limit, refetchOnWindowFocus, ignoreWhites
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Diff diff={data.files} ignoreWhitespace={ignoreWhitespace} {...props} />
|
<div className="is-flex has-gap-4 mb-4 mt-4 is-justify-content-space-between">
|
||||||
|
<DiffStatistics data={data.statistics} />
|
||||||
|
<DiffDropDown collapseDiffs={collapseDiffs} ignoreWhitespaces={ignoreWhitespaces} renderOnMount={true} />
|
||||||
|
</div>
|
||||||
|
<Diff
|
||||||
|
defaultCollapse={collapsed}
|
||||||
|
diff={data.files}
|
||||||
|
ignoreWhitespace={evaluateWhiteSpace()}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
{data.partial ? (
|
{data.partial ? (
|
||||||
<PartialNotification fetchNextPage={fetchNextPage} isFetchingNextPage={isFetchingNextPage} />
|
<PartialNotification fetchNextPage={fetchNextPage} isFetchingNextPage={isFetchingNextPage} />
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ import { FileControlFactory } from "../DiffTypes";
|
|||||||
|
|
||||||
type Props = WithTranslation & {
|
type Props = WithTranslation & {
|
||||||
changeset: Changeset;
|
changeset: Changeset;
|
||||||
defaultCollapse?: boolean;
|
|
||||||
ignoreWhitespace?: string;
|
|
||||||
fileControlFactory?: FileControlFactory;
|
fileControlFactory?: FileControlFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,7 +48,8 @@ export const createUrl = (changeset: HalRepresentation) => {
|
|||||||
|
|
||||||
class ChangesetDiff extends React.Component<Props> {
|
class ChangesetDiff extends React.Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
const { changeset, fileControlFactory, defaultCollapse, ignoreWhitespace, t } = this.props;
|
const { changeset, fileControlFactory, t } = this.props;
|
||||||
|
|
||||||
if (!isDiffSupported(changeset)) {
|
if (!isDiffSupported(changeset)) {
|
||||||
return <Notification type="danger">{t("changeset.diffNotSupported")}</Notification>;
|
return <Notification type="danger">{t("changeset.diffNotSupported")}</Notification>;
|
||||||
} else {
|
} else {
|
||||||
@@ -58,8 +57,6 @@ class ChangesetDiff extends React.Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<LoadingDiff
|
<LoadingDiff
|
||||||
url={url}
|
url={url}
|
||||||
defaultCollapse={defaultCollapse}
|
|
||||||
ignoreWhitespace={ignoreWhitespace}
|
|
||||||
sideBySide={false}
|
sideBySide={false}
|
||||||
fileControlFactory={fileControlFactory}
|
fileControlFactory={fileControlFactory}
|
||||||
stickyHeader={true}
|
stickyHeader={true}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export { default as CommitAuthor } from "./CommitAuthor";
|
|||||||
export { default as HealthCheckFailureDetail } from "./HealthCheckFailureDetail";
|
export { default as HealthCheckFailureDetail } from "./HealthCheckFailureDetail";
|
||||||
export { default as RepositoryFlags } from "./RepositoryFlags";
|
export { default as RepositoryFlags } from "./RepositoryFlags";
|
||||||
export { default as DiffDropDown } from "./DiffDropDown";
|
export { default as DiffDropDown } from "./DiffDropDown";
|
||||||
|
export { default as DiffStatistics } from "./DiffStatistics"
|
||||||
|
|
||||||
export {
|
export {
|
||||||
File,
|
File,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export type FileChangeType = "add" | "modify" | "delete" | "copy" | "rename";
|
|||||||
export type Diff = HalRepresentation & {
|
export type Diff = HalRepresentation & {
|
||||||
files: FileDiff[];
|
files: FileDiff[];
|
||||||
partial: boolean;
|
partial: boolean;
|
||||||
|
statistics?: Statistics;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FileDiff = {
|
export type FileDiff = {
|
||||||
@@ -49,6 +50,12 @@ export type FileDiff = {
|
|||||||
_links?: Links;
|
_links?: Links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Statistics = {
|
||||||
|
added: number;
|
||||||
|
deleted: number;
|
||||||
|
modified: number;
|
||||||
|
}
|
||||||
|
|
||||||
export type Hunk = {
|
export type Hunk = {
|
||||||
changes: Change[];
|
changes: Change[];
|
||||||
content: string;
|
content: string;
|
||||||
|
|||||||
@@ -270,7 +270,8 @@
|
|||||||
"checkBoxHideWhitespaceChanges": "Ignoriere Diffs die nur Whitespace Änderungen enthalten",
|
"checkBoxHideWhitespaceChanges": "Ignoriere Diffs die nur Whitespace Änderungen enthalten",
|
||||||
"activateWhitespace": "Whitespace-Änderungen einblenden",
|
"activateWhitespace": "Whitespace-Änderungen einblenden",
|
||||||
"moreDiffsAvailable": "Es sind weitere Diffs verfügbar",
|
"moreDiffsAvailable": "Es sind weitere Diffs verfügbar",
|
||||||
"loadMore": "Weitere laden"
|
"loadMore": "Weitere laden",
|
||||||
|
"showModifiedFiles": "<tag>{{newFiles}}</tag> hinzugefügte, <tag>{{modified}}</tag> geänderte, <tag>{{deleted}}</tag> gelöschte Dateien"
|
||||||
},
|
},
|
||||||
"changeset": {
|
"changeset": {
|
||||||
"label": "Changeset",
|
"label": "Changeset",
|
||||||
|
|||||||
@@ -270,7 +270,8 @@
|
|||||||
"checkBoxHideWhitespaceChanges": "Hide Diffs which only contain whitespace changes",
|
"checkBoxHideWhitespaceChanges": "Hide Diffs which only contain whitespace changes",
|
||||||
"activateWhitespace": "Show whitespaces changes",
|
"activateWhitespace": "Show whitespaces changes",
|
||||||
"moreDiffsAvailable": "There are more diffs available",
|
"moreDiffsAvailable": "There are more diffs available",
|
||||||
"loadMore": "Load more"
|
"loadMore": "Load more",
|
||||||
|
"showModifiedFiles": "<tag>{{newFiles}}</tag> added, <tag>{{modified}}</tag> modified, <tag>{{deleted}}</tag> deleted"
|
||||||
},
|
},
|
||||||
"changeset": {
|
"changeset": {
|
||||||
"label": "Changeset",
|
"label": "Changeset",
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ import {
|
|||||||
DateFromNow,
|
DateFromNow,
|
||||||
FileControlFactory,
|
FileControlFactory,
|
||||||
SignatureIcon,
|
SignatureIcon,
|
||||||
DiffDropDown
|
|
||||||
} from "@scm-manager/ui-components";
|
} from "@scm-manager/ui-components";
|
||||||
import { Tooltip, SubSubtitle } from "@scm-manager/ui-core";
|
import { Tooltip, SubSubtitle } from "@scm-manager/ui-core";
|
||||||
import { Button, Icon } from "@scm-manager/ui-buttons";
|
import { Button, Icon } from "@scm-manager/ui-buttons";
|
||||||
@@ -178,8 +177,6 @@ const ContainedInTags: FC<{ changeset: Changeset; repository: Repository }> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory }) => {
|
const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory }) => {
|
||||||
const [collapsed, setCollapsed] = useState(false);
|
|
||||||
const [ignoreWhitespace, setIgnoreWhitespace] = useState(false);
|
|
||||||
const [isTagCreationModalVisible, setTagCreationModalVisible] = useState(false);
|
const [isTagCreationModalVisible, setTagCreationModalVisible] = useState(false);
|
||||||
const [t] = useTranslation("repos");
|
const [t] = useTranslation("repos");
|
||||||
|
|
||||||
@@ -193,14 +190,6 @@ const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory
|
|||||||
));
|
));
|
||||||
const showCreateButton = "tag" in changeset._links;
|
const showCreateButton = "tag" in changeset._links;
|
||||||
|
|
||||||
const collapseDiffs = () => {
|
|
||||||
setCollapsed(!collapsed);
|
|
||||||
};
|
|
||||||
|
|
||||||
const ignoreWhitespaces = () => {
|
|
||||||
setIgnoreWhitespace(!ignoreWhitespace);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classNames("content", "m-0")}>
|
<div className={classNames("content", "m-0")}>
|
||||||
@@ -280,15 +269,9 @@ const ChangesetDetails: FC<Props> = ({ changeset, repository, fileControlFactory
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="is-flex has-gap-4 mb-4 is-justify-content-flex-end">
|
|
||||||
<DiffDropDown collapseDiffs={collapseDiffs} ignoreWhitespaces={ignoreWhitespaces} renderOnMount={false}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ChangesetDiff
|
<ChangesetDiff
|
||||||
changeset={changeset}
|
changeset={changeset}
|
||||||
fileControlFactory={fileControlFactory}
|
fileControlFactory={fileControlFactory}
|
||||||
defaultCollapse={collapsed}
|
|
||||||
ignoreWhitespace={ignoreWhitespace ? "ALL" : "NONE"}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import de.otto.edison.hal.HalRepresentation;
|
import de.otto.edison.hal.HalRepresentation;
|
||||||
import de.otto.edison.hal.Links;
|
import de.otto.edison.hal.Links;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import sonia.scm.repository.api.DiffResult;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -44,6 +46,7 @@ public class DiffResultDto extends HalRepresentation {
|
|||||||
|
|
||||||
private List<FileDto> files;
|
private List<FileDto> files;
|
||||||
private boolean partial;
|
private boolean partial;
|
||||||
|
private DiffStatisticsDto statistics;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@@ -69,6 +72,15 @@ public class DiffResultDto extends HalRepresentation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class DiffStatisticsDto {
|
||||||
|
private int added;
|
||||||
|
private int deleted;
|
||||||
|
private int modified;
|
||||||
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||||
public static class HunkDto {
|
public static class HunkDto {
|
||||||
|
|||||||
@@ -43,9 +43,6 @@ import static de.otto.edison.hal.Link.link;
|
|||||||
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;
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO conflicts
|
|
||||||
*/
|
|
||||||
class DiffResultToDiffResultDtoMapper {
|
class DiffResultToDiffResultDtoMapper {
|
||||||
|
|
||||||
private final ResourceLinks resourceLinks;
|
private final ResourceLinks resourceLinks;
|
||||||
@@ -108,6 +105,16 @@ class DiffResultToDiffResultDtoMapper {
|
|||||||
files.add(mapFile(file, result, repository, revision));
|
files.add(mapFile(file, result, repository, revision));
|
||||||
}
|
}
|
||||||
dto.setFiles(files);
|
dto.setFiles(files);
|
||||||
|
Optional<DiffResult.DiffStatistics> statistics = result.getStatistics();
|
||||||
|
if (statistics.isPresent()) {
|
||||||
|
DiffResult.DiffStatistics diffStatistics = statistics.get();
|
||||||
|
DiffResultDto.DiffStatisticsDto diffStatisticsDto = new DiffResultDto.DiffStatisticsDto(
|
||||||
|
diffStatistics.getAdded(),
|
||||||
|
diffStatistics.getDeleted(),
|
||||||
|
diffStatistics.getModified()
|
||||||
|
);
|
||||||
|
dto.setStatistics(diffStatisticsDto);
|
||||||
|
}
|
||||||
dto.setPartial(result.isPartial());
|
dto.setPartial(result.isPartial());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -177,6 +177,18 @@ class DiffResultToDiffResultDtoMapperTest {
|
|||||||
.isEqualTo("/scm/api/v2/repositories/space/X/incoming/feature%2Fsome/master/diff/parsed?ignoreWhitespace=ALL&offset=30&limit=10");
|
.isEqualTo("/scm/api/v2/repositories/space/X/incoming/feature%2Fsome/master/diff/parsed?ignoreWhitespace=ALL&offset=30&limit=10");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldMapStatistics() {
|
||||||
|
DiffResult result = createResult();
|
||||||
|
when(result.getStatistics()).thenReturn(of(new DiffResult.DiffStatistics(1, 2, 3)));
|
||||||
|
|
||||||
|
DiffResultDto.DiffStatisticsDto dto = mapper.mapForIncoming(REPOSITORY, result, "feature/some", "master").getStatistics();
|
||||||
|
|
||||||
|
assertThat(dto.getAdded()).isEqualTo(1);
|
||||||
|
assertThat(dto.getModified()).isEqualTo(2);
|
||||||
|
assertThat(dto.getDeleted()).isEqualTo(3);
|
||||||
|
}
|
||||||
|
|
||||||
private void mockPartialResult(DiffResult result) {
|
private void mockPartialResult(DiffResult result) {
|
||||||
when(result.getLimit()).thenReturn(of(10));
|
when(result.getLimit()).thenReturn(of(10));
|
||||||
when(result.getOffset()).thenReturn(20);
|
when(result.getOffset()).thenReturn(20);
|
||||||
|
|||||||
Reference in New Issue
Block a user