mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-01 11:05:56 +01:00
Merged 2.0.0-m3
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { withContextPath } from "./urls";
|
import {withContextPath} from "./urls";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
src: string,
|
src: string,
|
||||||
@@ -9,9 +9,18 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Image extends React.Component<Props> {
|
class Image extends React.Component<Props> {
|
||||||
|
|
||||||
|
createImageSrc = () => {
|
||||||
|
const { src } = this.props;
|
||||||
|
if (src.startsWith("http")) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
return withContextPath(src);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { src, alt, className } = this.props;
|
const { alt, className } = this.props;
|
||||||
return <img className={className} src={withContextPath(src)} alt={alt} />;
|
return <img className={className} src={this.createImageSrc()} alt={alt} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Route, Link } from "react-router-dom";
|
import {Link, Route} from "react-router-dom";
|
||||||
|
|
||||||
// TODO mostly copy of PrimaryNavigationLink
|
// TODO mostly copy of PrimaryNavigationLink
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,21 @@ export function withContextPath(path: string) {
|
|||||||
return contextPath + path;
|
return contextPath + path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function withEndingSlash(url: string) {
|
||||||
|
if (url.endsWith("/")) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return url + "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function concat(base: string, ...parts: string[]) {
|
||||||
|
let url = base;
|
||||||
|
for ( let p of parts) {
|
||||||
|
url = withEndingSlash(url) + p;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
export function getPageFromMatch(match: any) {
|
export function getPageFromMatch(match: any) {
|
||||||
let page = parseInt(match.params.page, 10);
|
let page = parseInt(match.params.page, 10);
|
||||||
if (isNaN(page) || !page) {
|
if (isNaN(page) || !page) {
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { getPageFromMatch } from "./urls";
|
import {concat, getPageFromMatch, withEndingSlash} from "./urls";
|
||||||
|
|
||||||
|
describe("tests for withEndingSlash", () => {
|
||||||
|
|
||||||
|
it("should append missing slash", () => {
|
||||||
|
expect(withEndingSlash("abc")).toBe("abc/");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not append a second slash", () => {
|
||||||
|
expect(withEndingSlash("abc/")).toBe("abc/");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("concat tests", () => {
|
||||||
|
|
||||||
|
it("should concat the parts to a single url", () => {
|
||||||
|
expect(concat("a")).toBe("a");
|
||||||
|
expect(concat("a", "b")).toBe("a/b");
|
||||||
|
expect(concat("a", "b", "c")).toBe("a/b/c");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe("tests for getPageFromMatch", () => {
|
describe("tests for getPageFromMatch", () => {
|
||||||
function createMatch(page: string) {
|
function createMatch(page: string) {
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import type { Links } from "./hal";
|
import type {Links} from "./hal";
|
||||||
|
|
||||||
export type Permission = {
|
export type Permission = PermissionCreateEntry & {
|
||||||
name: string,
|
_links: Links
|
||||||
type: string,
|
|
||||||
groupPermission: boolean,
|
|
||||||
_links?: Links
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PermissionEntry = {
|
export type PermissionCreateEntry = {
|
||||||
name: string,
|
name: string,
|
||||||
type: string,
|
type: string,
|
||||||
groupPermission: boolean
|
groupPermission: boolean
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ export type { Tag } from "./Tags";
|
|||||||
|
|
||||||
export type { Config } from "./Config";
|
export type { Config } from "./Config";
|
||||||
|
|
||||||
export type { Permission, PermissionEntry, PermissionCollection } from "./RepositoryPermissions";
|
export type { Permission, PermissionCreateEntry, PermissionCollection } from "./RepositoryPermissions";
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"summary": "Changeset {{id}} committed {{time}}"
|
"summary": "Changeset {{id}} was committed {{time}}"
|
||||||
},
|
},
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Author",
|
"name": "Author",
|
||||||
|
|||||||
32
scm-ui/src/repos/components/changesets/AvatarImage.js
Normal file
32
scm-ui/src/repos/components/changesets/AvatarImage.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import {binder} from "@scm-manager/ui-extensions";
|
||||||
|
import type {Changeset} from "@scm-manager/ui-types";
|
||||||
|
import {Image} from "@scm-manager/ui-components";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
changeset: Changeset
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvatarImage extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { changeset } = this.props;
|
||||||
|
|
||||||
|
const avatarFactory = binder.getExtension("changeset.avatar-factory");
|
||||||
|
if (avatarFactory) {
|
||||||
|
const avatar = avatarFactory(changeset);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
className="has-rounded-border"
|
||||||
|
src={avatar}
|
||||||
|
alt={changeset.author.name}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AvatarImage;
|
||||||
18
scm-ui/src/repos/components/changesets/AvatarWrapper.js
Normal file
18
scm-ui/src/repos/components/changesets/AvatarWrapper.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//@flow
|
||||||
|
import * as React from "react";
|
||||||
|
import {binder} from "@scm-manager/ui-extensions";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.Node
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvatarWrapper extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
if (binder.hasExtension("changeset.avatar-factory")) {
|
||||||
|
return <>{this.props.children}</>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AvatarWrapper;
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
//@flow
|
|
||||||
import React from "react";
|
|
||||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
|
||||||
import type { Changeset } from "@scm-manager/ui-types";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
changeset: Changeset
|
|
||||||
};
|
|
||||||
|
|
||||||
class ChangesetAvatar extends React.Component<Props> {
|
|
||||||
render() {
|
|
||||||
const { changeset } = this.props;
|
|
||||||
return (
|
|
||||||
<ExtensionPoint
|
|
||||||
name="repos.changeset-table.information"
|
|
||||||
renderAll={true}
|
|
||||||
props={{ changeset }}
|
|
||||||
>
|
|
||||||
{/* extension should render something like this: */}
|
|
||||||
{/* <div className="image is-64x64"> */}
|
|
||||||
{/* <figure className="media-left"> */}
|
|
||||||
{/* <Image src="/some/image.jpg" alt="Logo" /> */}
|
|
||||||
{/* </figure> */}
|
|
||||||
{/* </div> */}
|
|
||||||
</ExtensionPoint>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ChangesetAvatar;
|
|
||||||
97
scm-ui/src/repos/components/changesets/ChangesetDetails.js
Normal file
97
scm-ui/src/repos/components/changesets/ChangesetDetails.js
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import type {Changeset, Repository} from "../../../../../scm-ui-components/packages/ui-types/src/index";
|
||||||
|
import {Interpolate, translate} from "react-i18next";
|
||||||
|
import injectSheet from "react-jss";
|
||||||
|
import ChangesetTag from "./ChangesetTag";
|
||||||
|
import ChangesetAuthor from "./ChangesetAuthor";
|
||||||
|
import {parseDescription} from "./changesets";
|
||||||
|
import {DateFromNow} from "../../../../../scm-ui-components/packages/ui-components/src/index";
|
||||||
|
import AvatarWrapper from "./AvatarWrapper";
|
||||||
|
import AvatarImage from "./AvatarImage";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import ChangesetId from "./ChangesetId";
|
||||||
|
import type {Tag} from "@scm-manager/ui-types";
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
spacing: {
|
||||||
|
marginRight: "1em"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
changeset: Changeset,
|
||||||
|
repository: Repository,
|
||||||
|
t: string => string,
|
||||||
|
classes: any
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangesetDetails extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { changeset, repository, classes } = this.props;
|
||||||
|
|
||||||
|
const description = parseDescription(changeset.description);
|
||||||
|
|
||||||
|
const id = (
|
||||||
|
<ChangesetId repository={repository} changeset={changeset} link={false} />
|
||||||
|
);
|
||||||
|
const date = <DateFromNow date={changeset.date} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="content">
|
||||||
|
<h4>{description.title}</h4>
|
||||||
|
<article className="media">
|
||||||
|
<AvatarWrapper>
|
||||||
|
<p className={classNames("image", "is-64x64", classes.spacing)}>
|
||||||
|
<AvatarImage changeset={changeset} />
|
||||||
|
</p>
|
||||||
|
</AvatarWrapper>
|
||||||
|
<div className="media-content">
|
||||||
|
<p>
|
||||||
|
<ChangesetAuthor changeset={changeset} />
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Interpolate
|
||||||
|
i18nKey="changesets.changeset.summary"
|
||||||
|
id={id}
|
||||||
|
time={date}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="media-right">{this.renderTags()}</div>
|
||||||
|
</article>
|
||||||
|
<p>
|
||||||
|
{description.message.split("\n").map((item, key) => {
|
||||||
|
return (
|
||||||
|
<span key={key}>
|
||||||
|
{item}
|
||||||
|
<br />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTags = () => {
|
||||||
|
const { changeset } = this.props;
|
||||||
|
return changeset._embedded.tags || [];
|
||||||
|
};
|
||||||
|
|
||||||
|
renderTags = () => {
|
||||||
|
const tags = this.getTags();
|
||||||
|
if (tags.length > 0) {
|
||||||
|
return (
|
||||||
|
<div className="level-item">
|
||||||
|
{tags.map((tag: Tag) => {
|
||||||
|
return <ChangesetTag key={tag.name} tag={tag} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectSheet(styles)(translate("repos")(ChangesetDetails));
|
||||||
@@ -1,25 +1,47 @@
|
|||||||
//@flow
|
//@flow
|
||||||
|
|
||||||
import { Link } from "react-router-dom";
|
import {Link} from "react-router-dom";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Repository, Changeset } from "@scm-manager/ui-types";
|
import type {Changeset, Repository} from "@scm-manager/ui-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
repository: Repository,
|
||||||
changeset: Changeset
|
changeset: Changeset,
|
||||||
|
link: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class ChangesetId extends React.Component<Props> {
|
export default class ChangesetId extends React.Component<Props> {
|
||||||
render() {
|
static defaultProps = {
|
||||||
const { repository, changeset } = this.props;
|
link: true
|
||||||
|
};
|
||||||
|
|
||||||
|
shortId = (changeset: Changeset) => {
|
||||||
|
return changeset.id.substr(0, 7);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLink = () => {
|
||||||
|
const { changeset, repository } = this.props;
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={`/repo/${repository.namespace}/${repository.name}/changeset/${
|
to={`/repo/${repository.namespace}/${repository.name}/changeset/${
|
||||||
changeset.id
|
changeset.id
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{changeset.id.substr(0, 7)}
|
{this.shortId(changeset)}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderText = () => {
|
||||||
|
const { changeset } = this.props;
|
||||||
|
return this.shortId(changeset);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { link } = this.props;
|
||||||
|
if (link) {
|
||||||
|
return this.renderLink();
|
||||||
|
}
|
||||||
|
return this.renderText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import type { Changeset, Repository, Tag } from "@scm-manager/ui-types";
|
import type {Changeset, Repository, Tag} from "@scm-manager/ui-types";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { translate, Interpolate } from "react-i18next";
|
import {Interpolate, translate} from "react-i18next";
|
||||||
import ChangesetAvatar from "./ChangesetAvatar";
|
|
||||||
import ChangesetId from "./ChangesetId";
|
import ChangesetId from "./ChangesetId";
|
||||||
import injectSheet from "react-jss";
|
import injectSheet from "react-jss";
|
||||||
import { DateFromNow } from "@scm-manager/ui-components";
|
import {DateFromNow} from "@scm-manager/ui-components";
|
||||||
import ChangesetAuthor from "./ChangesetAuthor";
|
import ChangesetAuthor from "./ChangesetAuthor";
|
||||||
import ChangesetTag from "./ChangesetTag";
|
import ChangesetTag from "./ChangesetTag";
|
||||||
import { compose } from "redux";
|
import {compose} from "redux";
|
||||||
|
import {parseDescription} from "./changesets";
|
||||||
|
import AvatarWrapper from "./AvatarWrapper";
|
||||||
|
import AvatarImage from "./AvatarImage";
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
pointer: {
|
pointer: {
|
||||||
@@ -46,14 +48,23 @@ class ChangesetRow extends React.Component<Props> {
|
|||||||
const changesetLink = this.createLink(changeset);
|
const changesetLink = this.createLink(changeset);
|
||||||
const dateFromNow = <DateFromNow date={changeset.date} />;
|
const dateFromNow = <DateFromNow date={changeset.date} />;
|
||||||
const authorLine = <ChangesetAuthor changeset={changeset} />;
|
const authorLine = <ChangesetAuthor changeset={changeset} />;
|
||||||
|
const description = parseDescription(changeset.description);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={classNames("media", classes.inner)}>
|
<article className={classNames("media", classes.inner)}>
|
||||||
<ChangesetAvatar changeset={changeset} />
|
<AvatarWrapper>
|
||||||
|
<div>
|
||||||
|
<figure className="media-left">
|
||||||
|
<p className="image is-64x64">
|
||||||
|
<AvatarImage changeset={changeset} />
|
||||||
|
</p>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
</AvatarWrapper>
|
||||||
<div className={classNames("media-content", classes.withOverflow)}>
|
<div className={classNames("media-content", classes.withOverflow)}>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
<p className="is-ellipsis-overflow">
|
<p className="is-ellipsis-overflow">
|
||||||
{changeset.description}
|
<strong>{description.title}</strong>
|
||||||
<br />
|
<br />
|
||||||
<Interpolate
|
<Interpolate
|
||||||
i18nKey="changesets.changeset.summary"
|
i18nKey="changesets.changeset.summary"
|
||||||
|
|||||||
24
scm-ui/src/repos/components/changesets/changesets.js
Normal file
24
scm-ui/src/repos/components/changesets/changesets.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// @flow
|
||||||
|
export type Description = {
|
||||||
|
title: string,
|
||||||
|
message: string
|
||||||
|
};
|
||||||
|
|
||||||
|
export function parseDescription(description: string): Description {
|
||||||
|
const lineBreak = description.indexOf("\n");
|
||||||
|
|
||||||
|
let title;
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
if (lineBreak > 0) {
|
||||||
|
title = description.substring(0, lineBreak);
|
||||||
|
message = description.substring(lineBreak + 1);
|
||||||
|
} else {
|
||||||
|
title = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
message
|
||||||
|
};
|
||||||
|
}
|
||||||
16
scm-ui/src/repos/components/changesets/changesets.test.js
Normal file
16
scm-ui/src/repos/components/changesets/changesets.test.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
import {parseDescription} from "./changesets";
|
||||||
|
|
||||||
|
describe("parseDescription tests", () => {
|
||||||
|
it("should return a description with title and message", () => {
|
||||||
|
const desc = parseDescription("Hello\nTrillian");
|
||||||
|
expect(desc.title).toBe("Hello");
|
||||||
|
expect(desc.message).toBe("Trillian");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return a description with title and without message", () => {
|
||||||
|
const desc = parseDescription("Hello Trillian");
|
||||||
|
expect(desc.title).toBe("Hello Trillian");
|
||||||
|
});
|
||||||
|
});
|
||||||
75
scm-ui/src/repos/containers/ChangesetView.js
Normal file
75
scm-ui/src/repos/containers/ChangesetView.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//@flow
|
||||||
|
import React from "react";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { withRouter } from "react-router-dom";
|
||||||
|
import type { Changeset, Repository } from "@scm-manager/ui-types";
|
||||||
|
import {
|
||||||
|
fetchChangesetIfNeeded,
|
||||||
|
getChangeset,
|
||||||
|
getFetchChangesetFailure,
|
||||||
|
isFetchChangesetPending
|
||||||
|
} from "../modules/changesets";
|
||||||
|
import ChangesetDetails from "../components/changesets/ChangesetDetails";
|
||||||
|
import { translate } from "react-i18next";
|
||||||
|
import { ErrorPage, Loading } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string,
|
||||||
|
changeset: Changeset,
|
||||||
|
repository: Repository,
|
||||||
|
loading: boolean,
|
||||||
|
error: Error,
|
||||||
|
fetchChangesetIfNeeded: (repository: Repository, id: string) => void,
|
||||||
|
match: any,
|
||||||
|
t: string => string
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChangesetView extends React.Component<Props> {
|
||||||
|
componentDidMount() {
|
||||||
|
const { fetchChangesetIfNeeded, repository } = this.props;
|
||||||
|
const id = this.props.match.params.id;
|
||||||
|
fetchChangesetIfNeeded(repository, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { changeset, loading, error, t, repository } = this.props;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<ErrorPage
|
||||||
|
title={t("changeset-error.title")}
|
||||||
|
subtitle={t("changeset-error.subtitle")}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changeset || loading) return <Loading />;
|
||||||
|
|
||||||
|
return <ChangesetDetails changeset={changeset} repository={repository} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps: Props) => {
|
||||||
|
const repository = ownProps.repository;
|
||||||
|
const id = ownProps.match.params.id;
|
||||||
|
const changeset = getChangeset(state, repository, id);
|
||||||
|
const loading = isFetchChangesetPending(state, repository, id);
|
||||||
|
const error = getFetchChangesetFailure(state, repository, id);
|
||||||
|
return { changeset, error, loading };
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
fetchChangesetIfNeeded: (repository: Repository, id: string) => {
|
||||||
|
dispatch(fetchChangesetIfNeeded(repository, id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withRouter(
|
||||||
|
connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(translate("changesets")(ChangesetView))
|
||||||
|
);
|
||||||
@@ -1,13 +1,8 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { withRouter } from "react-router-dom";
|
import {withRouter} from "react-router-dom";
|
||||||
import type {
|
import type {Branch, Changeset, PagedCollection, Repository} from "@scm-manager/ui-types";
|
||||||
Branch,
|
|
||||||
Changeset,
|
|
||||||
PagedCollection,
|
|
||||||
Repository
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
import {
|
import {
|
||||||
fetchChangesets,
|
fetchChangesets,
|
||||||
getChangesets,
|
getChangesets,
|
||||||
@@ -16,15 +11,10 @@ import {
|
|||||||
selectListAsCollection
|
selectListAsCollection
|
||||||
} from "../modules/changesets";
|
} from "../modules/changesets";
|
||||||
|
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import ChangesetList from "../components/changesets/ChangesetList";
|
import ChangesetList from "../components/changesets/ChangesetList";
|
||||||
import {
|
import {ErrorNotification, getPageFromMatch, LinkPaginator, Loading} from "@scm-manager/ui-components";
|
||||||
ErrorNotification,
|
import {compose} from "redux";
|
||||||
LinkPaginator,
|
|
||||||
Loading,
|
|
||||||
getPageFromMatch
|
|
||||||
} from "@scm-manager/ui-components";
|
|
||||||
import { compose } from "redux";
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
repository: Repository,
|
repository: Repository,
|
||||||
|
|||||||
@@ -1,32 +1,23 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {deleteRepo, fetchRepo, getFetchRepoFailure, getRepository, isFetchRepoPending} from "../modules/repos";
|
||||||
deleteRepo,
|
|
||||||
fetchRepo,
|
import {connect} from "react-redux";
|
||||||
getFetchRepoFailure,
|
import {Route, Switch} from "react-router-dom";
|
||||||
getRepository,
|
import type {Repository} from "@scm-manager/ui-types";
|
||||||
isFetchRepoPending
|
|
||||||
} from "../modules/repos";
|
import {ErrorPage, Loading, Navigation, NavLink, Page, Section} from "@scm-manager/ui-components";
|
||||||
import { connect } from "react-redux";
|
import {translate} from "react-i18next";
|
||||||
import { Route, Switch } from "react-router-dom";
|
|
||||||
import type { Repository } from "@scm-manager/ui-types";
|
|
||||||
import {
|
|
||||||
ErrorPage,
|
|
||||||
Loading,
|
|
||||||
Navigation,
|
|
||||||
NavLink,
|
|
||||||
Page,
|
|
||||||
Section
|
|
||||||
} from "@scm-manager/ui-components";
|
|
||||||
import { translate } from "react-i18next";
|
|
||||||
import RepositoryDetails from "../components/RepositoryDetails";
|
import RepositoryDetails from "../components/RepositoryDetails";
|
||||||
import DeleteNavAction from "../components/DeleteNavAction";
|
import DeleteNavAction from "../components/DeleteNavAction";
|
||||||
import Edit from "../containers/Edit";
|
import Edit from "../containers/Edit";
|
||||||
import Permissions from "../permissions/containers/Permissions";
|
import Permissions from "../permissions/containers/Permissions";
|
||||||
|
|
||||||
import type { History } from "history";
|
import type {History} from "history";
|
||||||
import EditNavLink from "../components/EditNavLink";
|
import EditNavLink from "../components/EditNavLink";
|
||||||
|
|
||||||
import BranchRoot from "./BranchRoot";
|
import BranchRoot from "./BranchRoot";
|
||||||
|
import ChangesetView from "./ChangesetView";
|
||||||
import PermissionsNavLink from "../components/PermissionsNavLink";
|
import PermissionsNavLink from "../components/PermissionsNavLink";
|
||||||
import ScmDiff from "./ScmDiff";
|
import ScmDiff from "./ScmDiff";
|
||||||
|
|
||||||
@@ -73,6 +64,11 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
this.props.deleteRepo(repository, this.deleted);
|
this.props.deleteRepo(repository, this.deleted);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
matchChangeset = (route: any) => {
|
||||||
|
const url = this.matchedUrl();
|
||||||
|
return route.location.pathname.match(`${url}/changeset/`);
|
||||||
|
};
|
||||||
|
|
||||||
matches = (route: any) => {
|
matches = (route: any) => {
|
||||||
const url = this.matchedUrl();
|
const url = this.matchedUrl();
|
||||||
const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`);
|
const regex = new RegExp(`${url}(/branches)?/?[^/]*/changesets?.*`);
|
||||||
@@ -120,6 +116,11 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
<Route
|
||||||
|
exact
|
||||||
|
path={`${url}/changeset/:id`}
|
||||||
|
render={() => <ChangesetView repository={repository} />}
|
||||||
|
/>
|
||||||
<Route
|
<Route
|
||||||
path={`${url}/changesets`}
|
path={`${url}/changesets`}
|
||||||
render={() => (
|
render={() => (
|
||||||
@@ -145,7 +146,7 @@ class RepositoryRoot extends React.Component<Props> {
|
|||||||
component={() => (
|
component={() => (
|
||||||
<ScmDiff
|
<ScmDiff
|
||||||
repository={repository}
|
repository={repository}
|
||||||
revision={"2"} // TODO: this is hardcoded only for dev purposes.
|
revision={"a801749dc445d9d71e3fe4c50241433a2adfba6a"} // TODO: this is hardcoded only for dev purposes.
|
||||||
sideBySide={false}
|
sideBySide={false}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,27 +1,86 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import {
|
import {FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX} from "../../modules/types";
|
||||||
FAILURE_SUFFIX,
|
import {apiClient, urls} from "@scm-manager/ui-components";
|
||||||
PENDING_SUFFIX,
|
import {isPending} from "../../modules/pending";
|
||||||
SUCCESS_SUFFIX
|
import {getFailure} from "../../modules/failure";
|
||||||
} from "../../modules/types";
|
import type {Action, Branch, PagedCollection, Repository} from "@scm-manager/ui-types";
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
|
||||||
import { isPending } from "../../modules/pending";
|
|
||||||
import { getFailure } from "../../modules/failure";
|
|
||||||
import type {
|
|
||||||
Action,
|
|
||||||
Branch,
|
|
||||||
PagedCollection,
|
|
||||||
Repository
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
|
|
||||||
export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS";
|
export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS";
|
||||||
export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`;
|
export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`;
|
||||||
export const FETCH_CHANGESETS_SUCCESS = `${FETCH_CHANGESETS}_${SUCCESS_SUFFIX}`;
|
export const FETCH_CHANGESETS_SUCCESS = `${FETCH_CHANGESETS}_${SUCCESS_SUFFIX}`;
|
||||||
export const FETCH_CHANGESETS_FAILURE = `${FETCH_CHANGESETS}_${FAILURE_SUFFIX}`;
|
export const FETCH_CHANGESETS_FAILURE = `${FETCH_CHANGESETS}_${FAILURE_SUFFIX}`;
|
||||||
|
|
||||||
//TODO: Content type
|
export const FETCH_CHANGESET = "scm/repos/FETCH_CHANGESET";
|
||||||
|
export const FETCH_CHANGESET_PENDING = `${FETCH_CHANGESET}_${PENDING_SUFFIX}`;
|
||||||
|
export const FETCH_CHANGESET_SUCCESS = `${FETCH_CHANGESET}_${SUCCESS_SUFFIX}`;
|
||||||
|
export const FETCH_CHANGESET_FAILURE = `${FETCH_CHANGESET}_${FAILURE_SUFFIX}`;
|
||||||
|
|
||||||
// actions
|
// actions
|
||||||
|
//TODO: Content type
|
||||||
|
|
||||||
|
export function fetchChangesetIfNeeded(repository: Repository, id: string) {
|
||||||
|
return (dispatch: any, getState: any) => {
|
||||||
|
if (shouldFetchChangeset(getState(), repository, id)) {
|
||||||
|
return dispatch(fetchChangeset(repository, id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchChangeset(repository: Repository, id: string) {
|
||||||
|
return function(dispatch: any) {
|
||||||
|
dispatch(fetchChangesetPending(repository, id));
|
||||||
|
return apiClient
|
||||||
|
.get(createChangesetUrl(repository, id))
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => dispatch(fetchChangesetSuccess(data, repository, id)))
|
||||||
|
.catch(err => {
|
||||||
|
dispatch(fetchChangesetFailure(repository, id, err));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createChangesetUrl(repository: Repository, id: string) {
|
||||||
|
return urls.concat(repository._links.changesets.href, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchChangesetPending(
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_CHANGESET_PENDING,
|
||||||
|
itemId: createChangesetItemId(repository, id)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetchChangesetSuccess(
|
||||||
|
changeset: any,
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_CHANGESET_SUCCESS,
|
||||||
|
payload: { changeset, repository, id },
|
||||||
|
itemId: createChangesetItemId(repository, id)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchChangesetFailure(
|
||||||
|
repository: Repository,
|
||||||
|
id: string,
|
||||||
|
error: Error
|
||||||
|
): Action {
|
||||||
|
return {
|
||||||
|
type: FETCH_CHANGESET_FAILURE,
|
||||||
|
payload: {
|
||||||
|
repository,
|
||||||
|
id,
|
||||||
|
error
|
||||||
|
},
|
||||||
|
itemId: createChangesetItemId(repository, id)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function fetchChangesets(
|
export function fetchChangesets(
|
||||||
repository: Repository,
|
repository: Repository,
|
||||||
@@ -80,7 +139,11 @@ export function fetchChangesetsSuccess(
|
|||||||
): Action {
|
): Action {
|
||||||
return {
|
return {
|
||||||
type: FETCH_CHANGESETS_SUCCESS,
|
type: FETCH_CHANGESETS_SUCCESS,
|
||||||
payload: changesets,
|
payload: {
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
changesets
|
||||||
|
},
|
||||||
itemId: createItemId(repository, branch)
|
itemId: createItemId(repository, branch)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -101,6 +164,11 @@ function fetchChangesetsFailure(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createChangesetItemId(repository: Repository, id: string) {
|
||||||
|
const { namespace, name } = repository;
|
||||||
|
return namespace + "/" + name + "/" + id;
|
||||||
|
}
|
||||||
|
|
||||||
function createItemId(repository: Repository, branch?: Branch): string {
|
function createItemId(repository: Repository, branch?: Branch): string {
|
||||||
const { namespace, name } = repository;
|
const { namespace, name } = repository;
|
||||||
let itemId = namespace + "/" + name;
|
let itemId = namespace + "/" + name;
|
||||||
@@ -118,10 +186,32 @@ export default function reducer(
|
|||||||
if (!action.payload) {
|
if (!action.payload) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
const payload = action.payload;
|
const payload = action.payload;
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case FETCH_CHANGESET_SUCCESS:
|
||||||
|
const _key = createItemId(payload.repository);
|
||||||
|
|
||||||
|
let _oldByIds = {};
|
||||||
|
if (state[_key] && state[_key].byId) {
|
||||||
|
_oldByIds = state[_key].byId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeset = payload.changeset;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[_key]: {
|
||||||
|
...state[_key],
|
||||||
|
byId: {
|
||||||
|
..._oldByIds,
|
||||||
|
[changeset.id]: changeset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
case FETCH_CHANGESETS_SUCCESS:
|
case FETCH_CHANGESETS_SUCCESS:
|
||||||
const changesets = payload._embedded.changesets;
|
const changesets = payload.changesets._embedded.changesets;
|
||||||
const changesetIds = changesets.map(c => c.id);
|
const changesetIds = changesets.map(c => c.id);
|
||||||
const key = action.itemId;
|
const key = action.itemId;
|
||||||
|
|
||||||
@@ -129,26 +219,32 @@ export default function reducer(
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
let oldByIds = {};
|
const repoId = createItemId(payload.repository);
|
||||||
if (state[key] && state[key].byId) {
|
|
||||||
oldByIds = state[key].byId;
|
let oldState = {};
|
||||||
|
if (state[repoId]) {
|
||||||
|
oldState = state[repoId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const branchName = payload.branch ? payload.branch.name : "";
|
||||||
const byIds = extractChangesetsByIds(changesets);
|
const byIds = extractChangesetsByIds(changesets);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
[key]: {
|
[repoId]: {
|
||||||
byId: {
|
byId: {
|
||||||
...oldByIds,
|
...oldState.byId,
|
||||||
...byIds
|
...byIds
|
||||||
},
|
},
|
||||||
list: {
|
byBranch: {
|
||||||
entries: changesetIds,
|
...oldState.byBranch,
|
||||||
entry: {
|
[branchName]: {
|
||||||
page: payload.page,
|
entries: changesetIds,
|
||||||
pageTotal: payload.pageTotal,
|
entry: {
|
||||||
_links: payload._links
|
page: payload.changesets.page,
|
||||||
|
pageTotal: payload.changesets.pageTotal,
|
||||||
|
_links: payload.changesets._links
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,17 +270,76 @@ export function getChangesets(
|
|||||||
repository: Repository,
|
repository: Repository,
|
||||||
branch?: Branch
|
branch?: Branch
|
||||||
) {
|
) {
|
||||||
const key = createItemId(repository, branch);
|
const repoKey = createItemId(repository);
|
||||||
|
|
||||||
const changesets = state.changesets[key];
|
const stateRoot = state.changesets[repoKey];
|
||||||
|
if (!stateRoot || !stateRoot.byBranch) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const branchName = branch ? branch.name : "";
|
||||||
|
|
||||||
|
const changesets = stateRoot.byBranch[branchName];
|
||||||
if (!changesets) {
|
if (!changesets) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return changesets.list.entries.map((id: string) => {
|
|
||||||
return changesets.byId[id];
|
return changesets.entries.map((id: string) => {
|
||||||
|
return stateRoot.byId[id];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getChangeset(
|
||||||
|
state: Object,
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
const key = createItemId(repository);
|
||||||
|
const changesets =
|
||||||
|
state.changesets && state.changesets[key]
|
||||||
|
? state.changesets[key].byId
|
||||||
|
: null;
|
||||||
|
if (changesets != null && changesets[id]) {
|
||||||
|
return changesets[id];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldFetchChangeset(
|
||||||
|
state: Object,
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
if (getChangeset(state, repository, id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFetchChangesetPending(
|
||||||
|
state: Object,
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
return isPending(
|
||||||
|
state,
|
||||||
|
FETCH_CHANGESET,
|
||||||
|
createChangesetItemId(repository, id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getFetchChangesetFailure(
|
||||||
|
state: Object,
|
||||||
|
repository: Repository,
|
||||||
|
id: string
|
||||||
|
) {
|
||||||
|
return getFailure(
|
||||||
|
state,
|
||||||
|
FETCH_CHANGESET,
|
||||||
|
createChangesetItemId(repository, id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function isFetchChangesetsPending(
|
export function isFetchChangesetsPending(
|
||||||
state: Object,
|
state: Object,
|
||||||
repository: Repository,
|
repository: Repository,
|
||||||
@@ -202,9 +357,15 @@ export function getFetchChangesetsFailure(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selectList = (state: Object, repository: Repository, branch?: Branch) => {
|
const selectList = (state: Object, repository: Repository, branch?: Branch) => {
|
||||||
const itemId = createItemId(repository, branch);
|
const repoId = createItemId(repository);
|
||||||
if (state.changesets[itemId] && state.changesets[itemId].list) {
|
|
||||||
return state.changesets[itemId].list;
|
const branchName = branch ? branch.name : "";
|
||||||
|
if (state.changesets[repoId]) {
|
||||||
|
const repoState = state.changesets[repoId];
|
||||||
|
|
||||||
|
if (repoState.byBranch && repoState.byBranch[branchName]) {
|
||||||
|
return repoState.byBranch[branchName];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,15 +4,27 @@ import configureMockStore from "redux-mock-store";
|
|||||||
import thunk from "redux-thunk";
|
import thunk from "redux-thunk";
|
||||||
import fetchMock from "fetch-mock";
|
import fetchMock from "fetch-mock";
|
||||||
import reducer, {
|
import reducer, {
|
||||||
|
FETCH_CHANGESET,
|
||||||
|
FETCH_CHANGESET_FAILURE,
|
||||||
|
FETCH_CHANGESET_PENDING,
|
||||||
|
FETCH_CHANGESET_SUCCESS,
|
||||||
FETCH_CHANGESETS,
|
FETCH_CHANGESETS,
|
||||||
FETCH_CHANGESETS_FAILURE,
|
FETCH_CHANGESETS_FAILURE,
|
||||||
FETCH_CHANGESETS_PENDING,
|
FETCH_CHANGESETS_PENDING,
|
||||||
FETCH_CHANGESETS_SUCCESS,
|
FETCH_CHANGESETS_SUCCESS,
|
||||||
|
fetchChangeset,
|
||||||
|
fetchChangesetIfNeeded,
|
||||||
fetchChangesets,
|
fetchChangesets,
|
||||||
fetchChangesetsSuccess,
|
fetchChangesetsSuccess,
|
||||||
|
fetchChangesetSuccess,
|
||||||
|
getChangeset,
|
||||||
getChangesets,
|
getChangesets,
|
||||||
|
getFetchChangesetFailure,
|
||||||
getFetchChangesetsFailure,
|
getFetchChangesetsFailure,
|
||||||
isFetchChangesetsPending
|
isFetchChangesetPending,
|
||||||
|
isFetchChangesetsPending,
|
||||||
|
selectListAsCollection,
|
||||||
|
shouldFetchChangeset
|
||||||
} from "./changesets";
|
} from "./changesets";
|
||||||
|
|
||||||
const branch = {
|
const branch = {
|
||||||
@@ -21,7 +33,7 @@ const branch = {
|
|||||||
_links: {
|
_links: {
|
||||||
history: {
|
history: {
|
||||||
href:
|
href:
|
||||||
"http://scm/api/rest/v2/repositories/foo/bar/branches/specific/changesets"
|
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -32,14 +44,14 @@ const repository = {
|
|||||||
type: "GIT",
|
type: "GIT",
|
||||||
_links: {
|
_links: {
|
||||||
self: {
|
self: {
|
||||||
href: "http://scm/api/rest/v2/repositories/foo/bar"
|
href: "http://scm.hitchhicker.com/api/v2/repositories/foo/bar"
|
||||||
},
|
},
|
||||||
changesets: {
|
changesets: {
|
||||||
href: "http://scm/api/rest/v2/repositories/foo/bar/changesets"
|
href: "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/changesets"
|
||||||
},
|
},
|
||||||
branches: {
|
branches: {
|
||||||
href:
|
href:
|
||||||
"http://scm/api/rest/v2/repositories/foo/bar/branches/specific/branches"
|
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/branches"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -49,9 +61,10 @@ const changesets = {};
|
|||||||
describe("changesets", () => {
|
describe("changesets", () => {
|
||||||
describe("fetching of changesets", () => {
|
describe("fetching of changesets", () => {
|
||||||
const DEFAULT_BRANCH_URL =
|
const DEFAULT_BRANCH_URL =
|
||||||
"http://scm/api/rest/v2/repositories/foo/bar/changesets";
|
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/changesets";
|
||||||
const SPECIFIC_BRANCH_URL =
|
const SPECIFIC_BRANCH_URL =
|
||||||
"http://scm/api/rest/v2/repositories/foo/bar/branches/specific/changesets";
|
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets";
|
||||||
|
|
||||||
const mockStore = configureMockStore([thunk]);
|
const mockStore = configureMockStore([thunk]);
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -59,6 +72,102 @@ describe("changesets", () => {
|
|||||||
fetchMock.restore();
|
fetchMock.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const changesetId = "aba876c0625d90a6aff1494f3d161aaa7008b958";
|
||||||
|
|
||||||
|
it("should fetch changeset", () => {
|
||||||
|
fetchMock.getOnce(DEFAULT_BRANCH_URL + "/" + changesetId, "{}");
|
||||||
|
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: FETCH_CHANGESET_PENDING,
|
||||||
|
itemId: "foo/bar/" + changesetId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FETCH_CHANGESET_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
changeset: {},
|
||||||
|
id: changesetId,
|
||||||
|
repository: repository
|
||||||
|
},
|
||||||
|
itemId: "foo/bar/" + changesetId
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
return store
|
||||||
|
.dispatch(fetchChangeset(repository, changesetId))
|
||||||
|
.then(() => {
|
||||||
|
expect(store.getActions()).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail fetching changeset on error", () => {
|
||||||
|
fetchMock.getOnce(DEFAULT_BRANCH_URL + "/" + changesetId, 500);
|
||||||
|
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: FETCH_CHANGESET_PENDING,
|
||||||
|
itemId: "foo/bar/" + changesetId
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
return store
|
||||||
|
.dispatch(fetchChangeset(repository, changesetId))
|
||||||
|
.then(() => {
|
||||||
|
expect(store.getActions()[0]).toEqual(expectedActions[0]);
|
||||||
|
expect(store.getActions()[1].type).toEqual(FETCH_CHANGESET_FAILURE);
|
||||||
|
expect(store.getActions()[1].payload).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fetch changeset if needed", () => {
|
||||||
|
fetchMock.getOnce(DEFAULT_BRANCH_URL + "/id3", "{}");
|
||||||
|
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: FETCH_CHANGESET_PENDING,
|
||||||
|
itemId: "foo/bar/id3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: FETCH_CHANGESET_SUCCESS,
|
||||||
|
payload: {
|
||||||
|
changeset: {},
|
||||||
|
id: "id3",
|
||||||
|
repository: repository
|
||||||
|
},
|
||||||
|
itemId: "foo/bar/id3"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const store = mockStore({});
|
||||||
|
return store
|
||||||
|
.dispatch(fetchChangesetIfNeeded(repository, "id3"))
|
||||||
|
.then(() => {
|
||||||
|
expect(store.getActions()).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not fetch changeset if not needed", () => {
|
||||||
|
fetchMock.getOnce(DEFAULT_BRANCH_URL + "/id1", 500);
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id1: { id: "id1" },
|
||||||
|
id2: { id: "id2" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = mockStore(state);
|
||||||
|
return expect(
|
||||||
|
store.dispatch(fetchChangesetIfNeeded(repository, "id1"))
|
||||||
|
).toEqual(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
it("should fetch changesets for default branch", () => {
|
it("should fetch changesets for default branch", () => {
|
||||||
fetchMock.getOnce(DEFAULT_BRANCH_URL, "{}");
|
fetchMock.getOnce(DEFAULT_BRANCH_URL, "{}");
|
||||||
|
|
||||||
@@ -69,7 +178,11 @@ describe("changesets", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FETCH_CHANGESETS_SUCCESS,
|
type: FETCH_CHANGESETS_SUCCESS,
|
||||||
payload: changesets,
|
payload: {
|
||||||
|
repository,
|
||||||
|
undefined,
|
||||||
|
changesets
|
||||||
|
},
|
||||||
itemId: "foo/bar"
|
itemId: "foo/bar"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -91,7 +204,11 @@ describe("changesets", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FETCH_CHANGESETS_SUCCESS,
|
type: FETCH_CHANGESETS_SUCCESS,
|
||||||
payload: changesets,
|
payload: {
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
changesets
|
||||||
|
},
|
||||||
itemId
|
itemId
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -150,7 +267,11 @@ describe("changesets", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FETCH_CHANGESETS_SUCCESS,
|
type: FETCH_CHANGESETS_SUCCESS,
|
||||||
payload: changesets,
|
payload: {
|
||||||
|
repository,
|
||||||
|
undefined,
|
||||||
|
changesets
|
||||||
|
},
|
||||||
itemId: "foo/bar"
|
itemId: "foo/bar"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -173,7 +294,11 @@ describe("changesets", () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: FETCH_CHANGESETS_SUCCESS,
|
type: FETCH_CHANGESETS_SUCCESS,
|
||||||
payload: changesets,
|
payload: {
|
||||||
|
repository,
|
||||||
|
branch,
|
||||||
|
changesets
|
||||||
|
},
|
||||||
itemId: "foo/bar/specific"
|
itemId: "foo/bar/specific"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@@ -215,7 +340,7 @@ describe("changesets", () => {
|
|||||||
);
|
);
|
||||||
expect(newState["foo/bar"].byId["changeset2"].description).toEqual("foo");
|
expect(newState["foo/bar"].byId["changeset2"].description).toEqual("foo");
|
||||||
expect(newState["foo/bar"].byId["changeset3"].description).toEqual("bar");
|
expect(newState["foo/bar"].byId["changeset3"].description).toEqual("bar");
|
||||||
expect(newState["foo/bar"].list).toEqual({
|
expect(newState["foo/bar"].byBranch[""]).toEqual({
|
||||||
entry: {
|
entry: {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageTotal: 10,
|
pageTotal: 10,
|
||||||
@@ -225,6 +350,20 @@ describe("changesets", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should store the changeset list to branch", () => {
|
||||||
|
const newState = reducer(
|
||||||
|
{},
|
||||||
|
fetchChangesetsSuccess(repository, branch, responseBody)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(newState["foo/bar"].byId["changeset1"]).toBeDefined();
|
||||||
|
expect(newState["foo/bar"].byBranch["specific"].entries).toEqual([
|
||||||
|
"changeset1",
|
||||||
|
"changeset2",
|
||||||
|
"changeset3"
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it("should not remove existing changesets", () => {
|
it("should not remove existing changesets", () => {
|
||||||
const state = {
|
const state = {
|
||||||
"foo/bar": {
|
"foo/bar": {
|
||||||
@@ -232,8 +371,10 @@ describe("changesets", () => {
|
|||||||
id2: { id: "id2" },
|
id2: { id: "id2" },
|
||||||
id1: { id: "id1" }
|
id1: { id: "id1" }
|
||||||
},
|
},
|
||||||
list: {
|
byBranch: {
|
||||||
entries: ["id1", "id2"]
|
"": {
|
||||||
|
entries: ["id1", "id2"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -245,7 +386,7 @@ describe("changesets", () => {
|
|||||||
|
|
||||||
const fooBar = newState["foo/bar"];
|
const fooBar = newState["foo/bar"];
|
||||||
|
|
||||||
expect(fooBar.list.entries).toEqual([
|
expect(fooBar.byBranch[""].entries).toEqual([
|
||||||
"changeset1",
|
"changeset1",
|
||||||
"changeset2",
|
"changeset2",
|
||||||
"changeset3"
|
"changeset3"
|
||||||
@@ -253,11 +394,154 @@ describe("changesets", () => {
|
|||||||
expect(fooBar.byId["id2"]).toEqual({ id: "id2" });
|
expect(fooBar.byId["id2"]).toEqual({ id: "id2" });
|
||||||
expect(fooBar.byId["id1"]).toEqual({ id: "id1" });
|
expect(fooBar.byId["id1"]).toEqual({ id: "id1" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const responseBodySingleChangeset = {
|
||||||
|
id: "id3",
|
||||||
|
author: {
|
||||||
|
mail: "z@phod.com",
|
||||||
|
name: "zaphod"
|
||||||
|
},
|
||||||
|
date: "2018-09-13T08:46:22Z",
|
||||||
|
description: "added testChangeset",
|
||||||
|
_links: {},
|
||||||
|
_embedded: {
|
||||||
|
tags: [],
|
||||||
|
branches: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
it("should add changeset to state", () => {
|
||||||
|
const newState = reducer(
|
||||||
|
{
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
"id2": {
|
||||||
|
id: "id2",
|
||||||
|
author: { mail: "mail@author.com", name: "author" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
entry: {
|
||||||
|
page: 1,
|
||||||
|
pageTotal: 10,
|
||||||
|
_links: {}
|
||||||
|
},
|
||||||
|
entries: ["id2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fetchChangesetSuccess(responseBodySingleChangeset, repository, "id3")
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(newState).toBeDefined();
|
||||||
|
expect(newState["foo/bar"].byId["id3"].description).toEqual(
|
||||||
|
"added testChangeset"
|
||||||
|
);
|
||||||
|
expect(newState["foo/bar"].byId["id3"].author.mail).toEqual("z@phod.com");
|
||||||
|
expect(newState["foo/bar"].byId["id2"]).toBeDefined();
|
||||||
|
expect(newState["foo/bar"].byId["id3"]).toBeDefined();
|
||||||
|
expect(newState["foo/bar"].list).toEqual({
|
||||||
|
entry: {
|
||||||
|
page: 1,
|
||||||
|
pageTotal: 10,
|
||||||
|
_links: {}
|
||||||
|
},
|
||||||
|
entries: ["id2"]
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("changeset selectors", () => {
|
describe("changeset selectors", () => {
|
||||||
const error = new Error("Something went wrong");
|
const error = new Error("Something went wrong");
|
||||||
|
|
||||||
|
it("should return changeset", () => {
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id1: { id: "id1" },
|
||||||
|
id2: { id: "id2" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = getChangeset(state, repository, "id1");
|
||||||
|
expect(result).toEqual({ id: "id1" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return null if changeset does not exist", () => {
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id1: { id: "id1" },
|
||||||
|
id2: { id: "id2" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = getChangeset(state, repository, "id3");
|
||||||
|
expect(result).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true if changeset does not exist", () => {
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id1: { id: "id1" },
|
||||||
|
id2: { id: "id2" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = shouldFetchChangeset(state, repository, "id3");
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if changeset exists", () => {
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id1: { id: "id1" },
|
||||||
|
id2: { id: "id2" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const result = shouldFetchChangeset(state, repository, "id2");
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true, when fetching changeset is pending", () => {
|
||||||
|
const state = {
|
||||||
|
pending: {
|
||||||
|
[FETCH_CHANGESET + "/foo/bar/id1"]: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(isFetchChangesetPending(state, repository, "id1")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false, when fetching changeset is not pending", () => {
|
||||||
|
expect(isFetchChangesetPending({}, repository, "id1")).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return error if fetching changeset failed", () => {
|
||||||
|
const state = {
|
||||||
|
failure: {
|
||||||
|
[FETCH_CHANGESET + "/foo/bar/id1"]: error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(getFetchChangesetFailure(state, repository, "id1")).toEqual(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if fetching changeset did not fail", () => {
|
||||||
|
expect(getFetchChangesetFailure({}, repository, "id1")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("should get all changesets for a given repository", () => {
|
it("should get all changesets for a given repository", () => {
|
||||||
const state = {
|
const state = {
|
||||||
changesets: {
|
changesets: {
|
||||||
@@ -266,8 +550,10 @@ describe("changesets", () => {
|
|||||||
id2: { id: "id2" },
|
id2: { id: "id2" },
|
||||||
id1: { id: "id1" }
|
id1: { id: "id1" }
|
||||||
},
|
},
|
||||||
list: {
|
byBranch: {
|
||||||
entries: ["id1", "id2"]
|
"": {
|
||||||
|
entries: ["id1", "id2"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,5 +589,32 @@ describe("changesets", () => {
|
|||||||
it("should return false if fetching changesets did not fail", () => {
|
it("should return false if fetching changesets did not fail", () => {
|
||||||
expect(getFetchChangesetsFailure({}, repository)).toBeUndefined();
|
expect(getFetchChangesetsFailure({}, repository)).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should return list as collection for the default branch", () => {
|
||||||
|
const state = {
|
||||||
|
changesets: {
|
||||||
|
"foo/bar": {
|
||||||
|
byId: {
|
||||||
|
id2: { id: "id2" },
|
||||||
|
id1: { id: "id1" }
|
||||||
|
},
|
||||||
|
byBranch: {
|
||||||
|
"": {
|
||||||
|
entry: {
|
||||||
|
page: 1,
|
||||||
|
pageTotal: 10,
|
||||||
|
_links: {}
|
||||||
|
},
|
||||||
|
entries: ["id1", "id2"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const collection = selectListAsCollection(state, repository);
|
||||||
|
expect(collection.page).toBe(1);
|
||||||
|
expect(collection.pageTotal).toBe(10);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { translate } from "react-i18next";
|
import {translate} from "react-i18next";
|
||||||
import { Checkbox, InputField, SubmitButton } from "@scm-manager/ui-components";
|
import {Checkbox, InputField, SubmitButton} from "@scm-manager/ui-components";
|
||||||
import TypeSelector from "./TypeSelector";
|
import TypeSelector from "./TypeSelector";
|
||||||
import type {
|
import type {PermissionCollection, PermissionCreateEntry} from "@scm-manager/ui-types";
|
||||||
PermissionCollection,
|
|
||||||
PermissionEntry
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
import * as validator from "./permissionValidation";
|
import * as validator from "./permissionValidation";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
t: string => string,
|
t: string => string,
|
||||||
createPermission: (permission: PermissionEntry) => void,
|
createPermission: (permission: PermissionCreateEntry) => void,
|
||||||
loading: boolean,
|
loading: boolean,
|
||||||
currentPermissions: PermissionCollection
|
currentPermissions: PermissionCollection
|
||||||
};
|
};
|
||||||
@@ -65,7 +62,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
|||||||
<SubmitButton
|
<SubmitButton
|
||||||
label={t("permission.add-permission.submit-button")}
|
label={t("permission.add-permission.submit-button")}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
disabled={!this.state.valid || this.state.name == ""}
|
disabled={!this.state.valid || this.state.name === ""}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,21 +1,31 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { validation } from "@scm-manager/ui-components";
|
import {validation} from "@scm-manager/ui-components";
|
||||||
import type {
|
import type {PermissionCollection} from "@scm-manager/ui-types";
|
||||||
PermissionCollection,
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
const isNameValid = validation.isNameValid;
|
const isNameValid = validation.isNameValid;
|
||||||
|
|
||||||
export { isNameValid };
|
export { isNameValid };
|
||||||
|
|
||||||
export const isPermissionValid = (name: string, groupPermission: boolean, permissions: PermissionCollection) => {
|
export const isPermissionValid = (
|
||||||
return isNameValid(name) && !currentPermissionIncludeName(name, groupPermission, permissions);
|
name: string,
|
||||||
|
groupPermission: boolean,
|
||||||
|
permissions: PermissionCollection
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
isNameValid(name) &&
|
||||||
|
!currentPermissionIncludeName(name, groupPermission, permissions)
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const currentPermissionIncludeName = (name: string, groupPermission: boolean, permissions: PermissionCollection) => {
|
const currentPermissionIncludeName = (
|
||||||
|
name: string,
|
||||||
|
groupPermission: boolean,
|
||||||
|
permissions: PermissionCollection
|
||||||
|
) => {
|
||||||
for (let i = 0; i < permissions.length; i++) {
|
for (let i = 0; i < permissions.length; i++) {
|
||||||
if (
|
if (
|
||||||
permissions[i].name === name &&
|
permissions[i].name === name &&
|
||||||
permissions[i].groupPermission == groupPermission
|
permissions[i].groupPermission === groupPermission
|
||||||
)
|
)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ describe("permission validation", () => {
|
|||||||
{
|
{
|
||||||
name: "PermissionName",
|
name: "PermissionName",
|
||||||
groupPermission: true,
|
groupPermission: true,
|
||||||
type: "READ"
|
type: "READ",
|
||||||
|
_links: {}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const name = "PermissionName";
|
const name = "PermissionName";
|
||||||
@@ -33,7 +34,8 @@ describe("permission validation", () => {
|
|||||||
{
|
{
|
||||||
name: "PermissionName",
|
name: "PermissionName",
|
||||||
groupPermission: false,
|
groupPermission: false,
|
||||||
type: "READ"
|
type: "READ",
|
||||||
|
_links: {}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const name = "PermissionName";
|
const name = "PermissionName";
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import {connect} from "react-redux";
|
||||||
import { translate } from "react-i18next";
|
import {translate} from "react-i18next";
|
||||||
import {
|
import {
|
||||||
|
createPermission,
|
||||||
|
createPermissionReset,
|
||||||
|
deletePermissionReset,
|
||||||
fetchPermissions,
|
fetchPermissions,
|
||||||
|
getCreatePermissionFailure,
|
||||||
|
getDeletePermissionsFailure,
|
||||||
getFetchPermissionsFailure,
|
getFetchPermissionsFailure,
|
||||||
isFetchPermissionsPending,
|
getModifyPermissionsFailure,
|
||||||
getPermissionsOfRepo,
|
getPermissionsOfRepo,
|
||||||
hasCreatePermission,
|
hasCreatePermission,
|
||||||
createPermission,
|
|
||||||
isCreatePermissionPending,
|
isCreatePermissionPending,
|
||||||
getCreatePermissionFailure,
|
isFetchPermissionsPending,
|
||||||
createPermissionReset,
|
modifyPermissionReset
|
||||||
getDeletePermissionsFailure,
|
|
||||||
getModifyPermissionsFailure,
|
|
||||||
modifyPermissionReset,
|
|
||||||
deletePermissionReset
|
|
||||||
} from "../modules/permissions";
|
} from "../modules/permissions";
|
||||||
import { Loading, ErrorPage } from "@scm-manager/ui-components";
|
import {ErrorPage, Loading} from "@scm-manager/ui-components";
|
||||||
import type {
|
import type {Permission, PermissionCollection, PermissionCreateEntry} from "@scm-manager/ui-types";
|
||||||
Permission,
|
|
||||||
PermissionCollection,
|
|
||||||
PermissionEntry
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
import SinglePermission from "./SinglePermission";
|
import SinglePermission from "./SinglePermission";
|
||||||
import CreatePermissionForm from "../components/CreatePermissionForm";
|
import CreatePermissionForm from "../components/CreatePermissionForm";
|
||||||
import type { History } from "history";
|
import type {History} from "history";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
namespace: string,
|
namespace: string,
|
||||||
@@ -39,7 +35,7 @@ type Props = {
|
|||||||
//dispatch functions
|
//dispatch functions
|
||||||
fetchPermissions: (namespace: string, repoName: string) => void,
|
fetchPermissions: (namespace: string, repoName: string) => void,
|
||||||
createPermission: (
|
createPermission: (
|
||||||
permission: PermissionEntry,
|
permission: PermissionCreateEntry,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
@@ -176,7 +172,7 @@ const mapDispatchToProps = dispatch => {
|
|||||||
dispatch(fetchPermissions(namespace, repoName));
|
dispatch(fetchPermissions(namespace, repoName));
|
||||||
},
|
},
|
||||||
createPermission: (
|
createPermission: (
|
||||||
permission: PermissionEntry,
|
permission: PermissionCreateEntry,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
import type {Action} from "@scm-manager/ui-components";
|
||||||
|
import {apiClient} from "@scm-manager/ui-components";
|
||||||
import * as types from "../../../modules/types";
|
import * as types from "../../../modules/types";
|
||||||
import type { Action } from "@scm-manager/ui-components";
|
import type {Permission, PermissionCollection, PermissionCreateEntry} from "@scm-manager/ui-types";
|
||||||
import type {
|
import {isPending} from "../../../modules/pending";
|
||||||
PermissionCollection,
|
import {getFailure} from "../../../modules/failure";
|
||||||
Permission,
|
import {Dispatch} from "redux";
|
||||||
PermissionEntry
|
|
||||||
} from "@scm-manager/ui-types";
|
|
||||||
import { isPending } from "../../../modules/pending";
|
|
||||||
import { getFailure } from "../../../modules/failure";
|
|
||||||
import { Dispatch } from "redux";
|
|
||||||
|
|
||||||
export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS";
|
export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS";
|
||||||
export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${
|
export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${
|
||||||
@@ -219,7 +215,7 @@ export function modifyPermissionReset(namespace: string, repoName: string) {
|
|||||||
|
|
||||||
// create permission
|
// create permission
|
||||||
export function createPermission(
|
export function createPermission(
|
||||||
permission: PermissionEntry,
|
permission: PermissionCreateEntry,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
repoName: string,
|
repoName: string,
|
||||||
callback?: () => void
|
callback?: () => void
|
||||||
@@ -260,7 +256,7 @@ export function createPermission(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPermissionPending(
|
export function createPermissionPending(
|
||||||
permission: PermissionEntry,
|
permission: PermissionCreateEntry,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
repoName: string
|
repoName: string
|
||||||
): Action {
|
): Action {
|
||||||
@@ -272,7 +268,7 @@ export function createPermissionPending(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createPermissionSuccess(
|
export function createPermissionSuccess(
|
||||||
permission: PermissionEntry,
|
permission: PermissionCreateEntry,
|
||||||
namespace: string,
|
namespace: string,
|
||||||
repoName: string
|
repoName: string
|
||||||
): Action {
|
): Action {
|
||||||
|
|||||||
@@ -3,44 +3,44 @@ import configureMockStore from "redux-mock-store";
|
|||||||
import thunk from "redux-thunk";
|
import thunk from "redux-thunk";
|
||||||
import fetchMock from "fetch-mock";
|
import fetchMock from "fetch-mock";
|
||||||
import reducer, {
|
import reducer, {
|
||||||
fetchPermissions,
|
CREATE_PERMISSION,
|
||||||
fetchPermissionsSuccess,
|
CREATE_PERMISSION_FAILURE,
|
||||||
getPermissionsOfRepo,
|
|
||||||
isFetchPermissionsPending,
|
|
||||||
getFetchPermissionsFailure,
|
|
||||||
modifyPermission,
|
|
||||||
modifyPermissionSuccess,
|
|
||||||
getModifyPermissionFailure,
|
|
||||||
isModifyPermissionPending,
|
|
||||||
createPermission,
|
|
||||||
hasCreatePermission,
|
|
||||||
deletePermission,
|
|
||||||
deletePermissionSuccess,
|
|
||||||
getDeletePermissionFailure,
|
|
||||||
isDeletePermissionPending,
|
|
||||||
getModifyPermissionsFailure,
|
|
||||||
MODIFY_PERMISSION_FAILURE,
|
|
||||||
MODIFY_PERMISSION_PENDING,
|
|
||||||
FETCH_PERMISSIONS,
|
|
||||||
FETCH_PERMISSIONS_PENDING,
|
|
||||||
FETCH_PERMISSIONS_SUCCESS,
|
|
||||||
FETCH_PERMISSIONS_FAILURE,
|
|
||||||
MODIFY_PERMISSION_SUCCESS,
|
|
||||||
MODIFY_PERMISSION,
|
|
||||||
CREATE_PERMISSION_PENDING,
|
CREATE_PERMISSION_PENDING,
|
||||||
CREATE_PERMISSION_SUCCESS,
|
CREATE_PERMISSION_SUCCESS,
|
||||||
CREATE_PERMISSION_FAILURE,
|
createPermission,
|
||||||
|
createPermissionSuccess,
|
||||||
DELETE_PERMISSION,
|
DELETE_PERMISSION,
|
||||||
|
DELETE_PERMISSION_FAILURE,
|
||||||
DELETE_PERMISSION_PENDING,
|
DELETE_PERMISSION_PENDING,
|
||||||
DELETE_PERMISSION_SUCCESS,
|
DELETE_PERMISSION_SUCCESS,
|
||||||
DELETE_PERMISSION_FAILURE,
|
deletePermission,
|
||||||
CREATE_PERMISSION,
|
deletePermissionSuccess,
|
||||||
createPermissionSuccess,
|
FETCH_PERMISSIONS,
|
||||||
|
FETCH_PERMISSIONS_FAILURE,
|
||||||
|
FETCH_PERMISSIONS_PENDING,
|
||||||
|
FETCH_PERMISSIONS_SUCCESS,
|
||||||
|
fetchPermissions,
|
||||||
|
fetchPermissionsSuccess,
|
||||||
getCreatePermissionFailure,
|
getCreatePermissionFailure,
|
||||||
|
getDeletePermissionFailure,
|
||||||
|
getDeletePermissionsFailure,
|
||||||
|
getFetchPermissionsFailure,
|
||||||
|
getModifyPermissionFailure,
|
||||||
|
getModifyPermissionsFailure,
|
||||||
|
getPermissionsOfRepo,
|
||||||
|
hasCreatePermission,
|
||||||
isCreatePermissionPending,
|
isCreatePermissionPending,
|
||||||
getDeletePermissionsFailure
|
isDeletePermissionPending,
|
||||||
|
isFetchPermissionsPending,
|
||||||
|
isModifyPermissionPending,
|
||||||
|
MODIFY_PERMISSION,
|
||||||
|
MODIFY_PERMISSION_FAILURE,
|
||||||
|
MODIFY_PERMISSION_PENDING,
|
||||||
|
MODIFY_PERMISSION_SUCCESS,
|
||||||
|
modifyPermission,
|
||||||
|
modifyPermissionSuccess
|
||||||
} from "./permissions";
|
} from "./permissions";
|
||||||
import type { Permission, PermissionCollection } from "@scm-manager/ui-types";
|
import type {Permission, PermissionCollection} from "@scm-manager/ui-types";
|
||||||
|
|
||||||
const hitchhiker_puzzle42Permission_user_eins: Permission = {
|
const hitchhiker_puzzle42Permission_user_eins: Permission = {
|
||||||
name: "user_eins",
|
name: "user_eins",
|
||||||
@@ -640,7 +640,7 @@ describe("permissions selectors", () => {
|
|||||||
it("should return true, when createPermission is true", () => {
|
it("should return true, when createPermission is true", () => {
|
||||||
const state = {
|
const state = {
|
||||||
permissions: {
|
permissions: {
|
||||||
["hitchhiker/puzzle42"]: {
|
"hitchhiker/puzzle42": {
|
||||||
createPermission: true
|
createPermission: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -651,7 +651,7 @@ describe("permissions selectors", () => {
|
|||||||
it("should return false, when createPermission is false", () => {
|
it("should return false, when createPermission is false", () => {
|
||||||
const state = {
|
const state = {
|
||||||
permissions: {
|
permissions: {
|
||||||
["hitchhiker/puzzle42"]: {
|
"hitchhiker/puzzle42": {
|
||||||
createPermission: false
|
createPermission: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
|||||||
import sonia.scm.NotFoundException;
|
import sonia.scm.NotFoundException;
|
||||||
import sonia.scm.repository.NamespaceAndName;
|
import sonia.scm.repository.NamespaceAndName;
|
||||||
import sonia.scm.repository.RevisionNotFoundException;
|
import sonia.scm.repository.RevisionNotFoundException;
|
||||||
import sonia.scm.repository.api.DiffFormat;
|
|
||||||
import sonia.scm.repository.api.RepositoryService;
|
import sonia.scm.repository.api.RepositoryService;
|
||||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||||
import sonia.scm.util.HttpUtil;
|
import sonia.scm.util.HttpUtil;
|
||||||
@@ -58,7 +57,7 @@ public class DiffRootResource {
|
|||||||
try {
|
try {
|
||||||
repositoryService.getDiffCommand()
|
repositoryService.getDiffCommand()
|
||||||
.setRevision(revision)
|
.setRevision(revision)
|
||||||
.setFormat(DiffFormat.GIT) // TODO: Configure this at request time. Maybe as a query param?
|
// .setFormat(DiffFormat.GIT) // TODO: Configure this at request time. Maybe as a query param?
|
||||||
.retriveContent(output);
|
.retriveContent(output);
|
||||||
} catch (RevisionNotFoundException e) {
|
} catch (RevisionNotFoundException e) {
|
||||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||||
|
|||||||
Reference in New Issue
Block a user