mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 07:55:47 +01:00
merge 2.0.0-m3
This commit is contained in:
@@ -1,45 +0,0 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { ButtonAddons, Button } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branch: Branch,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class BranchButtonGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { repository, branch, t } = this.props;
|
||||
|
||||
const changesetLink = `/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/branch/${encodeURIComponent(branch.name)}/changesets/`;
|
||||
const sourcesLink = `/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/sources/${encodeURIComponent(branch.name)}/`;
|
||||
|
||||
return (
|
||||
<ButtonAddons>
|
||||
<Button
|
||||
link={changesetLink}
|
||||
icon="exchange-alt"
|
||||
label={t("branch.commits")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
<Button
|
||||
link={sourcesLink}
|
||||
icon="code"
|
||||
label={t("branch.sources")}
|
||||
reducedMobile={true}
|
||||
/>
|
||||
</ButtonAddons>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(BranchButtonGroup);
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { ButtonAddons, Button } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
branch: Branch;
|
||||
};
|
||||
|
||||
class BranchButtonGroup extends React.Component<Props> {
|
||||
render() {
|
||||
const { repository, branch, t } = this.props;
|
||||
|
||||
const changesetLink = `/repo/${repository.namespace}/${repository.name}/branch/${encodeURIComponent(
|
||||
branch.name
|
||||
)}/changesets/`;
|
||||
const sourcesLink = `/repo/${repository.namespace}/${repository.name}/sources/${encodeURIComponent(branch.name)}/`;
|
||||
|
||||
return (
|
||||
<ButtonAddons>
|
||||
<Button link={changesetLink} icon="exchange-alt" label={t("branch.commits")} reducedMobile={true} />
|
||||
<Button link={sourcesLink} icon="code" label={t("branch.sources")} reducedMobile={true} />
|
||||
</ButtonAddons>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("repos")(BranchButtonGroup);
|
||||
@@ -1,15 +1,12 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import BranchButtonGroup from "./BranchButtonGroup";
|
||||
import DefaultBranchTag from "./DefaultBranchTag";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branch: Branch,
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
branch: Branch;
|
||||
};
|
||||
|
||||
class BranchDetail extends React.Component<Props> {
|
||||
@@ -19,8 +16,7 @@ class BranchDetail extends React.Component<Props> {
|
||||
return (
|
||||
<div className="media">
|
||||
<div className="media-content subtitle">
|
||||
<strong>{t("branch.name")}</strong> {branch.name}{" "}
|
||||
<DefaultBranchTag defaultBranch={branch.defaultBranch} />
|
||||
<strong>{t("branch.name")}</strong> {branch.name} <DefaultBranchTag defaultBranch={branch.defaultBranch} />
|
||||
</div>
|
||||
<div className="media-right">
|
||||
<BranchButtonGroup repository={repository} branch={branch} />
|
||||
@@ -30,4 +26,4 @@ class BranchDetail extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(BranchDetail);
|
||||
export default withTranslation("repos")(BranchDetail);
|
||||
@@ -1,29 +1,22 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Select,
|
||||
InputField,
|
||||
SubmitButton,
|
||||
validation as validator
|
||||
} from "@scm-manager/ui-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import { Select, InputField, SubmitButton, validation as validator } from "@scm-manager/ui-components";
|
||||
import { orderBranches } from "../util/orderBranches";
|
||||
|
||||
type Props = {
|
||||
submitForm: BranchRequest => void,
|
||||
repository: Repository,
|
||||
branches: Branch[],
|
||||
loading?: boolean,
|
||||
transmittedName?: string,
|
||||
disabled?: boolean,
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
submitForm: (p: BranchRequest) => void;
|
||||
repository: Repository;
|
||||
branches: Branch[];
|
||||
loading?: boolean;
|
||||
transmittedName?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
source?: string,
|
||||
name?: string,
|
||||
nameValidationError: boolean
|
||||
source?: string;
|
||||
name?: string;
|
||||
nameValidationError: boolean;
|
||||
};
|
||||
|
||||
class BranchForm extends React.Component<Props, State> {
|
||||
@@ -42,11 +35,7 @@ class BranchForm extends React.Component<Props, State> {
|
||||
|
||||
isValid = () => {
|
||||
const { source, name } = this.state;
|
||||
return !(
|
||||
this.state.nameValidationError ||
|
||||
this.isFalsy(source) ||
|
||||
this.isFalsy(name)
|
||||
);
|
||||
return !(this.state.nameValidationError || this.isFalsy(source) || this.isFalsy(name));
|
||||
};
|
||||
|
||||
submit = (event: Event) => {
|
||||
@@ -122,4 +111,4 @@ class BranchForm extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(BranchForm);
|
||||
export default withTranslation("repos")(BranchForm);
|
||||
@@ -1,12 +1,11 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import type { Branch } from "@scm-manager/ui-types";
|
||||
import { Branch } from "@scm-manager/ui-types";
|
||||
import DefaultBranchTag from "./DefaultBranchTag";
|
||||
|
||||
type Props = {
|
||||
baseUrl: string,
|
||||
branch: Branch
|
||||
baseUrl: string;
|
||||
branch: Branch;
|
||||
};
|
||||
|
||||
class BranchRow extends React.Component<Props> {
|
||||
@@ -1,13 +1,11 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import BranchRow from "./BranchRow";
|
||||
import type { Branch } from "@scm-manager/ui-types";
|
||||
import { Branch } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
baseUrl: string,
|
||||
t: string => string,
|
||||
branches: Branch[]
|
||||
type Props = WithTranslation & {
|
||||
baseUrl: string;
|
||||
branches: Branch[];
|
||||
};
|
||||
|
||||
class BranchTable extends React.Component<Props> {
|
||||
@@ -37,4 +35,4 @@ class BranchTable extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(BranchTable);
|
||||
export default withTranslation("repos")(BranchTable);
|
||||
@@ -1,12 +1,11 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import BranchDetail from "./BranchDetail";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { Repository, Branch } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branch: Branch
|
||||
repository: Repository;
|
||||
branch: Branch;
|
||||
};
|
||||
|
||||
class BranchView extends React.Component<Props> {
|
||||
@@ -21,7 +20,10 @@ class BranchView extends React.Component<Props> {
|
||||
<ExtensionPoint
|
||||
name="repos.branch-details.information"
|
||||
renderAll={true}
|
||||
props={{ repository, branch }}
|
||||
props={{
|
||||
repository,
|
||||
branch
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,14 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { Tag } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
defaultBranch?: boolean,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
defaultBranch?: boolean;
|
||||
};
|
||||
|
||||
const LeftMarginTag = styled(Tag)`
|
||||
@@ -27,4 +23,4 @@ class DefaultBranchTag extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(DefaultBranchTag);
|
||||
export default withTranslation("repos")(DefaultBranchTag);
|
||||
@@ -1,34 +1,28 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import BranchView from "../components/BranchView";
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch, withRouter } from "react-router-dom";
|
||||
import type { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchBranch,
|
||||
getBranch,
|
||||
getFetchBranchFailure,
|
||||
isFetchBranchPending
|
||||
} from "../modules/branches";
|
||||
import { Repository, Branch } from "@scm-manager/ui-types";
|
||||
import { fetchBranch, getBranch, getFetchBranchFailure, isFetchBranchPending } from "../modules/branches";
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import type { History } from "history";
|
||||
import { History } from "history";
|
||||
import { NotFoundError } from "@scm-manager/ui-components";
|
||||
import queryString from "query-string";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branchName: string,
|
||||
branch: Branch,
|
||||
loading: boolean,
|
||||
error?: Error,
|
||||
repository: Repository;
|
||||
branchName: string;
|
||||
branch: Branch;
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
|
||||
// context props
|
||||
history: History,
|
||||
match: any,
|
||||
location: any,
|
||||
history: History;
|
||||
match: any;
|
||||
location: any;
|
||||
|
||||
// dispatch functions
|
||||
fetchBranch: (repository: Repository, branchName: string) => void
|
||||
fetchBranch: (repository: Repository, branchName: string) => void;
|
||||
};
|
||||
|
||||
class BranchRoot extends React.Component<Props> {
|
||||
@@ -55,15 +49,10 @@ class BranchRoot extends React.Component<Props> {
|
||||
const url = this.matchedUrl();
|
||||
|
||||
if (error) {
|
||||
if (
|
||||
error instanceof NotFoundError &&
|
||||
queryString.parse(location.search).create === "true"
|
||||
) {
|
||||
if (error instanceof NotFoundError && queryString.parse(location.search).create === "true") {
|
||||
return (
|
||||
<Redirect
|
||||
to={`/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/branches/create?name=${match.params.branch}`}
|
||||
to={`/repo/${repository.namespace}/${repository.name}/branches/create?name=${match.params.branch}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -78,12 +67,7 @@ class BranchRoot extends React.Component<Props> {
|
||||
return (
|
||||
<Switch>
|
||||
<Redirect exact from={url} to={`${url}/info`} />
|
||||
<Route
|
||||
path={`${url}/info`}
|
||||
component={() => (
|
||||
<BranchView repository={repository} branch={branch} />
|
||||
)}
|
||||
/>
|
||||
<Route path={`${url}/info`} component={() => <BranchView repository={repository} branch={branch} />} />
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { compose } from "redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { CreateButton, ErrorNotification, Loading, Notification, Subtitle } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
@@ -8,35 +13,22 @@ import {
|
||||
isPermittedToCreateBranches
|
||||
} from "../modules/branches";
|
||||
import { orderBranches } from "../util/orderBranches";
|
||||
import { connect } from "react-redux";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { compose } from "redux";
|
||||
import { translate } from "react-i18next";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import {
|
||||
CreateButton,
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
Notification,
|
||||
Subtitle
|
||||
} from "@scm-manager/ui-components";
|
||||
import BranchTable from "../components/BranchTable";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
baseUrl: string,
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
branches: Branch[],
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
baseUrl: string;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
branches: Branch[];
|
||||
|
||||
// dispatch props
|
||||
showCreateButton: boolean,
|
||||
fetchBranches: Repository => void,
|
||||
showCreateButton: boolean;
|
||||
fetchBranches: (p: Repository) => void;
|
||||
|
||||
// Context props
|
||||
history: any,
|
||||
match: any,
|
||||
t: string => string
|
||||
history: any;
|
||||
match: any;
|
||||
};
|
||||
|
||||
class BranchesOverview extends React.Component<Props> {
|
||||
@@ -77,12 +69,7 @@ class BranchesOverview extends React.Component<Props> {
|
||||
renderCreateButton() {
|
||||
const { showCreateButton, t } = this.props;
|
||||
if (showCreateButton) {
|
||||
return (
|
||||
<CreateButton
|
||||
label={t("branches.overview.createButton")}
|
||||
link="./create"
|
||||
/>
|
||||
);
|
||||
return <CreateButton label={t("branches.overview.createButton")} link="./create" />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -113,7 +100,7 @@ const mapDispatchToProps = dispatch => {
|
||||
};
|
||||
|
||||
export default compose(
|
||||
translate("repos"),
|
||||
withTranslation("repos"),
|
||||
withRouter,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
@@ -1,13 +1,12 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
Subtitle
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import queryString from "query-string";
|
||||
import { History } from "history";
|
||||
import { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading, Subtitle } from "@scm-manager/ui-components";
|
||||
import BranchForm from "../components/BranchForm";
|
||||
import type { Repository, Branch, BranchRequest } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
@@ -19,33 +18,28 @@ import {
|
||||
isFetchBranchesPending,
|
||||
getFetchBranchesFailure
|
||||
} from "../modules/branches";
|
||||
import type { History } from "history";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import queryString from "query-string";
|
||||
|
||||
type Props = {
|
||||
loading?: boolean,
|
||||
error?: Error,
|
||||
repository: Repository,
|
||||
branches: Branch[],
|
||||
createBranchesLink: string,
|
||||
isPermittedToCreateBranches: boolean,
|
||||
type Props = WithTranslation & {
|
||||
loading?: boolean;
|
||||
error?: Error;
|
||||
repository: Repository;
|
||||
branches: Branch[];
|
||||
createBranchesLink: string;
|
||||
isPermittedToCreateBranches: boolean;
|
||||
|
||||
// dispatcher functions
|
||||
fetchBranches: Repository => void,
|
||||
fetchBranches: (p: Repository) => void;
|
||||
createBranch: (
|
||||
createLink: string,
|
||||
repository: Repository,
|
||||
branch: BranchRequest,
|
||||
callback?: (Branch) => void
|
||||
) => void,
|
||||
resetForm: Repository => void,
|
||||
callback?: (p: Branch) => void
|
||||
) => void;
|
||||
resetForm: (p: Repository) => void;
|
||||
|
||||
// context objects
|
||||
t: string => string,
|
||||
history: History,
|
||||
location: any
|
||||
history: History;
|
||||
location: any;
|
||||
};
|
||||
|
||||
class CreateBranch extends React.Component<Props> {
|
||||
@@ -57,19 +51,12 @@ class CreateBranch extends React.Component<Props> {
|
||||
|
||||
branchCreated = (branch: Branch) => {
|
||||
const { history, repository } = this.props;
|
||||
history.push(
|
||||
`/repo/${repository.namespace}/${
|
||||
repository.name
|
||||
}/branch/${encodeURIComponent(branch.name)}/info`
|
||||
);
|
||||
history.push(`/repo/${repository.namespace}/${repository.name}/branch/${encodeURIComponent(branch.name)}/info`);
|
||||
};
|
||||
|
||||
createBranch = (branch: BranchRequest) => {
|
||||
this.props.createBranch(
|
||||
this.props.createBranchesLink,
|
||||
this.props.repository,
|
||||
branch,
|
||||
newBranch => this.branchCreated(newBranch)
|
||||
this.props.createBranch(this.props.createBranchesLink, this.props.repository, branch, newBranch =>
|
||||
this.branchCreated(newBranch)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -126,11 +113,8 @@ const mapDispatchToProps = dispatch => {
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { repository } = ownProps;
|
||||
const loading =
|
||||
isFetchBranchesPending(state, repository) ||
|
||||
isCreateBranchPending(state, repository);
|
||||
const error =
|
||||
getFetchBranchesFailure(state, repository) || getCreateBranchFailure(state);
|
||||
const loading = isFetchBranchesPending(state, repository) || isCreateBranchPending(state, repository);
|
||||
const error = getFetchBranchesFailure(state, repository) || getCreateBranchFailure(state);
|
||||
const branches = getBranches(state, repository);
|
||||
const createBranchesLink = getBranchCreateLink(state, repository);
|
||||
return {
|
||||
@@ -146,5 +130,5 @@ export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(CreateBranch))
|
||||
)(withTranslation("repos")(CreateBranch))
|
||||
);
|
||||
@@ -39,11 +39,26 @@ const repository = {
|
||||
}
|
||||
};
|
||||
|
||||
const branch1 = { name: "branch1", revision: "revision1" };
|
||||
const branch2 = { name: "branch2", revision: "revision2" };
|
||||
const branch3 = { name: "branch3", revision: "revision3" };
|
||||
const branchRequest = { name: "newBranch", source: "master" };
|
||||
const newBranch = { name: "newBranch", revision: "rev3" };
|
||||
const branch1 = {
|
||||
name: "branch1",
|
||||
revision: "revision1"
|
||||
};
|
||||
const branch2 = {
|
||||
name: "branch2",
|
||||
revision: "revision2"
|
||||
};
|
||||
const branch3 = {
|
||||
name: "branch3",
|
||||
revision: "revision3"
|
||||
};
|
||||
const branchRequest = {
|
||||
name: "newBranch",
|
||||
source: "master"
|
||||
};
|
||||
const newBranch = {
|
||||
name: "newBranch",
|
||||
revision: "rev3"
|
||||
};
|
||||
|
||||
describe("branches", () => {
|
||||
describe("fetch branches", () => {
|
||||
@@ -63,12 +78,17 @@ describe("branches", () => {
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_BRANCHES_PENDING,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: key
|
||||
},
|
||||
{
|
||||
type: FETCH_BRANCHES_SUCCESS,
|
||||
payload: { data: collection, repository },
|
||||
payload: {
|
||||
data: collection,
|
||||
repository
|
||||
},
|
||||
itemId: key
|
||||
}
|
||||
];
|
||||
@@ -87,12 +107,17 @@ describe("branches", () => {
|
||||
const expectedActions = [
|
||||
{
|
||||
type: FETCH_BRANCHES_PENDING,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: key
|
||||
},
|
||||
{
|
||||
type: FETCH_BRANCHES_FAILURE,
|
||||
payload: { error: collection, repository },
|
||||
payload: {
|
||||
error: collection,
|
||||
repository
|
||||
},
|
||||
itemId: key
|
||||
}
|
||||
];
|
||||
@@ -143,13 +168,11 @@ describe("branches", () => {
|
||||
fetchMock.getOnce(URL + "/newBranch", newBranch);
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_SUCCESS);
|
||||
});
|
||||
return store.dispatch(createBranch(URL, repository, branchRequest)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should call the callback with the branch from the location header", () => {
|
||||
@@ -172,11 +195,9 @@ describe("branches", () => {
|
||||
receivedBranch = branch;
|
||||
};
|
||||
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest, callback))
|
||||
.then(() => {
|
||||
expect(receivedBranch).toEqual(newBranch);
|
||||
});
|
||||
return store.dispatch(createBranch(URL, repository, branchRequest, callback)).then(() => {
|
||||
expect(receivedBranch).toEqual(newBranch);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail creating a branch on HTTP 500", () => {
|
||||
@@ -185,13 +206,11 @@ describe("branches", () => {
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(createBranch(URL, repository, branchRequest))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_FAILURE);
|
||||
});
|
||||
return store.dispatch(createBranch(URL, repository, branchRequest)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(CREATE_BRANCH_PENDING);
|
||||
expect(actions[1].type).toEqual(CREATE_BRANCH_FAILURE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -377,7 +396,12 @@ describe("branches", () => {
|
||||
});
|
||||
|
||||
it("should return undefined, if no branches for the repository available", () => {
|
||||
const branches = getBranches({ branches: {} }, repository);
|
||||
const branches = getBranches(
|
||||
{
|
||||
branches: {}
|
||||
},
|
||||
repository
|
||||
);
|
||||
expect(branches).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -1,17 +1,6 @@
|
||||
// @flow
|
||||
import {
|
||||
FAILURE_SUFFIX,
|
||||
PENDING_SUFFIX,
|
||||
SUCCESS_SUFFIX,
|
||||
RESET_SUFFIX
|
||||
} from "../../../modules/types";
|
||||
import { FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX, RESET_SUFFIX } from "../../../modules/types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import type {
|
||||
Action,
|
||||
Branch,
|
||||
BranchRequest,
|
||||
Repository
|
||||
} from "@scm-manager/ui-types";
|
||||
import { Action, Branch, BranchRequest, Repository } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
|
||||
@@ -33,8 +22,7 @@ export const CREATE_BRANCH_SUCCESS = `${CREATE_BRANCH}_${SUCCESS_SUFFIX}`;
|
||||
export const CREATE_BRANCH_FAILURE = `${CREATE_BRANCH}_${FAILURE_SUFFIX}`;
|
||||
export const CREATE_BRANCH_RESET = `${CREATE_BRANCH}_${RESET_SUFFIX}`;
|
||||
|
||||
const CONTENT_TYPE_BRANCH_REQUEST =
|
||||
"application/vnd.scmm-branchRequest+json;v=2";
|
||||
const CONTENT_TYPE_BRANCH_REQUEST = "application/vnd.scmm-branchRequest+json;v=2";
|
||||
|
||||
// Fetching branches
|
||||
|
||||
@@ -42,7 +30,10 @@ export function fetchBranches(repository: Repository) {
|
||||
if (!repository._links.branches) {
|
||||
return {
|
||||
type: FETCH_BRANCHES_SUCCESS,
|
||||
payload: { repository, data: {} },
|
||||
payload: {
|
||||
repository,
|
||||
data: {}
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
@@ -116,26 +107,21 @@ function collectBranches(repoState) {
|
||||
|
||||
const memoizedBranchCollector = memoizeOne(collectBranches);
|
||||
|
||||
export function getBranches(state: Object, repository: Repository) {
|
||||
export function getBranches(state: object, repository: Repository) {
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (repoState && repoState.list) {
|
||||
return memoizedBranchCollector(repoState);
|
||||
}
|
||||
}
|
||||
|
||||
export function getBranchCreateLink(state: Object, repository: Repository) {
|
||||
export function getBranchCreateLink(state: object, repository: Repository) {
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (
|
||||
repoState &&
|
||||
repoState.list &&
|
||||
repoState.list._links &&
|
||||
repoState.list._links.create
|
||||
) {
|
||||
if (repoState && repoState.list && repoState.list._links && repoState.list._links.create) {
|
||||
return repoState.list._links.create.href;
|
||||
}
|
||||
}
|
||||
|
||||
function getRepoState(state: Object, repository: Repository) {
|
||||
function getRepoState(state: object, repository: Repository) {
|
||||
const key = createKey(repository);
|
||||
const repoState = state.branches[key];
|
||||
if (repoState && repoState.byName) {
|
||||
@@ -143,24 +129,12 @@ function getRepoState(state: Object, repository: Repository) {
|
||||
}
|
||||
}
|
||||
|
||||
export const isPermittedToCreateBranches = (
|
||||
state: Object,
|
||||
repository: Repository
|
||||
): boolean => {
|
||||
export const isPermittedToCreateBranches = (state: object, repository: Repository): boolean => {
|
||||
const repoState = getRepoState(state, repository);
|
||||
return !!(
|
||||
repoState &&
|
||||
repoState.list &&
|
||||
repoState.list._links &&
|
||||
repoState.list._links.create
|
||||
);
|
||||
return !!(repoState && repoState.list && repoState.list._links && repoState.list._links.create);
|
||||
};
|
||||
|
||||
export function getBranch(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
): ?Branch {
|
||||
export function getBranch(state: object, repository: Repository, name: string): Branch | null | undefined {
|
||||
const repoState = getRepoState(state, repository);
|
||||
if (repoState) {
|
||||
return repoState.byName[name];
|
||||
@@ -168,21 +142,20 @@ export function getBranch(
|
||||
}
|
||||
|
||||
// Action creators
|
||||
export function isFetchBranchesPending(
|
||||
state: Object,
|
||||
repository: Repository
|
||||
): boolean {
|
||||
export function isFetchBranchesPending(state: object, repository: Repository): boolean {
|
||||
return isPending(state, FETCH_BRANCHES, createKey(repository));
|
||||
}
|
||||
|
||||
export function getFetchBranchesFailure(state: Object, repository: Repository) {
|
||||
export function getFetchBranchesFailure(state: object, repository: Repository) {
|
||||
return getFailure(state, FETCH_BRANCHES, createKey(repository));
|
||||
}
|
||||
|
||||
export function fetchBranchesPending(repository: Repository) {
|
||||
return {
|
||||
type: FETCH_BRANCHES_PENDING,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
@@ -190,7 +163,10 @@ export function fetchBranchesPending(repository: Repository) {
|
||||
export function fetchBranchesSuccess(data: string, repository: Repository) {
|
||||
return {
|
||||
type: FETCH_BRANCHES_SUCCESS,
|
||||
payload: { data, repository },
|
||||
payload: {
|
||||
data,
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
@@ -198,23 +174,28 @@ export function fetchBranchesSuccess(data: string, repository: Repository) {
|
||||
export function fetchBranchesFailure(repository: Repository, error: Error) {
|
||||
return {
|
||||
type: FETCH_BRANCHES_FAILURE,
|
||||
payload: { error, repository },
|
||||
payload: {
|
||||
error,
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function isCreateBranchPending(state: Object, repository: Repository) {
|
||||
export function isCreateBranchPending(state: object, repository: Repository) {
|
||||
return isPending(state, CREATE_BRANCH, createKey(repository));
|
||||
}
|
||||
|
||||
export function getCreateBranchFailure(state: Object) {
|
||||
export function getCreateBranchFailure(state: object) {
|
||||
return getFailure(state, CREATE_BRANCH);
|
||||
}
|
||||
|
||||
export function createBranchPending(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_PENDING,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
@@ -222,18 +203,20 @@ export function createBranchPending(repository: Repository): Action {
|
||||
export function createBranchSuccess(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_SUCCESS,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function createBranchFailure(
|
||||
repository: Repository,
|
||||
error: Error
|
||||
): Action {
|
||||
export function createBranchFailure(repository: Repository, error: Error): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_FAILURE,
|
||||
payload: { repository, error },
|
||||
payload: {
|
||||
repository,
|
||||
error
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
@@ -241,57 +224,51 @@ export function createBranchFailure(
|
||||
export function createBranchReset(repository: Repository): Action {
|
||||
return {
|
||||
type: CREATE_BRANCH_RESET,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: createKey(repository)
|
||||
};
|
||||
}
|
||||
|
||||
export function isFetchBranchPending(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
export function isFetchBranchPending(state: object, repository: Repository, name: string) {
|
||||
return isPending(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function getFetchBranchFailure(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
name: string
|
||||
) {
|
||||
export function getFetchBranchFailure(state: object, repository: Repository, name: string) {
|
||||
return getFailure(state, FETCH_BRANCH, createKey(repository) + "/" + name);
|
||||
}
|
||||
|
||||
export function fetchBranchPending(
|
||||
repository: Repository,
|
||||
name: string
|
||||
): Action {
|
||||
export function fetchBranchPending(repository: Repository, name: string): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_PENDING,
|
||||
payload: { repository, name },
|
||||
payload: {
|
||||
repository,
|
||||
name
|
||||
},
|
||||
itemId: createKey(repository) + "/" + name
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranchSuccess(
|
||||
repository: Repository,
|
||||
branch: Branch
|
||||
): Action {
|
||||
export function fetchBranchSuccess(repository: Repository, branch: Branch): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_SUCCESS,
|
||||
payload: { repository, branch },
|
||||
payload: {
|
||||
repository,
|
||||
branch
|
||||
},
|
||||
itemId: createKey(repository) + "/" + branch.name
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchBranchFailure(
|
||||
repository: Repository,
|
||||
name: string,
|
||||
error: Error
|
||||
): Action {
|
||||
export function fetchBranchFailure(repository: Repository, name: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_BRANCH_FAILURE,
|
||||
payload: { error, repository, name },
|
||||
payload: {
|
||||
error,
|
||||
repository,
|
||||
name
|
||||
},
|
||||
itemId: createKey(repository) + "/" + name
|
||||
};
|
||||
}
|
||||
@@ -310,7 +287,7 @@ const reduceByBranchesSuccess = (state, payload) => {
|
||||
if (response._embedded) {
|
||||
const branches = response._embedded.branches;
|
||||
response._embedded.branches = branches.map(b => b.name);
|
||||
for (let branch of branches) {
|
||||
for (const branch of branches) {
|
||||
byName[branch.name] = branch;
|
||||
}
|
||||
return {
|
||||
@@ -346,8 +323,10 @@ const reduceByBranchSuccess = (state, payload) => {
|
||||
|
||||
export default function reducer(
|
||||
state: {} = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): Object {
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
import { orderBranches } from "./orderBranches";
|
||||
|
||||
const branch1 = { name: "branch1", revision: "revision1" };
|
||||
const branch2 = { name: "branch2", revision: "revision2" };
|
||||
const branch3 = { name: "branch3", revision: "revision3", defaultBranch: true };
|
||||
const branch1 = {
|
||||
name: "branch1",
|
||||
revision: "revision1"
|
||||
};
|
||||
const branch2 = {
|
||||
name: "branch2",
|
||||
revision: "revision2"
|
||||
};
|
||||
const branch3 = {
|
||||
name: "branch3",
|
||||
revision: "revision3",
|
||||
defaultBranch: true
|
||||
};
|
||||
const defaultBranch = {
|
||||
name: "default",
|
||||
revision: "revision4",
|
||||
@@ -21,31 +31,26 @@ const masterBranch = {
|
||||
|
||||
describe("order branches", () => {
|
||||
it("should return branches", () => {
|
||||
let branches = [branch1, branch2];
|
||||
const branches = [branch1, branch2];
|
||||
orderBranches(branches);
|
||||
expect(branches).toEqual([branch1, branch2]);
|
||||
});
|
||||
|
||||
it("should return defaultBranch first", () => {
|
||||
let branches = [branch1, branch2, branch3];
|
||||
const branches = [branch1, branch2, branch3];
|
||||
orderBranches(branches);
|
||||
expect(branches).toEqual([branch3, branch1, branch2]);
|
||||
});
|
||||
|
||||
it("should order special branches as follows: master > default > develop", () => {
|
||||
let branches = [defaultBranch, developBranch, masterBranch];
|
||||
const branches = [defaultBranch, developBranch, masterBranch];
|
||||
orderBranches(branches);
|
||||
expect(branches).toEqual([masterBranch, defaultBranch, developBranch]);
|
||||
});
|
||||
|
||||
it("should order special branches but starting with defaultBranch", () => {
|
||||
let branches = [masterBranch, developBranch, defaultBranch, branch3];
|
||||
const branches = [masterBranch, developBranch, defaultBranch, branch3];
|
||||
orderBranches(branches);
|
||||
expect(branches).toEqual([
|
||||
branch3,
|
||||
masterBranch,
|
||||
defaultBranch,
|
||||
developBranch
|
||||
]);
|
||||
expect(branches).toEqual([branch3, masterBranch, defaultBranch, developBranch]);
|
||||
});
|
||||
});
|
||||
@@ -1,8 +1,6 @@
|
||||
// @flow
|
||||
|
||||
// master, default should always be the first one,
|
||||
// followed by develop the rest should be ordered by its name
|
||||
import type {Branch} from "@scm-manager/ui-types";
|
||||
import { Branch } from "@scm-manager/ui-types";
|
||||
|
||||
export function orderBranches(branches: Branch[]) {
|
||||
branches.sort((a, b) => {
|
||||
@@ -6,15 +6,12 @@ import "@scm-manager/ui-tests/i18n";
|
||||
import EditRepoNavLink from "./EditRepoNavLink";
|
||||
|
||||
describe("GeneralNavLink", () => {
|
||||
|
||||
it("should render nothing, if the modify link is missing", () => {
|
||||
const repository = {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const navLink = shallow(
|
||||
<EditRepoNavLink repository={repository} editUrl="" />
|
||||
);
|
||||
const navLink = shallow(<EditRepoNavLink repository={repository} editUrl="" />);
|
||||
expect(navLink.text()).toBe("");
|
||||
});
|
||||
|
||||
@@ -27,9 +24,7 @@ describe("GeneralNavLink", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const navLink = mount(
|
||||
<EditRepoNavLink repository={repository} editUrl="" />
|
||||
);
|
||||
const navLink = mount(<EditRepoNavLink repository={repository} editUrl="" />);
|
||||
expect(navLink.text()).toBe("repositoryRoot.menu.generalNavLink");
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
editUrl: string,
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
editUrl: string;
|
||||
};
|
||||
|
||||
class EditRepoNavLink extends React.Component<Props> {
|
||||
@@ -25,4 +23,4 @@ class EditRepoNavLink extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(EditRepoNavLink);
|
||||
export default withTranslation("repos")(EditRepoNavLink);
|
||||
@@ -1,20 +1,16 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import {mount, shallow } from "@scm-manager/ui-tests/enzyme-router";
|
||||
import { mount, shallow } from "@scm-manager/ui-tests/enzyme-router";
|
||||
import "@scm-manager/ui-tests/enzyme";
|
||||
import "@scm-manager/ui-tests/i18n";
|
||||
import PermissionsNavLink from "./PermissionsNavLink";
|
||||
|
||||
describe("PermissionsNavLink", () => {
|
||||
|
||||
it("should render nothing, if the modify link is missing", () => {
|
||||
const repository = {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const navLink = shallow(
|
||||
<PermissionsNavLink repository={repository} permissionUrl="" />
|
||||
);
|
||||
const navLink = shallow(<PermissionsNavLink repository={repository} permissionUrl="" />);
|
||||
expect(navLink.text()).toBe("");
|
||||
});
|
||||
|
||||
@@ -27,9 +23,7 @@ describe("PermissionsNavLink", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const navLink = mount(
|
||||
<PermissionsNavLink repository={repository} permissionUrl="" />
|
||||
);
|
||||
const navLink = mount(<PermissionsNavLink repository={repository} permissionUrl="" />);
|
||||
expect(navLink.text()).toBe("repositoryRoot.menu.permissionsNavLink");
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
permissionUrl: string,
|
||||
t: string => string,
|
||||
repository: Repository
|
||||
type Props = WithTranslation & {
|
||||
permissionUrl: string;
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
class PermissionsNavLink extends React.Component<Props> {
|
||||
@@ -19,10 +17,8 @@ class PermissionsNavLink extends React.Component<Props> {
|
||||
return null;
|
||||
}
|
||||
const { permissionUrl, t } = this.props;
|
||||
return (
|
||||
<NavLink to={permissionUrl} label={t("repositoryRoot.menu.permissionsNavLink")} />
|
||||
);
|
||||
return <NavLink to={permissionUrl} label={t("repositoryRoot.menu.permissionsNavLink")} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(PermissionsNavLink);
|
||||
export default withTranslation("repos")(PermissionsNavLink);
|
||||
@@ -1,13 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { MailLink, DateFromNow } from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
class RepositoryDetailTable extends React.Component<Props> {
|
||||
@@ -52,4 +49,4 @@ class RepositoryDetailTable extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(RepositoryDetailTable);
|
||||
export default withTranslation("repos")(RepositoryDetailTable);
|
||||
@@ -1,11 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import RepositoryDetailTable from "./RepositoryDetailTable";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
class RepositoryDetails extends React.Component<Props> {
|
||||
@@ -13,13 +12,15 @@ class RepositoryDetails extends React.Component<Props> {
|
||||
const { repository } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<RepositoryDetailTable repository={repository}/>
|
||||
<hr/>
|
||||
<RepositoryDetailTable repository={repository} />
|
||||
<hr />
|
||||
<div className="content">
|
||||
<ExtensionPoint
|
||||
name="repos.repository-details.information"
|
||||
renderAll={true}
|
||||
props={{ repository }}
|
||||
props={{
|
||||
repository
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,11 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { shallow, mount } from "@scm-manager/ui-tests/enzyme-router";
|
||||
import "@scm-manager/ui-tests/i18n";
|
||||
import RepositoryNavLink from "./RepositoryNavLink";
|
||||
|
||||
describe("RepositoryNavLink", () => {
|
||||
|
||||
it("should render nothing, if the sources link is missing", () => {
|
||||
const repository = {
|
||||
namespace: "Namespace",
|
||||
@@ -1,15 +1,14 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { NavLink } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
to: string,
|
||||
label: string,
|
||||
linkName: string,
|
||||
activeWhenMatch?: (route: any) => boolean,
|
||||
activeOnlyWhenExact: boolean
|
||||
repository: Repository;
|
||||
to: string;
|
||||
label: string;
|
||||
linkName: string;
|
||||
activeWhenMatch?: (route: any) => boolean;
|
||||
activeOnlyWhenExact: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1,10 +1,9 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Interpolate, translate } from "react-i18next";
|
||||
import { Trans, WithTranslation, withTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Changeset, Repository, Tag } from "@scm-manager/ui-types";
|
||||
import { Changeset, Repository, Tag } from "@scm-manager/ui-types";
|
||||
import {
|
||||
DateFromNow,
|
||||
ChangesetId,
|
||||
@@ -18,16 +17,13 @@ import {
|
||||
Button
|
||||
} from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
changeset: Changeset,
|
||||
repository: Repository,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
changeset: Changeset;
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
type State = {
|
||||
collapsed: boolean
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
const RightMarginP = styled.p`
|
||||
@@ -57,9 +53,7 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
const { collapsed } = this.state;
|
||||
|
||||
const description = changesets.parseDescription(changeset.description);
|
||||
const id = (
|
||||
<ChangesetId repository={repository} changeset={changeset} link={false} />
|
||||
);
|
||||
const id = <ChangesetId repository={repository} changeset={changeset} link={false} />;
|
||||
const date = <DateFromNow date={changeset.date} />;
|
||||
|
||||
return (
|
||||
@@ -68,7 +62,10 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
<h4>
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{ changeset, value: description.title }}
|
||||
props={{
|
||||
changeset,
|
||||
value: description.title
|
||||
}}
|
||||
renderAll={false}
|
||||
>
|
||||
{description.title}
|
||||
@@ -85,7 +82,7 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
<ChangesetAuthor changeset={changeset} />
|
||||
</p>
|
||||
<p>
|
||||
<Interpolate i18nKey="changeset.summary" id={id} time={date} />
|
||||
<Trans i18nKey="repos:changeset.summary" components={[id, date]} />
|
||||
</p>
|
||||
</div>
|
||||
<div className="media-right">{this.renderTags()}</div>
|
||||
@@ -97,7 +94,10 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
<span key={key}>
|
||||
<ExtensionPoint
|
||||
name="changeset.description"
|
||||
props={{ changeset, value: item }}
|
||||
props={{
|
||||
changeset,
|
||||
value: item
|
||||
}}
|
||||
renderAll={false}
|
||||
>
|
||||
{item}
|
||||
@@ -151,4 +151,4 @@ class ChangesetDetails extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(ChangesetDetails);
|
||||
export default withTranslation("repos")(ChangesetDetails);
|
||||
@@ -1,31 +1,23 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import {
|
||||
Subtitle,
|
||||
InputField,
|
||||
Select,
|
||||
SubmitButton,
|
||||
Textarea
|
||||
} from "@scm-manager/ui-components";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||
import { Repository, RepositoryType } from "@scm-manager/ui-types";
|
||||
import { Subtitle, InputField, Select, SubmitButton, Textarea } from "@scm-manager/ui-components";
|
||||
import * as validator from "./repositoryValidation";
|
||||
|
||||
type Props = {
|
||||
submitForm: Repository => void,
|
||||
repository?: Repository,
|
||||
repositoryTypes: RepositoryType[],
|
||||
namespaceStrategy: string,
|
||||
loading?: boolean,
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
submitForm: (p: Repository) => void;
|
||||
repository?: Repository;
|
||||
repositoryTypes: RepositoryType[];
|
||||
namespaceStrategy: string;
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
repository: Repository,
|
||||
namespaceValidationError: boolean,
|
||||
nameValidationError: boolean,
|
||||
contactValidationError: boolean
|
||||
repository: Repository;
|
||||
namespaceValidationError: boolean;
|
||||
nameValidationError: boolean;
|
||||
contactValidationError: boolean;
|
||||
};
|
||||
|
||||
const CUSTOM_NAMESPACE_STRATEGY = "CustomNamespaceStrategy";
|
||||
@@ -52,15 +44,17 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
componentDidMount() {
|
||||
const { repository } = this.props;
|
||||
if (repository) {
|
||||
this.setState({ repository: { ...repository } });
|
||||
this.setState({
|
||||
repository: {
|
||||
...repository
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isFalsy(value) {
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return !value;
|
||||
|
||||
}
|
||||
|
||||
isValid = () => {
|
||||
@@ -71,8 +65,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
this.state.nameValidationError ||
|
||||
this.state.contactValidationError ||
|
||||
this.isFalsy(repository.name) ||
|
||||
(namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY &&
|
||||
this.isFalsy(repository.namespace))
|
||||
(namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && this.isFalsy(repository.namespace))
|
||||
);
|
||||
};
|
||||
|
||||
@@ -98,11 +91,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
const disabled = !this.isModifiable() && !this.isCreateMode();
|
||||
|
||||
const submitButton = disabled ? null : (
|
||||
<SubmitButton
|
||||
disabled={!this.isValid()}
|
||||
loading={loading}
|
||||
label={t("repositoryForm.submit")}
|
||||
/>
|
||||
<SubmitButton disabled={!this.isValid()} loading={loading} label={t("repositoryForm.submit")} />
|
||||
);
|
||||
|
||||
let subtitle = null;
|
||||
@@ -164,13 +153,7 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
return <InputField {...props} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<ExtensionPoint
|
||||
name="repos.create.namespace"
|
||||
props={props}
|
||||
renderAll={false}
|
||||
/>
|
||||
);
|
||||
return <ExtensionPoint name="repos.create.namespace" props={props} renderAll={false} />;
|
||||
};
|
||||
|
||||
renderCreateOnlyFields() {
|
||||
@@ -204,35 +187,50 @@ class RepositoryForm extends React.Component<Props, State> {
|
||||
handleNamespaceChange = (namespace: string) => {
|
||||
this.setState({
|
||||
namespaceValidationError: !validator.isNameValid(namespace),
|
||||
repository: { ...this.state.repository, namespace }
|
||||
repository: {
|
||||
...this.state.repository,
|
||||
namespace
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleNameChange = (name: string) => {
|
||||
this.setState({
|
||||
nameValidationError: !validator.isNameValid(name),
|
||||
repository: { ...this.state.repository, name }
|
||||
repository: {
|
||||
...this.state.repository,
|
||||
name
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleTypeChange = (type: string) => {
|
||||
this.setState({
|
||||
repository: { ...this.state.repository, type }
|
||||
repository: {
|
||||
...this.state.repository,
|
||||
type
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleContactChange = (contact: string) => {
|
||||
this.setState({
|
||||
contactValidationError: !validator.isContactValid(contact),
|
||||
repository: { ...this.state.repository, contact }
|
||||
repository: {
|
||||
...this.state.repository,
|
||||
contact
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleDescriptionChange = (description: string) => {
|
||||
this.setState({
|
||||
repository: { ...this.state.repository, description }
|
||||
repository: {
|
||||
...this.state.repository,
|
||||
description
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(RepositoryForm);
|
||||
export default withTranslation("repos")(RepositoryForm);
|
||||
@@ -13,21 +13,9 @@ describe("repository name validation", () => {
|
||||
});
|
||||
|
||||
it("should allow same names as the backend", () => {
|
||||
const validPaths = [
|
||||
"scm",
|
||||
"s",
|
||||
"sc",
|
||||
".hiddenrepo",
|
||||
"b.",
|
||||
"...",
|
||||
"..c",
|
||||
"d..",
|
||||
"a..c"
|
||||
];
|
||||
const validPaths = ["scm", "s", "sc", ".hiddenrepo", "b.", "...", "..c", "d..", "a..c"];
|
||||
|
||||
validPaths.forEach((path) =>
|
||||
expect(validator.isNameValid(path)).toBe(true)
|
||||
);
|
||||
validPaths.forEach(path => expect(validator.isNameValid(path)).toBe(true));
|
||||
});
|
||||
|
||||
it("should deny same names as the backend", () => {
|
||||
@@ -82,9 +70,7 @@ describe("repository name validation", () => {
|
||||
"scm/plugins/git-plugin"
|
||||
];
|
||||
|
||||
invalidPaths.forEach((path) =>
|
||||
expect(validator.isNameValid(path)).toBe(false)
|
||||
);
|
||||
invalidPaths.forEach(path => expect(validator.isNameValid(path)).toBe(false));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -95,9 +81,7 @@ describe("repository contact validation", () => {
|
||||
|
||||
// we don't need rich tests, because they are in validation.test.js
|
||||
it("should allow real mail addresses", () => {
|
||||
expect(validator.isContactValid("trici.mcmillian@hitchhiker.com")).toBe(
|
||||
true
|
||||
);
|
||||
expect(validator.isContactValid("trici.mcmillian@hitchhiker.com")).toBe(true);
|
||||
});
|
||||
|
||||
it("should fail on invalid mail addresses", () => {
|
||||
@@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import { validation } from "@scm-manager/ui-components";
|
||||
|
||||
const nameRegex = /(?!^\.\.$)(?!^\.$)(?!.*[\\\[\]])^[A-Za-z0-9\.][A-Za-z0-9\.\-_]*$/;
|
||||
@@ -1,11 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { Image } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
class RepositoryAvatar extends React.Component<Props> {
|
||||
@@ -13,7 +12,12 @@ class RepositoryAvatar extends React.Component<Props> {
|
||||
const { repository } = this.props;
|
||||
return (
|
||||
<p className="image is-64x64">
|
||||
<ExtensionPoint name="repos.repository-avatar" props={{ repository }}>
|
||||
<ExtensionPoint
|
||||
name="repos.repository-avatar"
|
||||
props={{
|
||||
repository
|
||||
}}
|
||||
>
|
||||
<Image src="/images/blib.jpg" alt="Logo" />
|
||||
</ExtensionPoint>
|
||||
</p>
|
||||
@@ -1,12 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { CardColumn, DateFromNow } from "@scm-manager/ui-components";
|
||||
import RepositoryEntryLink from "./RepositoryEntryLink";
|
||||
import RepositoryAvatar from "./RepositoryAvatar";
|
||||
|
||||
type Props = {
|
||||
repository: Repository
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
class RepositoryEntry extends React.Component<Props> {
|
||||
@@ -16,45 +15,28 @@ class RepositoryEntry extends React.Component<Props> {
|
||||
|
||||
renderBranchesLink = (repository: Repository, repositoryLink: string) => {
|
||||
if (repository._links["branches"]) {
|
||||
return (
|
||||
<RepositoryEntryLink
|
||||
icon="code-branch"
|
||||
to={repositoryLink + "/branches"}
|
||||
/>
|
||||
);
|
||||
return <RepositoryEntryLink icon="code-branch" to={repositoryLink + "/branches"} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderChangesetsLink = (repository: Repository, repositoryLink: string) => {
|
||||
if (repository._links["changesets"]) {
|
||||
return (
|
||||
<RepositoryEntryLink
|
||||
icon="exchange-alt"
|
||||
to={repositoryLink + "/changesets"}
|
||||
/>
|
||||
);
|
||||
return <RepositoryEntryLink icon="exchange-alt" to={repositoryLink + "/changesets"} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderSourcesLink = (repository: Repository, repositoryLink: string) => {
|
||||
if (repository._links["sources"]) {
|
||||
return (
|
||||
<RepositoryEntryLink icon="code" to={repositoryLink + "/sources"} />
|
||||
);
|
||||
return <RepositoryEntryLink icon="code" to={repositoryLink + "/sources"} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderModifyLink = (repository: Repository, repositoryLink: string) => {
|
||||
if (repository._links["update"]) {
|
||||
return (
|
||||
<RepositoryEntryLink
|
||||
icon="cog"
|
||||
to={repositoryLink + "/settings/general"}
|
||||
/>
|
||||
);
|
||||
return <RepositoryEntryLink icon="cog" to={repositoryLink + "/settings/general"} />;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -1,12 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { Icon } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
to: string,
|
||||
icon: string
|
||||
to: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
const PointerEventsLink = styled(Link)`
|
||||
@@ -1,11 +1,10 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { CardColumnGroup } from "@scm-manager/ui-components";
|
||||
import type { RepositoryGroup } from "@scm-manager/ui-types";
|
||||
import { RepositoryGroup } from "@scm-manager/ui-types";
|
||||
import RepositoryEntry from "./RepositoryEntry";
|
||||
|
||||
type Props = {
|
||||
group: RepositoryGroup
|
||||
group: RepositoryGroup;
|
||||
};
|
||||
|
||||
class RepositoryGroupEntry extends React.Component<Props> {
|
||||
@@ -1,13 +1,12 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
import groupByNamespace from "./groupByNamespace";
|
||||
import RepositoryGroupEntry from "./RepositoryGroupEntry";
|
||||
|
||||
type Props = {
|
||||
repositories: Repository[]
|
||||
repositories: Repository[];
|
||||
};
|
||||
|
||||
class RepositoryList extends React.Component<Props> {
|
||||
@@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import groupByNamespace from "./groupByNamespace";
|
||||
|
||||
const base = {
|
||||
@@ -54,11 +53,7 @@ it("should group the repositories by their namespace", () => {
|
||||
const expected = [
|
||||
{
|
||||
name: "hitchhiker",
|
||||
repositories: [
|
||||
hitchhikerHeartOfGold,
|
||||
hitchhikerPuzzle42,
|
||||
hitchhikerRestand
|
||||
]
|
||||
repositories: [hitchhikerHeartOfGold, hitchhikerPuzzle42, hitchhikerRestand]
|
||||
},
|
||||
{
|
||||
name: "slarti",
|
||||
@@ -1,11 +1,8 @@
|
||||
// @flow
|
||||
import type { Repository, RepositoryGroup } from "@scm-manager/ui-types";
|
||||
import { Repository, RepositoryGroup } from "@scm-manager/ui-types";
|
||||
|
||||
export default function groupByNamespace(
|
||||
repositories: Repository[]
|
||||
): RepositoryGroup[] {
|
||||
let groups = {};
|
||||
for (let repository of repositories) {
|
||||
export default function groupByNamespace(repositories: Repository[]): RepositoryGroup[] {
|
||||
const groups = {};
|
||||
for (const repository of repositories) {
|
||||
const groupName = repository.namespace;
|
||||
|
||||
let group = groups[groupName];
|
||||
@@ -19,8 +16,8 @@ export default function groupByNamespace(
|
||||
group.repositories.push(repository);
|
||||
}
|
||||
|
||||
let groupArray = [];
|
||||
for (let groupName in groups) {
|
||||
const groupArray = [];
|
||||
for (const groupName in groups) {
|
||||
const group = groups[groupName];
|
||||
group.repositories.sort(sortByName);
|
||||
groupArray.push(groups[groupName]);
|
||||
@@ -1,8 +1,9 @@
|
||||
//@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 { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Changeset, Repository } from "@scm-manager/ui-types";
|
||||
import { ErrorPage, Loading } from "@scm-manager/ui-components";
|
||||
import {
|
||||
fetchChangesetIfNeeded,
|
||||
getChangeset,
|
||||
@@ -10,18 +11,15 @@ import {
|
||||
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
|
||||
type Props = WithTranslation & {
|
||||
id: string;
|
||||
changeset: Changeset;
|
||||
repository: Repository;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
fetchChangesetIfNeeded: (repository: Repository, id: string) => void;
|
||||
match: any;
|
||||
};
|
||||
|
||||
class ChangesetView extends React.Component<Props> {
|
||||
@@ -35,13 +33,7 @@ class ChangesetView extends React.Component<Props> {
|
||||
const { changeset, loading, error, t, repository } = this.props;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("changesets.errorTitle")}
|
||||
subtitle={t("changesets.errorSubtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
return <ErrorPage title={t("changesets.errorTitle")} subtitle={t("changesets.errorSubtitle")} error={error} />;
|
||||
}
|
||||
|
||||
if (!changeset || loading) return <Loading />;
|
||||
@@ -56,7 +48,11 @@ const mapStateToProps = (state, ownProps: Props) => {
|
||||
const changeset = getChangeset(state, repository, id);
|
||||
const loading = isFetchChangesetPending(state, repository, id);
|
||||
const error = getFetchChangesetFailure(state, repository, id);
|
||||
return { changeset, error, loading };
|
||||
return {
|
||||
changeset,
|
||||
error,
|
||||
loading
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
@@ -71,5 +67,5 @@ export default withRouter(
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(ChangesetView))
|
||||
)(withTranslation("repos")(ChangesetView))
|
||||
);
|
||||
@@ -1,22 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type {
|
||||
Branch,
|
||||
Changeset,
|
||||
PagedCollection,
|
||||
Repository
|
||||
} from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchChangesets,
|
||||
getChangesets,
|
||||
getFetchChangesetsFailure,
|
||||
isFetchChangesetsPending,
|
||||
selectListAsCollection
|
||||
} from "../modules/changesets";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { compose } from "redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Branch, Changeset, PagedCollection, Repository } from "@scm-manager/ui-types";
|
||||
import {
|
||||
ErrorNotification,
|
||||
getPageFromMatch,
|
||||
@@ -25,26 +12,30 @@ import {
|
||||
Loading,
|
||||
Notification
|
||||
} from "@scm-manager/ui-components";
|
||||
import { compose } from "redux";
|
||||
import { translate } from "react-i18next";
|
||||
import {
|
||||
fetchChangesets,
|
||||
getChangesets,
|
||||
getFetchChangesetsFailure,
|
||||
isFetchChangesetsPending,
|
||||
selectListAsCollection
|
||||
} from "../modules/changesets";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
branch: Branch,
|
||||
page: number,
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
branch: Branch;
|
||||
page: number;
|
||||
|
||||
// State props
|
||||
changesets: Changeset[],
|
||||
list: PagedCollection,
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
changesets: Changeset[];
|
||||
list: PagedCollection;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
|
||||
// Dispatch props
|
||||
fetchChangesets: (Repository, Branch, number) => void,
|
||||
fetchChangesets: (p1: Repository, p2: Branch, p3: number) => void;
|
||||
|
||||
// context props
|
||||
match: any,
|
||||
t: string => string
|
||||
match: any;
|
||||
};
|
||||
|
||||
class Changesets extends React.Component<Props> {
|
||||
@@ -68,9 +59,7 @@ class Changesets extends React.Component<Props> {
|
||||
if (!changesets || changesets.length === 0) {
|
||||
return (
|
||||
<div className="panel-block">
|
||||
<Notification type="info">
|
||||
{t("changesets.noChangesets")}
|
||||
</Notification>
|
||||
<Notification type="info">{t("changesets.noChangesets")}</Notification>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -120,11 +109,17 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const list = selectListAsCollection(state, repository, branch);
|
||||
const page = getPageFromMatch(match);
|
||||
|
||||
return { changesets, list, page, loading, error };
|
||||
return {
|
||||
changesets,
|
||||
list,
|
||||
page,
|
||||
loading,
|
||||
error
|
||||
};
|
||||
};
|
||||
|
||||
export default compose(
|
||||
translate("repos"),
|
||||
withTranslation("repos"),
|
||||
withRouter,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
@@ -1,43 +1,36 @@
|
||||
// @flow
|
||||
|
||||
import React from "react";
|
||||
import type { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { translate } from "react-i18next";
|
||||
import { Route, withRouter } from "react-router-dom";
|
||||
import Changesets from "./Changesets";
|
||||
import { compose } from "redux";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
BranchSelector,
|
||||
ErrorNotification,
|
||||
Loading
|
||||
} from "@scm-manager/ui-components";
|
||||
import { Route, withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { BranchSelector, ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import Changesets from "./Changesets";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "../branches/modules/branches";
|
||||
import { compose } from "redux";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
baseUrl: string,
|
||||
selected: string,
|
||||
baseUrlWithBranch: string,
|
||||
baseUrlWithoutBranch: string,
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
baseUrl: string;
|
||||
selected: string;
|
||||
baseUrlWithBranch: string;
|
||||
baseUrlWithoutBranch: string;
|
||||
|
||||
// State props
|
||||
branches: Branch[],
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
branches: Branch[];
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
|
||||
// Dispatch props
|
||||
fetchBranches: Repository => void,
|
||||
fetchBranches: (p: Repository) => void;
|
||||
|
||||
// Context props
|
||||
history: any, // TODO flow type
|
||||
match: any,
|
||||
t: string => string
|
||||
history: any; // TODO flow type
|
||||
match: any;
|
||||
};
|
||||
|
||||
class ChangesetsRoot extends React.Component<Props> {
|
||||
@@ -48,9 +41,7 @@ class ChangesetsRoot extends React.Component<Props> {
|
||||
|
||||
redirectToDefaultBranch = () => {
|
||||
if (this.shouldRedirectToDefaultBranch()) {
|
||||
const defaultBranches = this.props.branches.filter(
|
||||
b => b.defaultBranch === true
|
||||
);
|
||||
const defaultBranches = this.props.branches.filter(b => b.defaultBranch === true);
|
||||
if (defaultBranches.length > 0) {
|
||||
this.branchSelected(defaultBranches[0]);
|
||||
}
|
||||
@@ -61,8 +52,7 @@ class ChangesetsRoot extends React.Component<Props> {
|
||||
return (
|
||||
this.props.branches &&
|
||||
this.props.branches.length > 0 &&
|
||||
this.props.selected !==
|
||||
this.props.branches.filter(b => b.defaultBranch === true)[0]
|
||||
this.props.selected !== this.props.branches.filter(b => b.defaultBranch === true)[0]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -76,9 +66,7 @@ class ChangesetsRoot extends React.Component<Props> {
|
||||
branchSelected = (branch?: Branch) => {
|
||||
let url;
|
||||
if (branch) {
|
||||
url = `${this.props.baseUrlWithBranch}/${encodeURIComponent(
|
||||
branch.name
|
||||
)}/changesets/`;
|
||||
url = `${this.props.baseUrlWithBranch}/${encodeURIComponent(branch.name)}/changesets/`;
|
||||
} else {
|
||||
url = `${this.props.baseUrlWithoutBranch}/`;
|
||||
}
|
||||
@@ -162,7 +150,7 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
translate("repos"),
|
||||
withTranslation("repos"),
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
@@ -1,50 +1,41 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { Repository, RepositoryType, NamespaceStrategies } from "@scm-manager/ui-types";
|
||||
import { Page } from "@scm-manager/ui-components";
|
||||
import RepositoryForm from "../components/form";
|
||||
import type { Repository, RepositoryType, NamespaceStrategies } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchRepositoryTypesIfNeeded,
|
||||
getFetchRepositoryTypesFailure,
|
||||
getRepositoryTypes,
|
||||
isFetchRepositoryTypesPending
|
||||
} from "../modules/repositoryTypes";
|
||||
import {
|
||||
createRepo,
|
||||
createRepoReset,
|
||||
getCreateRepoFailure,
|
||||
isCreateRepoPending
|
||||
} from "../modules/repos";
|
||||
import type { History } from "history";
|
||||
import RepositoryForm from "../components/form";
|
||||
import { createRepo, createRepoReset, getCreateRepoFailure, isCreateRepoPending } from "../modules/repos";
|
||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
||||
import {
|
||||
fetchNamespaceStrategiesIfNeeded,
|
||||
getFetchNamespaceStrategiesFailure, getNamespaceStrategies, isFetchNamespaceStrategiesPending
|
||||
getFetchNamespaceStrategiesFailure,
|
||||
getNamespaceStrategies,
|
||||
isFetchNamespaceStrategiesPending
|
||||
} from "../../admin/modules/namespaceStrategies";
|
||||
|
||||
type Props = {
|
||||
repositoryTypes: RepositoryType[],
|
||||
namespaceStrategies: NamespaceStrategies,
|
||||
pageLoading: boolean,
|
||||
createLoading: boolean,
|
||||
error: Error,
|
||||
repoLink: string,
|
||||
type Props = WithTranslation & {
|
||||
repositoryTypes: RepositoryType[];
|
||||
namespaceStrategies: NamespaceStrategies;
|
||||
pageLoading: boolean;
|
||||
createLoading: boolean;
|
||||
error: Error;
|
||||
repoLink: string;
|
||||
|
||||
// dispatch functions
|
||||
fetchNamespaceStrategiesIfNeeded: () => void,
|
||||
fetchRepositoryTypesIfNeeded: () => void,
|
||||
createRepo: (
|
||||
link: string,
|
||||
Repository,
|
||||
callback: (repo: Repository) => void
|
||||
) => void,
|
||||
resetForm: () => void,
|
||||
fetchNamespaceStrategiesIfNeeded: () => void;
|
||||
fetchRepositoryTypesIfNeeded: () => void;
|
||||
createRepo: (link: string, p2: Repository, callback: (repo: Repository) => void) => void;
|
||||
resetForm: () => void;
|
||||
|
||||
// context props
|
||||
t: string => string,
|
||||
history: History
|
||||
history: History;
|
||||
};
|
||||
|
||||
class Create extends React.Component<Props> {
|
||||
@@ -61,14 +52,7 @@ class Create extends React.Component<Props> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
pageLoading,
|
||||
createLoading,
|
||||
repositoryTypes,
|
||||
namespaceStrategies,
|
||||
createRepo,
|
||||
error
|
||||
} = this.props;
|
||||
const { pageLoading, createLoading, repositoryTypes, namespaceStrategies, createRepo, error } = this.props;
|
||||
|
||||
const { t, repoLink } = this.props;
|
||||
return (
|
||||
@@ -84,9 +68,7 @@ class Create extends React.Component<Props> {
|
||||
loading={createLoading}
|
||||
namespaceStrategy={namespaceStrategies.current}
|
||||
submitForm={repo => {
|
||||
createRepo(repoLink, repo, (repo: Repository) =>
|
||||
this.repoCreated(repo)
|
||||
);
|
||||
createRepo(repoLink, repo, (repo: Repository) => this.repoCreated(repo));
|
||||
}}
|
||||
/>
|
||||
</Page>
|
||||
@@ -97,12 +79,10 @@ class Create extends React.Component<Props> {
|
||||
const mapStateToProps = state => {
|
||||
const repositoryTypes = getRepositoryTypes(state);
|
||||
const namespaceStrategies = getNamespaceStrategies(state);
|
||||
const pageLoading = isFetchRepositoryTypesPending(state)
|
||||
|| isFetchNamespaceStrategiesPending(state);
|
||||
const pageLoading = isFetchRepositoryTypesPending(state) || isFetchNamespaceStrategiesPending(state);
|
||||
const createLoading = isCreateRepoPending(state);
|
||||
const error = getFetchRepositoryTypesFailure(state)
|
||||
|| getCreateRepoFailure(state)
|
||||
|| getFetchNamespaceStrategiesFailure(state);
|
||||
const error =
|
||||
getFetchRepositoryTypesFailure(state) || getCreateRepoFailure(state) || getFetchNamespaceStrategiesFailure(state);
|
||||
const repoLink = getRepositoriesLink(state);
|
||||
return {
|
||||
repositoryTypes,
|
||||
@@ -122,11 +102,7 @@ const mapDispatchToProps = dispatch => {
|
||||
fetchNamespaceStrategiesIfNeeded: () => {
|
||||
dispatch(fetchNamespaceStrategiesIfNeeded());
|
||||
},
|
||||
createRepo: (
|
||||
link: string,
|
||||
repository: Repository,
|
||||
callback: () => void
|
||||
) => {
|
||||
createRepo: (link: string, repository: Repository, callback: () => void) => {
|
||||
dispatch(createRepo(link, repository, callback));
|
||||
},
|
||||
resetForm: () => {
|
||||
@@ -138,4 +114,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(Create));
|
||||
)(withTranslation("repos")(Create));
|
||||
@@ -1,32 +1,21 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Subtitle,
|
||||
DeleteButton,
|
||||
confirmAlert,
|
||||
ErrorNotification
|
||||
} from "@scm-manager/ui-components";
|
||||
import {
|
||||
deleteRepo,
|
||||
getDeleteRepoFailure,
|
||||
isDeleteRepoPending
|
||||
} from "../modules/repos";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { History } from "history";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { Subtitle, DeleteButton, confirmAlert, ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
repository: Repository,
|
||||
confirmDialog?: boolean,
|
||||
deleteRepo: (Repository, () => void) => void,
|
||||
type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repository: Repository;
|
||||
confirmDialog?: boolean;
|
||||
deleteRepo: (p1: Repository, p2: () => void) => void;
|
||||
|
||||
// context props
|
||||
history: History,
|
||||
t: string => string
|
||||
history: History;
|
||||
};
|
||||
|
||||
class DeleteRepo extends React.Component<Props> {
|
||||
@@ -79,11 +68,7 @@ class DeleteRepo extends React.Component<Props> {
|
||||
<ErrorNotification error={error} />
|
||||
<div className="columns">
|
||||
<div className="column">
|
||||
<DeleteButton
|
||||
label={t("deleteRepo.button")}
|
||||
action={action}
|
||||
loading={loading}
|
||||
/>
|
||||
<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@@ -112,4 +97,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(withRouter(translate("repos")(DeleteRepo)));
|
||||
)(withRouter(withTranslation("repos")(DeleteRepo)));
|
||||
@@ -1,31 +1,25 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import RepositoryForm from "../components/form";
|
||||
import DeleteRepo from "./DeleteRepo";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import {
|
||||
modifyRepo,
|
||||
isModifyRepoPending,
|
||||
getModifyRepoFailure,
|
||||
modifyRepoReset
|
||||
} from "../modules/repos";
|
||||
import type { History } from "history";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { modifyRepo, isModifyRepoPending, getModifyRepoFailure, modifyRepoReset } from "../modules/repos";
|
||||
import { History } from "history";
|
||||
import { ErrorNotification } from "@scm-manager/ui-components";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
|
||||
modifyRepo: (Repository, () => void) => void,
|
||||
modifyRepoReset: Repository => void,
|
||||
modifyRepo: (p1: Repository, p2: () => void) => void;
|
||||
modifyRepoReset: (p: Repository) => void;
|
||||
|
||||
// context props
|
||||
repository: Repository,
|
||||
history: History,
|
||||
match: any
|
||||
repository: Repository;
|
||||
history: History;
|
||||
match: any;
|
||||
};
|
||||
|
||||
class EditRepo extends React.Component<Props> {
|
||||
@@ -70,11 +64,7 @@ class EditRepo extends React.Component<Props> {
|
||||
this.props.modifyRepo(repo, this.repoModified);
|
||||
}}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="repo-config.route"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
/>
|
||||
<ExtensionPoint name="repo-config.route" props={extensionProps} renderAll={true} />
|
||||
<DeleteRepo repository={repository} />
|
||||
</div>
|
||||
);
|
||||
@@ -1,17 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { translate } from "react-i18next";
|
||||
import type { History } from "history";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import type { RepositoryCollection } from "@scm-manager/ui-types";
|
||||
import {
|
||||
fetchReposByPage,
|
||||
getFetchReposFailure,
|
||||
getRepositoryCollection,
|
||||
isAbleToCreateRepos,
|
||||
isFetchReposPending
|
||||
} from "../modules/repos";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { RepositoryCollection } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Page,
|
||||
PageActions,
|
||||
@@ -21,53 +13,44 @@ import {
|
||||
LinkPaginator,
|
||||
urls
|
||||
} from "@scm-manager/ui-components";
|
||||
import RepositoryList from "../components/list";
|
||||
import { getRepositoriesLink } from "../../modules/indexResource";
|
||||
import {
|
||||
fetchReposByPage,
|
||||
getFetchReposFailure,
|
||||
getRepositoryCollection,
|
||||
isAbleToCreateRepos,
|
||||
isFetchReposPending
|
||||
} from "../modules/repos";
|
||||
import RepositoryList from "../components/list";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
showCreateButton: boolean,
|
||||
collection: RepositoryCollection,
|
||||
page: number,
|
||||
reposLink: string,
|
||||
type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
showCreateButton: boolean;
|
||||
collection: RepositoryCollection;
|
||||
page: number;
|
||||
reposLink: string;
|
||||
|
||||
// context props
|
||||
t: string => string,
|
||||
history: History,
|
||||
location: any,
|
||||
history: History;
|
||||
location: any;
|
||||
|
||||
// dispatched functions
|
||||
fetchReposByPage: (link: string, page: number, filter?: string) => void
|
||||
fetchReposByPage: (link: string, page: number, filter?: string) => void;
|
||||
};
|
||||
|
||||
class Overview extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchReposByPage, reposLink, page, location } = this.props;
|
||||
fetchReposByPage(
|
||||
reposLink,
|
||||
page,
|
||||
urls.getQueryStringFromLocation(location)
|
||||
);
|
||||
fetchReposByPage(reposLink, page, urls.getQueryStringFromLocation(location));
|
||||
}
|
||||
|
||||
componentDidUpdate = (prevProps: Props) => {
|
||||
const {
|
||||
loading,
|
||||
collection,
|
||||
page,
|
||||
reposLink,
|
||||
location,
|
||||
fetchReposByPage
|
||||
} = this.props;
|
||||
const { loading, collection, page, reposLink, location, fetchReposByPage } = this.props;
|
||||
if (collection && page && !loading) {
|
||||
const statePage: number = collection.page + 1;
|
||||
if (page !== statePage || prevProps.location.search !== location.search) {
|
||||
fetchReposByPage(
|
||||
reposLink,
|
||||
page,
|
||||
urls.getQueryStringFromLocation(location)
|
||||
);
|
||||
fetchReposByPage(reposLink, page, urls.getQueryStringFromLocation(location));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -75,19 +58,10 @@ class Overview extends React.Component<Props> {
|
||||
render() {
|
||||
const { error, loading, showCreateButton, t } = this.props;
|
||||
return (
|
||||
<Page
|
||||
title={t("overview.title")}
|
||||
subtitle={t("overview.subtitle")}
|
||||
loading={loading}
|
||||
error={error}
|
||||
>
|
||||
<Page title={t("overview.title")} subtitle={t("overview.subtitle")} loading={loading} error={error}>
|
||||
{this.renderOverview()}
|
||||
<PageActions>
|
||||
<OverviewPageActions
|
||||
showCreateButton={showCreateButton}
|
||||
link="repos"
|
||||
label={t("overview.createButton")}
|
||||
/>
|
||||
<OverviewPageActions showCreateButton={showCreateButton} link="repos" label={t("overview.createButton")} />
|
||||
</PageActions>
|
||||
</Page>
|
||||
);
|
||||
@@ -100,17 +74,11 @@ class Overview extends React.Component<Props> {
|
||||
return (
|
||||
<>
|
||||
<RepositoryList repositories={collection._embedded.repositories} />
|
||||
<LinkPaginator
|
||||
collection={collection}
|
||||
page={page}
|
||||
filter={urls.getQueryStringFromLocation(location)}
|
||||
/>
|
||||
<LinkPaginator collection={collection} page={page} filter={urls.getQueryStringFromLocation(location)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Notification type="info">{t("overview.noRepositories")}</Notification>
|
||||
);
|
||||
return <Notification type="info">{t("overview.noRepositories")}</Notification>;
|
||||
}
|
||||
|
||||
renderOverview() {
|
||||
@@ -129,9 +97,7 @@ class Overview extends React.Component<Props> {
|
||||
renderCreateButton() {
|
||||
const { showCreateButton, t } = this.props;
|
||||
if (showCreateButton) {
|
||||
return (
|
||||
<CreateButton label={t("overview.createButton")} link="/repos/create" />
|
||||
);
|
||||
return <CreateButton label={t("overview.createButton")} link="/repos/create" />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -165,4 +131,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(withRouter(Overview)));
|
||||
)(withTranslation("repos")(withRouter(Overview)));
|
||||
@@ -1,33 +1,17 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {
|
||||
fetchRepoByName,
|
||||
getFetchRepoFailure,
|
||||
getRepository,
|
||||
isFetchRepoPending
|
||||
} from "../modules/repos";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
import {
|
||||
ErrorPage,
|
||||
Loading,
|
||||
Navigation,
|
||||
NavLink,
|
||||
Page,
|
||||
Section,
|
||||
SubNavigation
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
import { ErrorPage, Loading, Navigation, NavLink, Page, Section, SubNavigation } from "@scm-manager/ui-components";
|
||||
import { fetchRepoByName, getFetchRepoFailure, getRepository, isFetchRepoPending } from "../modules/repos";
|
||||
import RepositoryDetails from "../components/RepositoryDetails";
|
||||
import EditRepo from "./EditRepo";
|
||||
import BranchesOverview from "../branches/containers/BranchesOverview";
|
||||
import CreateBranch from "../branches/containers/CreateBranch";
|
||||
import Permissions from "../permissions/containers/Permissions";
|
||||
|
||||
import type { History } from "history";
|
||||
import EditRepoNavLink from "../components/EditRepoNavLink";
|
||||
import BranchRoot from "../branches/containers/BranchRoot";
|
||||
import ChangesetsRoot from "./ChangesetsRoot";
|
||||
@@ -36,30 +20,27 @@ import PermissionsNavLink from "../components/PermissionsNavLink";
|
||||
import Sources from "../sources/containers/Sources";
|
||||
import RepositoryNavLink from "../components/RepositoryNavLink";
|
||||
import { getLinks, getRepositoriesLink } from "../../modules/indexResource";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
|
||||
type Props = {
|
||||
namespace: string,
|
||||
name: string,
|
||||
repository: Repository,
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
repoLink: string,
|
||||
indexLinks: Object,
|
||||
type Props = WithTranslation & {
|
||||
namespace: string;
|
||||
name: string;
|
||||
repository: Repository;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
repoLink: string;
|
||||
indexLinks: object;
|
||||
|
||||
// dispatch functions
|
||||
fetchRepoByName: (link: string, namespace: string, name: string) => void,
|
||||
fetchRepoByName: (link: string, namespace: string, name: string) => void;
|
||||
|
||||
// context props
|
||||
t: string => string,
|
||||
history: History,
|
||||
match: any
|
||||
history: History;
|
||||
match: any;
|
||||
};
|
||||
|
||||
class RepositoryRoot extends React.Component<Props> {
|
||||
componentDidMount() {
|
||||
const { fetchRepoByName, namespace, name, repoLink } = this.props;
|
||||
|
||||
fetchRepoByName(repoLink, namespace, name);
|
||||
}
|
||||
|
||||
@@ -91,11 +72,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("repositoryRoot.errorTitle")}
|
||||
subtitle={t("repositoryRoot.errorSubtitle")}
|
||||
error={error}
|
||||
/>
|
||||
<ErrorPage title={t("repositoryRoot.errorTitle")} subtitle={t("repositoryRoot.errorSubtitle")} error={error} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -111,10 +88,7 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
indexLinks
|
||||
};
|
||||
|
||||
const redirectUrlFactory = binder.getExtension(
|
||||
"repository.redirect",
|
||||
this.props
|
||||
);
|
||||
const redirectUrlFactory = binder.getExtension("repository.redirect", this.props);
|
||||
let redirectedUrl;
|
||||
if (redirectUrlFactory) {
|
||||
redirectedUrl = url + redirectUrlFactory(this.props);
|
||||
@@ -128,41 +102,23 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
<div className="column is-three-quarters">
|
||||
<Switch>
|
||||
<Redirect exact from={this.props.match.url} to={redirectedUrl} />
|
||||
<Route
|
||||
path={`${url}/info`}
|
||||
exact
|
||||
component={() => <RepositoryDetails repository={repository} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/settings/general`}
|
||||
component={() => <EditRepo repository={repository} />}
|
||||
/>
|
||||
<Route path={`${url}/info`} exact component={() => <RepositoryDetails repository={repository} />} />
|
||||
<Route path={`${url}/settings/general`} component={() => <EditRepo repository={repository} />} />
|
||||
<Route
|
||||
path={`${url}/settings/permissions`}
|
||||
render={() => (
|
||||
<Permissions
|
||||
namespace={this.props.repository.namespace}
|
||||
repoName={this.props.repository.name}
|
||||
/>
|
||||
<Permissions namespace={this.props.repository.namespace} repoName={this.props.repository.name} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={`${url}/changeset/:id`}
|
||||
render={() => <ChangesetView repository={repository} />}
|
||||
/>
|
||||
<Route exact path={`${url}/changeset/:id`} render={() => <ChangesetView repository={repository} />} />
|
||||
<Route
|
||||
path={`${url}/sources`}
|
||||
exact={true}
|
||||
render={() => (
|
||||
<Sources repository={repository} baseUrl={`${url}/sources`} />
|
||||
)}
|
||||
render={() => <Sources repository={repository} baseUrl={`${url}/sources`} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/sources/:revision/:path*`}
|
||||
render={() => (
|
||||
<Sources repository={repository} baseUrl={`${url}/sources`} />
|
||||
)}
|
||||
render={() => <Sources repository={repository} baseUrl={`${url}/sources`} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/changesets`}
|
||||
@@ -186,42 +142,21 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/branch/:branch`}
|
||||
render={() => (
|
||||
<BranchRoot
|
||||
repository={repository}
|
||||
baseUrl={`${url}/branch`}
|
||||
/>
|
||||
)}
|
||||
render={() => <BranchRoot repository={repository} baseUrl={`${url}/branch`} />}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/branches`}
|
||||
exact={true}
|
||||
render={() => (
|
||||
<BranchesOverview
|
||||
repository={repository}
|
||||
baseUrl={`${url}/branch`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
path={`${url}/branches/create`}
|
||||
render={() => <CreateBranch repository={repository} />}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="repository.route"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
render={() => <BranchesOverview repository={repository} baseUrl={`${url}/branch`} />}
|
||||
/>
|
||||
<Route path={`${url}/branches/create`} render={() => <CreateBranch repository={repository} />} />
|
||||
<ExtensionPoint name="repository.route" props={extensionProps} renderAll={true} />
|
||||
</Switch>
|
||||
</div>
|
||||
<div className="column">
|
||||
<Navigation>
|
||||
<Section label={t("repositoryRoot.menu.navigationLabel")}>
|
||||
<ExtensionPoint
|
||||
name="repository.navigation.topLevel"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
/>
|
||||
<ExtensionPoint name="repository.navigation.topLevel" props={extensionProps} renderAll={true} />
|
||||
<NavLink
|
||||
to={`${url}/info`}
|
||||
icon="fas fa-info-circle"
|
||||
@@ -253,28 +188,11 @@ class RepositoryRoot extends React.Component<Props> {
|
||||
label={t("repositoryRoot.menu.sourcesNavLink")}
|
||||
activeOnlyWhenExact={false}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="repository.navigation"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
/>
|
||||
<SubNavigation
|
||||
to={`${url}/settings/general`}
|
||||
label={t("repositoryRoot.menu.settingsNavLink")}
|
||||
>
|
||||
<EditRepoNavLink
|
||||
repository={repository}
|
||||
editUrl={`${url}/settings/general`}
|
||||
/>
|
||||
<PermissionsNavLink
|
||||
permissionUrl={`${url}/settings/permissions`}
|
||||
repository={repository}
|
||||
/>
|
||||
<ExtensionPoint
|
||||
name="repository.setting"
|
||||
props={extensionProps}
|
||||
renderAll={true}
|
||||
/>
|
||||
<ExtensionPoint name="repository.navigation" props={extensionProps} renderAll={true} />
|
||||
<SubNavigation to={`${url}/settings/general`} label={t("repositoryRoot.menu.settingsNavLink")}>
|
||||
<EditRepoNavLink repository={repository} editUrl={`${url}/settings/general`} />
|
||||
<PermissionsNavLink permissionUrl={`${url}/settings/permissions`} repository={repository} />
|
||||
<ExtensionPoint name="repository.setting" props={extensionProps} renderAll={true} />
|
||||
</SubNavigation>
|
||||
</Section>
|
||||
</Navigation>
|
||||
@@ -314,4 +232,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(RepositoryRoot));
|
||||
)(withTranslation("repos")(RepositoryRoot));
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
@@ -32,8 +30,7 @@ const branch = {
|
||||
revision: "123",
|
||||
_links: {
|
||||
history: {
|
||||
href:
|
||||
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets"
|
||||
href: "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -50,8 +47,7 @@ const repository = {
|
||||
href: "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/changesets"
|
||||
},
|
||||
branches: {
|
||||
href:
|
||||
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/branches"
|
||||
href: "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/branches"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -60,10 +56,8 @@ const changesets = {};
|
||||
|
||||
describe("changesets", () => {
|
||||
describe("fetching of changesets", () => {
|
||||
const DEFAULT_BRANCH_URL =
|
||||
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/changesets";
|
||||
const SPECIFIC_BRANCH_URL =
|
||||
"http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets";
|
||||
const DEFAULT_BRANCH_URL = "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/changesets";
|
||||
const SPECIFIC_BRANCH_URL = "http://scm.hitchhicker.com/api/v2/repositories/foo/bar/branches/specific/changesets";
|
||||
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
|
||||
@@ -94,11 +88,9 @@ describe("changesets", () => {
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchChangeset(repository, changesetId))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
return store.dispatch(fetchChangeset(repository, changesetId)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail fetching changeset on error", () => {
|
||||
@@ -112,13 +104,11 @@ describe("changesets", () => {
|
||||
];
|
||||
|
||||
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();
|
||||
});
|
||||
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", () => {
|
||||
@@ -141,11 +131,9 @@ describe("changesets", () => {
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchChangesetIfNeeded(repository, "id3"))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
return store.dispatch(fetchChangesetIfNeeded(repository, "id3")).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not fetch changeset if not needed", () => {
|
||||
@@ -155,17 +143,19 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id1: { id: "id1" },
|
||||
id2: { id: "id2" }
|
||||
id1: {
|
||||
id: "id1"
|
||||
},
|
||||
id2: {
|
||||
id: "id2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const store = mockStore(state);
|
||||
return expect(
|
||||
store.dispatch(fetchChangesetIfNeeded(repository, "id1"))
|
||||
).toEqual(undefined);
|
||||
return expect(store.dispatch(fetchChangesetIfNeeded(repository, "id1"))).toEqual(undefined);
|
||||
});
|
||||
|
||||
it("should fetch changesets for default branch", () => {
|
||||
@@ -277,11 +267,9 @@ describe("changesets", () => {
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(fetchChangesets(repository, undefined, 5))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
return store.dispatch(fetchChangesets(repository, undefined, 5)).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fetch changesets by branch and page", () => {
|
||||
@@ -317,9 +305,21 @@ describe("changesets", () => {
|
||||
_links: {},
|
||||
_embedded: {
|
||||
changesets: [
|
||||
{ id: "changeset1", author: { mail: "z@phod.com", name: "zaphod" } },
|
||||
{ id: "changeset2", description: "foo" },
|
||||
{ id: "changeset3", description: "bar" }
|
||||
{
|
||||
id: "changeset1",
|
||||
author: {
|
||||
mail: "z@phod.com",
|
||||
name: "zaphod"
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "changeset2",
|
||||
description: "foo"
|
||||
},
|
||||
{
|
||||
id: "changeset3",
|
||||
description: "bar"
|
||||
}
|
||||
],
|
||||
_embedded: {
|
||||
tags: [],
|
||||
@@ -330,14 +330,9 @@ describe("changesets", () => {
|
||||
};
|
||||
|
||||
it("should set state to received changesets", () => {
|
||||
const newState = reducer(
|
||||
{},
|
||||
fetchChangesetsSuccess(repository, undefined, responseBody)
|
||||
);
|
||||
const newState = reducer({}, fetchChangesetsSuccess(repository, undefined, responseBody));
|
||||
expect(newState).toBeDefined();
|
||||
expect(newState["foo/bar"].byId["changeset1"].author.mail).toEqual(
|
||||
"z@phod.com"
|
||||
);
|
||||
expect(newState["foo/bar"].byId["changeset1"].author.mail).toEqual("z@phod.com");
|
||||
expect(newState["foo/bar"].byId["changeset2"].description).toEqual("foo");
|
||||
expect(newState["foo/bar"].byId["changeset3"].description).toEqual("bar");
|
||||
expect(newState["foo/bar"].byBranch[""]).toEqual({
|
||||
@@ -351,25 +346,22 @@ describe("changesets", () => {
|
||||
});
|
||||
|
||||
it("should store the changeset list to branch", () => {
|
||||
const newState = reducer(
|
||||
{},
|
||||
fetchChangesetsSuccess(repository, branch, responseBody)
|
||||
);
|
||||
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"
|
||||
]);
|
||||
expect(newState["foo/bar"].byBranch["specific"].entries).toEqual(["changeset1", "changeset2", "changeset3"]);
|
||||
});
|
||||
|
||||
it("should not remove existing changesets", () => {
|
||||
const state = {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id2: { id: "id2" },
|
||||
id1: { id: "id1" }
|
||||
id2: {
|
||||
id: "id2"
|
||||
},
|
||||
id1: {
|
||||
id: "id1"
|
||||
}
|
||||
},
|
||||
byBranch: {
|
||||
"": {
|
||||
@@ -379,20 +371,17 @@ describe("changesets", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
fetchChangesetsSuccess(repository, undefined, responseBody)
|
||||
);
|
||||
const newState = reducer(state, fetchChangesetsSuccess(repository, undefined, responseBody));
|
||||
|
||||
const fooBar = newState["foo/bar"];
|
||||
|
||||
expect(fooBar.byBranch[""].entries).toEqual([
|
||||
"changeset1",
|
||||
"changeset2",
|
||||
"changeset3"
|
||||
]);
|
||||
expect(fooBar.byId["id2"]).toEqual({ id: "id2" });
|
||||
expect(fooBar.byId["id1"]).toEqual({ id: "id1" });
|
||||
expect(fooBar.byBranch[""].entries).toEqual(["changeset1", "changeset2", "changeset3"]);
|
||||
expect(fooBar.byId["id2"]).toEqual({
|
||||
id: "id2"
|
||||
});
|
||||
expect(fooBar.byId["id1"]).toEqual({
|
||||
id: "id1"
|
||||
});
|
||||
});
|
||||
|
||||
const responseBodySingleChangeset = {
|
||||
@@ -415,9 +404,12 @@ describe("changesets", () => {
|
||||
{
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
"id2": {
|
||||
id2: {
|
||||
id: "id2",
|
||||
author: { mail: "mail@author.com", name: "author" }
|
||||
author: {
|
||||
mail: "mail@author.com",
|
||||
name: "author"
|
||||
}
|
||||
}
|
||||
},
|
||||
list: {
|
||||
@@ -434,9 +426,7 @@ describe("changesets", () => {
|
||||
);
|
||||
|
||||
expect(newState).toBeDefined();
|
||||
expect(newState["foo/bar"].byId["id3"].description).toEqual(
|
||||
"added testChangeset"
|
||||
);
|
||||
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();
|
||||
@@ -459,14 +449,20 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id1: { id: "id1" },
|
||||
id2: { id: "id2" }
|
||||
id1: {
|
||||
id: "id1"
|
||||
},
|
||||
id2: {
|
||||
id: "id2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const result = getChangeset(state, repository, "id1");
|
||||
expect(result).toEqual({ id: "id1" });
|
||||
expect(result).toEqual({
|
||||
id: "id1"
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null if changeset does not exist", () => {
|
||||
@@ -474,8 +470,12 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id1: { id: "id1" },
|
||||
id2: { id: "id2" }
|
||||
id1: {
|
||||
id: "id1"
|
||||
},
|
||||
id2: {
|
||||
id: "id2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,8 +489,12 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id1: { id: "id1" },
|
||||
id2: { id: "id2" }
|
||||
id1: {
|
||||
id: "id1"
|
||||
},
|
||||
id2: {
|
||||
id: "id2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,8 +508,12 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id1: { id: "id1" },
|
||||
id2: { id: "id2" }
|
||||
id1: {
|
||||
id: "id1"
|
||||
},
|
||||
id2: {
|
||||
id: "id2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,8 +555,12 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id2: { id: "id2" },
|
||||
id1: { id: "id1" }
|
||||
id2: {
|
||||
id: "id2"
|
||||
},
|
||||
id1: {
|
||||
id: "id1"
|
||||
}
|
||||
},
|
||||
byBranch: {
|
||||
"": {
|
||||
@@ -559,7 +571,14 @@ describe("changesets", () => {
|
||||
}
|
||||
};
|
||||
const result = getChangesets(state, repository);
|
||||
expect(result).toEqual([{ id: "id1" }, { id: "id2" }]);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
id: "id1"
|
||||
},
|
||||
{
|
||||
id: "id2"
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it("should return true, when fetching changesets is pending", () => {
|
||||
@@ -595,8 +614,12 @@ describe("changesets", () => {
|
||||
changesets: {
|
||||
"foo/bar": {
|
||||
byId: {
|
||||
id2: { id: "id2" },
|
||||
id1: { id: "id1" }
|
||||
id2: {
|
||||
id: "id2"
|
||||
},
|
||||
id1: {
|
||||
id: "id1"
|
||||
}
|
||||
},
|
||||
byBranch: {
|
||||
"": {
|
||||
@@ -1,19 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
FAILURE_SUFFIX,
|
||||
PENDING_SUFFIX,
|
||||
SUCCESS_SUFFIX
|
||||
} from "../../modules/types";
|
||||
import { FAILURE_SUFFIX, PENDING_SUFFIX, SUCCESS_SUFFIX } from "../../modules/types";
|
||||
import { apiClient, urls } 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";
|
||||
import { Action, Branch, PagedCollection, Repository } from "@scm-manager/ui-types";
|
||||
|
||||
export const FETCH_CHANGESETS = "scm/repos/FETCH_CHANGESETS";
|
||||
export const FETCH_CHANGESETS_PENDING = `${FETCH_CHANGESETS}_${PENDING_SUFFIX}`;
|
||||
@@ -53,33 +42,26 @@ function createChangesetUrl(repository: Repository, id: string) {
|
||||
return urls.concat(repository._links.changesets.href, id);
|
||||
}
|
||||
|
||||
export function fetchChangesetPending(
|
||||
repository: Repository,
|
||||
id: string
|
||||
): Action {
|
||||
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 {
|
||||
export function fetchChangesetSuccess(changeset: any, repository: Repository, id: string): Action {
|
||||
return {
|
||||
type: FETCH_CHANGESET_SUCCESS,
|
||||
payload: { changeset, repository, id },
|
||||
payload: {
|
||||
changeset,
|
||||
repository,
|
||||
id
|
||||
},
|
||||
itemId: createChangesetItemId(repository, id)
|
||||
};
|
||||
}
|
||||
|
||||
function fetchChangesetFailure(
|
||||
repository: Repository,
|
||||
id: string,
|
||||
error: Error
|
||||
): Action {
|
||||
function fetchChangesetFailure(repository: Repository, id: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_CHANGESET_FAILURE,
|
||||
payload: {
|
||||
@@ -91,11 +73,7 @@ function fetchChangesetFailure(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchChangesets(
|
||||
repository: Repository,
|
||||
branch?: Branch,
|
||||
page?: number
|
||||
) {
|
||||
export function fetchChangesets(repository: Repository, branch?: Branch, page?: number) {
|
||||
const link = createChangesetsLink(repository, branch, page);
|
||||
|
||||
return function(dispatch: any) {
|
||||
@@ -112,11 +90,7 @@ export function fetchChangesets(
|
||||
};
|
||||
}
|
||||
|
||||
function createChangesetsLink(
|
||||
repository: Repository,
|
||||
branch?: Branch,
|
||||
page?: number
|
||||
) {
|
||||
function createChangesetsLink(repository: Repository, branch?: Branch, page?: number) {
|
||||
let link = repository._links.changesets.href;
|
||||
|
||||
if (branch) {
|
||||
@@ -129,10 +103,7 @@ function createChangesetsLink(
|
||||
return link;
|
||||
}
|
||||
|
||||
export function fetchChangesetsPending(
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
): Action {
|
||||
export function fetchChangesetsPending(repository: Repository, branch?: Branch): Action {
|
||||
const itemId = createItemId(repository, branch);
|
||||
|
||||
return {
|
||||
@@ -141,11 +112,7 @@ export function fetchChangesetsPending(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchChangesetsSuccess(
|
||||
repository: Repository,
|
||||
branch?: Branch,
|
||||
changesets: any
|
||||
): Action {
|
||||
export function fetchChangesetsSuccess(repository: Repository, branch?: Branch, changesets: any): Action {
|
||||
return {
|
||||
type: FETCH_CHANGESETS_SUCCESS,
|
||||
payload: {
|
||||
@@ -157,11 +124,7 @@ export function fetchChangesetsSuccess(
|
||||
};
|
||||
}
|
||||
|
||||
function fetchChangesetsFailure(
|
||||
repository: Repository,
|
||||
branch?: Branch,
|
||||
error: Error
|
||||
): Action {
|
||||
function fetchChangesetsFailure(repository: Repository, branch?: Branch, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_CHANGESETS_FAILURE,
|
||||
payload: {
|
||||
@@ -190,8 +153,10 @@ function createItemId(repository: Repository, branch?: Branch): string {
|
||||
// reducer
|
||||
export default function reducer(
|
||||
state: any = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): Object {
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
@@ -266,7 +231,7 @@ export default function reducer(
|
||||
function extractChangesetsByIds(changesets: any) {
|
||||
const changesetsByIds = {};
|
||||
|
||||
for (let changeset of changesets) {
|
||||
for (const changeset of changesets) {
|
||||
changesetsByIds[changeset.id] = changeset;
|
||||
}
|
||||
|
||||
@@ -274,11 +239,7 @@ function extractChangesetsByIds(changesets: any) {
|
||||
}
|
||||
|
||||
//selectors
|
||||
export function getChangesets(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
) {
|
||||
export function getChangesets(state: object, repository: Repository, branch?: Branch) {
|
||||
const repoKey = createItemId(repository);
|
||||
|
||||
const stateRoot = state.changesets[repoKey];
|
||||
@@ -298,74 +259,39 @@ export function getChangesets(
|
||||
});
|
||||
}
|
||||
|
||||
export function getChangeset(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
id: string
|
||||
) {
|
||||
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;
|
||||
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
|
||||
) {
|
||||
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 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 getFetchChangesetFailure(state: object, repository: Repository, id: string) {
|
||||
return getFailure(state, FETCH_CHANGESET, createChangesetItemId(repository, id));
|
||||
}
|
||||
|
||||
export function isFetchChangesetsPending(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
) {
|
||||
export function isFetchChangesetsPending(state: object, repository: Repository, branch?: Branch) {
|
||||
return isPending(state, FETCH_CHANGESETS, createItemId(repository, branch));
|
||||
}
|
||||
|
||||
export function getFetchChangesetsFailure(
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
) {
|
||||
export function getFetchChangesetsFailure(state: object, repository: Repository, branch?: Branch) {
|
||||
return getFailure(state, FETCH_CHANGESETS, createItemId(repository, branch));
|
||||
}
|
||||
|
||||
const selectList = (state: Object, repository: Repository, branch?: Branch) => {
|
||||
const selectList = (state: object, repository: Repository, branch?: Branch) => {
|
||||
const repoId = createItemId(repository);
|
||||
|
||||
const branchName = branch ? branch.name : "";
|
||||
@@ -379,11 +305,7 @@ const selectList = (state: Object, repository: Repository, branch?: Branch) => {
|
||||
return {};
|
||||
};
|
||||
|
||||
const selectListEntry = (
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
): Object => {
|
||||
const selectListEntry = (state: object, repository: Repository, branch?: Branch): object => {
|
||||
const list = selectList(state, repository, branch);
|
||||
if (list.entry) {
|
||||
return list.entry;
|
||||
@@ -391,10 +313,6 @@ const selectListEntry = (
|
||||
return {};
|
||||
};
|
||||
|
||||
export const selectListAsCollection = (
|
||||
state: Object,
|
||||
repository: Repository,
|
||||
branch?: Branch
|
||||
): PagedCollection => {
|
||||
export const selectListAsCollection = (state: object, repository: Repository, branch?: Branch): PagedCollection => {
|
||||
return selectListEntry(state, repository, branch);
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
@@ -48,7 +47,7 @@ import reducer, {
|
||||
getModifyRepoFailure,
|
||||
getPermissionsLink
|
||||
} from "./repos";
|
||||
import type { Repository, RepositoryCollection } from "@scm-manager/ui-types";
|
||||
import { Repository, RepositoryCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const hitchhikerPuzzle42: Repository = {
|
||||
contact: "fourtytwo@hitchhiker.com",
|
||||
@@ -68,24 +67,19 @@ const hitchhikerPuzzle42: Repository = {
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42"
|
||||
},
|
||||
permissions: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/permissions/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/permissions/"
|
||||
},
|
||||
tags: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/tags/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/tags/"
|
||||
},
|
||||
branches: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/branches/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/branches/"
|
||||
},
|
||||
changesets: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/changesets/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/changesets/"
|
||||
},
|
||||
sources: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/sources/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/puzzle42/sources/"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -108,24 +102,19 @@ const hitchhikerRestatend: Repository = {
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend"
|
||||
},
|
||||
permissions: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/restatend/permissions/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend/permissions/"
|
||||
},
|
||||
tags: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/restatend/tags/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend/tags/"
|
||||
},
|
||||
branches: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/restatend/branches/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend/branches/"
|
||||
},
|
||||
changesets: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/restatend/changesets/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend/changesets/"
|
||||
},
|
||||
sources: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/hitchhiker/restatend/sources/"
|
||||
href: "http://localhost:8081/api/v2/repositories/hitchhiker/restatend/sources/"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -148,8 +137,7 @@ const slartiFjords: Repository = {
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords"
|
||||
},
|
||||
permissions: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords/permissions/"
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords/permissions/"
|
||||
},
|
||||
tags: {
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords/tags/"
|
||||
@@ -158,8 +146,7 @@ const slartiFjords: Repository = {
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords/branches/"
|
||||
},
|
||||
changesets: {
|
||||
href:
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords/changesets/"
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords/changesets/"
|
||||
},
|
||||
sources: {
|
||||
href: "http://localhost:8081/api/v2/repositories/slarti/fjords/sources/"
|
||||
@@ -207,11 +194,7 @@ const repositoryCollectionWithNames: RepositoryCollection = {
|
||||
}
|
||||
},
|
||||
_embedded: {
|
||||
repositories: [
|
||||
"hitchhiker/puzzle42",
|
||||
"hitchhiker/restatend",
|
||||
"slarti/fjords"
|
||||
]
|
||||
repositories: ["hitchhiker/puzzle42", "hitchhiker/restatend", "slarti/fjords"]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -231,7 +214,9 @@ describe("repos fetch", () => {
|
||||
fetchMock.getOnce(REPOS_URL_WITH_SORT, repositoryCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_REPOS_PENDING },
|
||||
{
|
||||
type: FETCH_REPOS_PENDING
|
||||
},
|
||||
{
|
||||
type: FETCH_REPOS_SUCCESS,
|
||||
payload: repositoryCollection
|
||||
@@ -249,7 +234,9 @@ describe("repos fetch", () => {
|
||||
fetchMock.getOnce(url, repositoryCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_REPOS_PENDING },
|
||||
{
|
||||
type: FETCH_REPOS_PENDING
|
||||
},
|
||||
{
|
||||
type: FETCH_REPOS_SUCCESS,
|
||||
payload: repositoryCollection
|
||||
@@ -264,13 +251,12 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully fetch repos from link", () => {
|
||||
fetchMock.getOnce(
|
||||
REPOS_URL + "?" + SORT + "&page=42",
|
||||
repositoryCollection
|
||||
);
|
||||
fetchMock.getOnce(REPOS_URL + "?" + SORT + "&page=42", repositoryCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_REPOS_PENDING },
|
||||
{
|
||||
type: FETCH_REPOS_PENDING
|
||||
},
|
||||
{
|
||||
type: FETCH_REPOS_SUCCESS,
|
||||
payload: repositoryCollection
|
||||
@@ -278,23 +264,18 @@ describe("repos fetch", () => {
|
||||
];
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
fetchReposByLink("/repositories?sortBy=namespaceAndName&page=42")
|
||||
)
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
return store.dispatch(fetchReposByLink("/repositories?sortBy=namespaceAndName&page=42")).then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
});
|
||||
|
||||
it("should append sortby parameter and successfully fetch repos from link", () => {
|
||||
fetchMock.getOnce(
|
||||
"/api/v2/repositories?one=1&sortBy=namespaceAndName",
|
||||
repositoryCollection
|
||||
);
|
||||
fetchMock.getOnce("/api/v2/repositories?one=1&sortBy=namespaceAndName", repositoryCollection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_REPOS_PENDING },
|
||||
{
|
||||
type: FETCH_REPOS_PENDING
|
||||
},
|
||||
{
|
||||
type: FETCH_REPOS_SUCCESS,
|
||||
payload: repositoryCollection
|
||||
@@ -365,10 +346,7 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully fetch repo slarti/fjords", () => {
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
slartiFjords
|
||||
);
|
||||
fetchMock.getOnce("http://localhost:8081/api/v2/repositories/slarti/fjords", slartiFjords);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
@@ -393,12 +371,9 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should dispatch FETCH_REPO_FAILURE, it the request for slarti/fjords fails", () => {
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.getOnce("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(fetchRepoByLink(slartiFjords)).then(() => {
|
||||
@@ -445,7 +420,6 @@ describe("repos fetch", () => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
fetchMock.getOnce(REPOS_URL + "/slarti/fjords", slartiFjords);
|
||||
|
||||
let callMe = "not yet";
|
||||
@@ -476,12 +450,9 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully delete repo slarti/fjords", () => {
|
||||
fetchMock.delete(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.delete("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 204
|
||||
});
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
@@ -503,12 +474,9 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully delete repo slarti/fjords and call the callback", () => {
|
||||
fetchMock.delete(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.delete("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 204
|
||||
});
|
||||
|
||||
let callMe = "not yet";
|
||||
|
||||
@@ -523,12 +491,9 @@ describe("repos fetch", () => {
|
||||
});
|
||||
|
||||
it("should disapatch failure on delete, if server returns status code 500", () => {
|
||||
fetchMock.delete(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.delete("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store.dispatch(deleteRepo(slartiFjords)).then(() => {
|
||||
@@ -544,14 +509,13 @@ describe("repos fetch", () => {
|
||||
fetchMock.putOnce(slartiFjords._links.update.href, {
|
||||
status: 204
|
||||
});
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.getOnce("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
let editedFjords = { ...slartiFjords };
|
||||
const editedFjords = {
|
||||
...slartiFjords
|
||||
};
|
||||
editedFjords.description = "coast of africa";
|
||||
|
||||
const store = mockStore({});
|
||||
@@ -568,14 +532,13 @@ describe("repos fetch", () => {
|
||||
fetchMock.putOnce(slartiFjords._links.update.href, {
|
||||
status: 204
|
||||
});
|
||||
fetchMock.getOnce(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords",
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.getOnce("http://localhost:8081/api/v2/repositories/slarti/fjords", {
|
||||
status: 500
|
||||
});
|
||||
|
||||
let editedFjords = { ...slartiFjords };
|
||||
const editedFjords = {
|
||||
...slartiFjords
|
||||
};
|
||||
editedFjords.description = "coast of africa";
|
||||
|
||||
const store = mockStore({});
|
||||
@@ -599,7 +562,9 @@ describe("repos fetch", () => {
|
||||
status: 500
|
||||
});
|
||||
|
||||
let editedFjords = { ...slartiFjords };
|
||||
const editedFjords = {
|
||||
...slartiFjords
|
||||
};
|
||||
editedFjords.description = "coast of africa";
|
||||
|
||||
const store = mockStore({});
|
||||
@@ -619,13 +584,21 @@ describe("repos reducer", () => {
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is undefined", () => {
|
||||
const state = { x: true };
|
||||
const state = {
|
||||
x: true
|
||||
};
|
||||
expect(reducer(state)).toBe(state);
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is unknown to the reducer", () => {
|
||||
const state = { x: true };
|
||||
expect(reducer(state, { type: "EL_SPECIALE" })).toBe(state);
|
||||
const state = {
|
||||
x: true
|
||||
};
|
||||
expect(
|
||||
reducer(state, {
|
||||
type: "EL_SPECIALE"
|
||||
})
|
||||
).toBe(state);
|
||||
});
|
||||
|
||||
it("should store the repositories by it's namespace and name on FETCH_REPOS_SUCCESS", () => {
|
||||
@@ -717,9 +690,7 @@ describe("repos selectors", () => {
|
||||
};
|
||||
|
||||
const link = getPermissionsLink(state, "slarti", "fjords");
|
||||
expect(link).toEqual(
|
||||
"http://localhost:8081/api/v2/repositories/slarti/fjords/permissions/"
|
||||
);
|
||||
expect(link).toEqual("http://localhost:8081/api/v2/repositories/slarti/fjords/permissions/");
|
||||
});
|
||||
|
||||
it("should return true, when fetch repo is pending", () => {
|
||||
@@ -1,11 +1,6 @@
|
||||
// @flow
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from "../../modules/types";
|
||||
import type {
|
||||
Action,
|
||||
Repository,
|
||||
RepositoryCollection
|
||||
} from "@scm-manager/ui-types";
|
||||
import { Action, Repository, RepositoryCollection } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
|
||||
@@ -48,9 +43,7 @@ export function fetchRepos(link: string) {
|
||||
|
||||
export function fetchReposByPage(link: string, page: number, filter?: string) {
|
||||
if (filter) {
|
||||
return fetchReposByLink(
|
||||
`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`
|
||||
);
|
||||
return fetchReposByLink(`${link}?page=${page - 1}&q=${decodeURIComponent(filter)}`);
|
||||
}
|
||||
return fetchReposByLink(`${link}?page=${page - 1}`);
|
||||
}
|
||||
@@ -148,11 +141,7 @@ export function fetchRepoSuccess(repository: Repository): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRepoFailure(
|
||||
namespace: string,
|
||||
name: string,
|
||||
error: Error
|
||||
): Action {
|
||||
export function fetchRepoFailure(namespace: string, name: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_REPO_FAILURE,
|
||||
payload: {
|
||||
@@ -166,11 +155,7 @@ export function fetchRepoFailure(
|
||||
|
||||
// create repo
|
||||
|
||||
export function createRepo(
|
||||
link: string,
|
||||
repository: Repository,
|
||||
callback?: (repo: Repository) => void
|
||||
) {
|
||||
export function createRepo(link: string, repository: Repository, callback?: (repo: Repository) => void) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(createRepoPending());
|
||||
return apiClient
|
||||
@@ -256,13 +241,13 @@ export function modifyRepoSuccess(repository: Repository): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyRepoFailure(
|
||||
repository: Repository,
|
||||
error: Error
|
||||
): Action {
|
||||
export function modifyRepoFailure(repository: Repository, error: Error): Action {
|
||||
return {
|
||||
type: MODIFY_REPO_FAILURE,
|
||||
payload: { error, repository },
|
||||
payload: {
|
||||
error,
|
||||
repository
|
||||
},
|
||||
itemId: createIdentifier(repository)
|
||||
};
|
||||
}
|
||||
@@ -270,7 +255,9 @@ export function modifyRepoFailure(
|
||||
export function modifyRepoReset(repository: Repository): Action {
|
||||
return {
|
||||
type: MODIFY_REPO_RESET,
|
||||
payload: { repository },
|
||||
payload: {
|
||||
repository
|
||||
},
|
||||
itemId: createIdentifier(repository)
|
||||
};
|
||||
}
|
||||
@@ -310,10 +297,7 @@ export function deleteRepoSuccess(repository: Repository): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteRepoFailure(
|
||||
repository: Repository,
|
||||
error: Error
|
||||
): Action {
|
||||
export function deleteRepoFailure(repository: Repository, error: Error): Action {
|
||||
return {
|
||||
type: DELETE_REPO_FAILURE,
|
||||
payload: {
|
||||
@@ -330,9 +314,7 @@ function createIdentifier(repository: Repository) {
|
||||
return repository.namespace + "/" + repository.name;
|
||||
}
|
||||
|
||||
function normalizeByNamespaceAndName(
|
||||
repositoryCollection: RepositoryCollection
|
||||
) {
|
||||
function normalizeByNamespaceAndName(repositoryCollection: RepositoryCollection) {
|
||||
const names = [];
|
||||
const byNames = {};
|
||||
for (const repository of repositoryCollection._embedded.repositories) {
|
||||
@@ -351,7 +333,7 @@ function normalizeByNamespaceAndName(
|
||||
};
|
||||
}
|
||||
|
||||
const reducerByNames = (state: Object, repository: Repository) => {
|
||||
const reducerByNames = (state: object, repository: Repository) => {
|
||||
const identifier = createIdentifier(repository);
|
||||
return {
|
||||
...state,
|
||||
@@ -363,9 +345,11 @@ const reducerByNames = (state: Object, repository: Repository) => {
|
||||
};
|
||||
|
||||
export default function reducer(
|
||||
state: Object = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): Object {
|
||||
state: object = {},
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
@@ -382,10 +366,10 @@ export default function reducer(
|
||||
|
||||
// selectors
|
||||
|
||||
export function getRepositoryCollection(state: Object) {
|
||||
export function getRepositoryCollection(state: object) {
|
||||
if (state.repos && state.repos.list && state.repos.byNames) {
|
||||
const repositories = [];
|
||||
for (let repositoryName of state.repos.list._embedded.repositories) {
|
||||
for (const repositoryName of state.repos.list._embedded.repositories) {
|
||||
repositories.push(state.repos.byNames[repositoryName]);
|
||||
}
|
||||
return {
|
||||
@@ -397,90 +381,57 @@ export function getRepositoryCollection(state: Object) {
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchReposPending(state: Object) {
|
||||
export function isFetchReposPending(state: object) {
|
||||
return isPending(state, FETCH_REPOS);
|
||||
}
|
||||
|
||||
export function getFetchReposFailure(state: Object) {
|
||||
export function getFetchReposFailure(state: object) {
|
||||
return getFailure(state, FETCH_REPOS);
|
||||
}
|
||||
|
||||
export function getRepository(state: Object, namespace: string, name: string) {
|
||||
export function getRepository(state: object, namespace: string, name: string) {
|
||||
if (state.repos && state.repos.byNames) {
|
||||
return state.repos.byNames[namespace + "/" + name];
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchRepoPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function isFetchRepoPending(state: object, namespace: string, name: string) {
|
||||
return isPending(state, FETCH_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getFetchRepoFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function getFetchRepoFailure(state: object, namespace: string, name: string) {
|
||||
return getFailure(state, FETCH_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function isAbleToCreateRepos(state: Object) {
|
||||
return !!(
|
||||
state.repos &&
|
||||
state.repos.list &&
|
||||
state.repos.list._links &&
|
||||
state.repos.list._links.create
|
||||
);
|
||||
export function isAbleToCreateRepos(state: object) {
|
||||
return !!(state.repos && state.repos.list && state.repos.list._links && state.repos.list._links.create);
|
||||
}
|
||||
|
||||
export function isCreateRepoPending(state: Object) {
|
||||
export function isCreateRepoPending(state: object) {
|
||||
return isPending(state, CREATE_REPO);
|
||||
}
|
||||
|
||||
export function getCreateRepoFailure(state: Object) {
|
||||
export function getCreateRepoFailure(state: object) {
|
||||
return getFailure(state, CREATE_REPO);
|
||||
}
|
||||
|
||||
export function isModifyRepoPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function isModifyRepoPending(state: object, namespace: string, name: string) {
|
||||
return isPending(state, MODIFY_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getModifyRepoFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function getModifyRepoFailure(state: object, namespace: string, name: string) {
|
||||
return getFailure(state, MODIFY_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function isDeleteRepoPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function isDeleteRepoPending(state: object, namespace: string, name: string) {
|
||||
return isPending(state, DELETE_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getDeleteRepoFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function getDeleteRepoFailure(state: object, namespace: string, name: string) {
|
||||
return getFailure(state, DELETE_REPO, namespace + "/" + name);
|
||||
}
|
||||
|
||||
export function getPermissionsLink(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
name: string
|
||||
) {
|
||||
export function getPermissionsLink(state: object, namespace: string, name: string) {
|
||||
const repo = getRepository(state, namespace, name);
|
||||
return repo && repo._links ? repo._links.permissions.href : undefined;
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import fetchMock from "fetch-mock";
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
@@ -109,7 +107,9 @@ describe("repository types fetch", () => {
|
||||
fetchMock.getOnce(URL, collection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_REPOSITORY_TYPES_PENDING },
|
||||
{
|
||||
type: FETCH_REPOSITORY_TYPES_PENDING
|
||||
},
|
||||
{
|
||||
type: FETCH_REPOSITORY_TYPES_SUCCESS,
|
||||
payload: collection
|
||||
@@ -1,28 +1,16 @@
|
||||
// @flow
|
||||
|
||||
import * as types from "../../modules/types";
|
||||
import type {
|
||||
Action,
|
||||
RepositoryType,
|
||||
RepositoryTypeCollection
|
||||
} from "@scm-manager/ui-types";
|
||||
import { Action, RepositoryType, RepositoryTypeCollection } from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { isPending } from "../../modules/pending";
|
||||
import { getFailure } from "../../modules/failure";
|
||||
|
||||
export const FETCH_REPOSITORY_TYPES = "scm/repos/FETCH_REPOSITORY_TYPES";
|
||||
export const FETCH_REPOSITORY_TYPES_PENDING = `${FETCH_REPOSITORY_TYPES}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const FETCH_REPOSITORY_TYPES_SUCCESS = `${FETCH_REPOSITORY_TYPES}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const FETCH_REPOSITORY_TYPES_FAILURE = `${FETCH_REPOSITORY_TYPES}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const FETCH_REPOSITORY_TYPES_PENDING = `${FETCH_REPOSITORY_TYPES}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_REPOSITORY_TYPES_SUCCESS = `${FETCH_REPOSITORY_TYPES}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_REPOSITORY_TYPES_FAILURE = `${FETCH_REPOSITORY_TYPES}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export function fetchRepositoryTypesIfNeeded() {
|
||||
return function(dispatch: any, getState: () => Object) {
|
||||
return function(dispatch: any, getState: () => object) {
|
||||
if (shouldFetchRepositoryTypes(getState())) {
|
||||
return fetchRepositoryTypes(dispatch);
|
||||
}
|
||||
@@ -42,11 +30,8 @@ function fetchRepositoryTypes(dispatch: any) {
|
||||
});
|
||||
}
|
||||
|
||||
export function shouldFetchRepositoryTypes(state: Object) {
|
||||
if (
|
||||
isFetchRepositoryTypesPending(state) ||
|
||||
getFetchRepositoryTypesFailure(state)
|
||||
) {
|
||||
export function shouldFetchRepositoryTypes(state: object) {
|
||||
if (isFetchRepositoryTypesPending(state) || getFetchRepositoryTypesFailure(state)) {
|
||||
return false;
|
||||
}
|
||||
return !(state.repositoryTypes && state.repositoryTypes.length > 0);
|
||||
@@ -58,9 +43,7 @@ export function fetchRepositoryTypesPending(): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchRepositoryTypesSuccess(
|
||||
repositoryTypes: RepositoryTypeCollection
|
||||
): Action {
|
||||
export function fetchRepositoryTypesSuccess(repositoryTypes: RepositoryTypeCollection): Action {
|
||||
return {
|
||||
type: FETCH_REPOSITORY_TYPES_SUCCESS,
|
||||
payload: repositoryTypes
|
||||
@@ -78,7 +61,9 @@ export function fetchRepositoryTypesFailure(error: Error): Action {
|
||||
|
||||
export default function reducer(
|
||||
state: RepositoryType[] = [],
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): RepositoryType[] {
|
||||
if (action.type === FETCH_REPOSITORY_TYPES_SUCCESS && action.payload) {
|
||||
return action.payload._embedded["repositoryTypes"];
|
||||
@@ -88,17 +73,17 @@ export default function reducer(
|
||||
|
||||
// selectors
|
||||
|
||||
export function getRepositoryTypes(state: Object) {
|
||||
export function getRepositoryTypes(state: object) {
|
||||
if (state.repositoryTypes) {
|
||||
return state.repositoryTypes;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
export function isFetchRepositoryTypesPending(state: Object) {
|
||||
export function isFetchRepositoryTypesPending(state: object) {
|
||||
return isPending(state, FETCH_REPOSITORY_TYPES);
|
||||
}
|
||||
|
||||
export function getFetchRepositoryTypesFailure(state: Object) {
|
||||
export function getFetchRepositoryTypesFailure(state: object) {
|
||||
return getFailure(state, FETCH_REPOSITORY_TYPES);
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Checkbox } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
disabled: boolean,
|
||||
name: string,
|
||||
checked: boolean,
|
||||
onChange?: (value: boolean, name?: string) => void
|
||||
type Props = WithTranslation & {
|
||||
disabled: boolean;
|
||||
name: string;
|
||||
checked: boolean;
|
||||
onChange?: (value: boolean, name?: string) => void;
|
||||
};
|
||||
|
||||
class PermissionCheckbox extends React.Component<Props> {
|
||||
@@ -28,4 +26,4 @@ class PermissionCheckbox extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("plugins")(PermissionCheckbox);
|
||||
export default withTranslation("plugins")(PermissionCheckbox);
|
||||
@@ -1,34 +1,23 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Select } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
availableRoles: string[],
|
||||
handleRoleChange: string => void,
|
||||
role: string,
|
||||
label?: string,
|
||||
helpText?: string,
|
||||
loading?: boolean
|
||||
type Props = WithTranslation & {
|
||||
availableRoles: string[];
|
||||
handleRoleChange: (p: string) => void;
|
||||
role: string;
|
||||
label?: string;
|
||||
helpText?: string;
|
||||
loading?: boolean;
|
||||
};
|
||||
|
||||
class RoleSelector extends React.Component<Props> {
|
||||
render() {
|
||||
const {
|
||||
availableRoles,
|
||||
role,
|
||||
handleRoleChange,
|
||||
loading,
|
||||
label,
|
||||
helpText
|
||||
} = this.props;
|
||||
const { availableRoles, role, handleRoleChange, loading, label, helpText } = this.props;
|
||||
|
||||
if (!availableRoles) return null;
|
||||
|
||||
const options = role
|
||||
? this.createSelectOptions(availableRoles)
|
||||
: ["", ...this.createSelectOptions(availableRoles)];
|
||||
const options = role ? this.createSelectOptions(availableRoles) : ["", ...this.createSelectOptions(availableRoles)];
|
||||
|
||||
return (
|
||||
<Select
|
||||
@@ -52,4 +41,4 @@ class RoleSelector extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(RoleSelector);
|
||||
export default withTranslation("repos")(RoleSelector);
|
||||
@@ -11,18 +11,12 @@ jest.mock("@scm-manager/ui-components", () => ({
|
||||
}));
|
||||
|
||||
describe("DeletePermissionButton", () => {
|
||||
|
||||
it("should render nothing, if the delete link is missing", () => {
|
||||
const permission = {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const navLink = shallow(
|
||||
<DeletePermissionButton
|
||||
permission={permission}
|
||||
deletePermission={() => {}}
|
||||
/>
|
||||
);
|
||||
const navLink = shallow(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
|
||||
expect(navLink.text()).toBe("");
|
||||
});
|
||||
|
||||
@@ -35,12 +29,7 @@ describe("DeletePermissionButton", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteIcon = mount(
|
||||
<DeletePermissionButton
|
||||
permission={permission}
|
||||
deletePermission={() => {}}
|
||||
/>
|
||||
);
|
||||
const deleteIcon = mount(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
|
||||
expect(deleteIcon.html()).not.toBe("");
|
||||
});
|
||||
|
||||
@@ -53,12 +42,7 @@ describe("DeletePermissionButton", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const button = mount(
|
||||
<DeletePermissionButton
|
||||
permission={permission}
|
||||
deletePermission={() => {}}
|
||||
/>
|
||||
);
|
||||
const button = mount(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
|
||||
button.find(".fa-trash").simulate("click");
|
||||
|
||||
expect(confirmAlert.mock.calls.length).toBe(1);
|
||||
@@ -79,11 +63,7 @@ describe("DeletePermissionButton", () => {
|
||||
}
|
||||
|
||||
const button = mount(
|
||||
<DeletePermissionButton
|
||||
permission={permission}
|
||||
confirmDialog={false}
|
||||
deletePermission={capture}
|
||||
/>
|
||||
<DeletePermissionButton permission={permission} confirmDialog={false} deletePermission={capture} />
|
||||
);
|
||||
button.find(".fa-trash").simulate("click");
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { Permission } from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Permission } from "@scm-manager/ui-types";
|
||||
import { confirmAlert } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
confirmDialog?: boolean,
|
||||
t: string => string,
|
||||
deletePermission: (
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) => void,
|
||||
loading: boolean
|
||||
type Props = WithTranslation & {
|
||||
permission: Permission;
|
||||
namespace: string;
|
||||
repoName: string;
|
||||
confirmDialog?: boolean;
|
||||
deletePermission: (permission: Permission, namespace: string, repoName: string) => void;
|
||||
loading: boolean;
|
||||
};
|
||||
|
||||
class DeletePermissionButton extends React.Component<Props> {
|
||||
@@ -24,11 +18,7 @@ class DeletePermissionButton extends React.Component<Props> {
|
||||
};
|
||||
|
||||
deletePermission = () => {
|
||||
this.props.deletePermission(
|
||||
this.props.permission,
|
||||
this.props.namespace,
|
||||
this.props.repoName
|
||||
);
|
||||
this.props.deletePermission(this.props.permission, this.props.namespace, this.props.repoName);
|
||||
};
|
||||
|
||||
confirmDelete = () => {
|
||||
@@ -70,4 +60,4 @@ class DeletePermissionButton extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(DeletePermissionButton);
|
||||
export default withTranslation("repos")(DeletePermissionButton);
|
||||
@@ -1,33 +0,0 @@
|
||||
// @flow
|
||||
import {validation} from "@scm-manager/ui-components";
|
||||
import type {PermissionCollection} from "@scm-manager/ui-types";
|
||||
|
||||
const isNameValid = validation.isNameValid;
|
||||
|
||||
export { isNameValid };
|
||||
|
||||
export const isPermissionValid = (
|
||||
name: string,
|
||||
groupPermission: boolean,
|
||||
permissions: PermissionCollection
|
||||
) => {
|
||||
return (
|
||||
isNameValid(name) &&
|
||||
!currentPermissionIncludeName(name, groupPermission, permissions)
|
||||
);
|
||||
};
|
||||
|
||||
const currentPermissionIncludeName = (
|
||||
name: string,
|
||||
groupPermission: boolean,
|
||||
permissions: PermissionCollection
|
||||
) => {
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
if (
|
||||
permissions[i].name === name &&
|
||||
permissions[i].groupPermission === groupPermission
|
||||
)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
//@flow
|
||||
import * as validator from "./permissionValidation";
|
||||
|
||||
describe("permission validation", () => {
|
||||
@@ -7,9 +6,7 @@ describe("permission validation", () => {
|
||||
const name = "PermissionName";
|
||||
const groupPermission = false;
|
||||
|
||||
expect(
|
||||
validator.isPermissionValid(name, groupPermission, permissions)
|
||||
).toBe(true);
|
||||
expect(validator.isPermissionValid(name, groupPermission, permissions)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return true if permission is valid and does not exists with same group permission", () => {
|
||||
@@ -25,9 +22,7 @@ describe("permission validation", () => {
|
||||
const name = "PermissionName";
|
||||
const groupPermission = false;
|
||||
|
||||
expect(
|
||||
validator.isPermissionValid(name, groupPermission, permissions)
|
||||
).toBe(true);
|
||||
expect(validator.isPermissionValid(name, groupPermission, permissions)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if permission is valid but exists", () => {
|
||||
@@ -43,9 +38,7 @@ describe("permission validation", () => {
|
||||
const name = "PermissionName";
|
||||
const groupPermission = false;
|
||||
|
||||
expect(
|
||||
validator.isPermissionValid(name, groupPermission, permissions)
|
||||
).toBe(false);
|
||||
expect(validator.isPermissionValid(name, groupPermission, permissions)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if permission does not exist but is invalid", () => {
|
||||
@@ -53,9 +46,7 @@ describe("permission validation", () => {
|
||||
const name = "@PermissionName";
|
||||
const groupPermission = false;
|
||||
|
||||
expect(
|
||||
validator.isPermissionValid(name, groupPermission, permissions)
|
||||
).toBe(false);
|
||||
expect(validator.isPermissionValid(name, groupPermission, permissions)).toBe(false);
|
||||
});
|
||||
|
||||
it("should return false if permission is not valid and does not exist", () => {
|
||||
@@ -63,8 +54,6 @@ describe("permission validation", () => {
|
||||
const name = "@PermissionName";
|
||||
const groupPermission = false;
|
||||
|
||||
expect(
|
||||
validator.isPermissionValid(name, groupPermission, permissions)
|
||||
).toBe(false);
|
||||
expect(validator.isPermissionValid(name, groupPermission, permissions)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { validation } from "@scm-manager/ui-components";
|
||||
import { PermissionCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const isNameValid = validation.isNameValid;
|
||||
|
||||
export { isNameValid };
|
||||
|
||||
export const isPermissionValid = (name: string, groupPermission: boolean, permissions: PermissionCollection) => {
|
||||
return isNameValid(name) && !currentPermissionIncludeName(name, groupPermission, permissions);
|
||||
};
|
||||
|
||||
const currentPermissionIncludeName = (name: string, groupPermission: boolean, permissions: PermissionCollection) => {
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
if (permissions[i].name === name && permissions[i].groupPermission === groupPermission) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -1,27 +1,18 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import {
|
||||
ButtonGroup,
|
||||
Button,
|
||||
SubmitButton,
|
||||
Modal
|
||||
} from "@scm-manager/ui-components";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ButtonGroup, Button, SubmitButton, Modal } from "@scm-manager/ui-components";
|
||||
import PermissionCheckbox from "../components/PermissionCheckbox";
|
||||
|
||||
type Props = {
|
||||
readOnly: boolean,
|
||||
availableVerbs: string[],
|
||||
selectedVerbs: string[],
|
||||
onSubmit: (string[]) => void,
|
||||
onClose: () => void,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
readOnly: boolean;
|
||||
availableVerbs: string[];
|
||||
selectedVerbs: string[];
|
||||
onSubmit: (p: string[]) => void;
|
||||
onClose: () => void;
|
||||
};
|
||||
|
||||
type State = {
|
||||
verbs: any
|
||||
verbs: any;
|
||||
};
|
||||
|
||||
class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
@@ -30,12 +21,11 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
|
||||
const verbs = {};
|
||||
props.availableVerbs.forEach(
|
||||
verb =>
|
||||
(verbs[verb] = props.selectedVerbs
|
||||
? props.selectedVerbs.includes(verb)
|
||||
: false)
|
||||
verb => (verbs[verb] = props.selectedVerbs ? props.selectedVerbs.includes(verb) : false)
|
||||
);
|
||||
this.state = { verbs };
|
||||
this.state = {
|
||||
verbs
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -43,18 +33,10 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
const { verbs } = this.state;
|
||||
|
||||
const verbSelectBoxes = Object.entries(verbs).map(e => (
|
||||
<PermissionCheckbox
|
||||
key={e[0]}
|
||||
disabled={readOnly}
|
||||
name={e[0]}
|
||||
checked={e[1]}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<PermissionCheckbox key={e[0]} disabled={readOnly} name={e[0]} checked={e[1]} onChange={this.handleChange} />
|
||||
));
|
||||
|
||||
const submitButton = !readOnly ? (
|
||||
<SubmitButton label={t("permission.advanced.dialog.submit")} />
|
||||
) : null;
|
||||
const submitButton = !readOnly ? <SubmitButton label={t("permission.advanced.dialog.submit")} /> : null;
|
||||
|
||||
const body = <>{verbSelectBoxes}</>;
|
||||
|
||||
@@ -62,10 +44,7 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<ButtonGroup>
|
||||
{submitButton}
|
||||
<Button
|
||||
label={t("permission.advanced.dialog.abort")}
|
||||
action={onClose}
|
||||
/>
|
||||
<Button label={t("permission.advanced.dialog.abort")} action={onClose} />
|
||||
</ButtonGroup>
|
||||
</form>
|
||||
);
|
||||
@@ -83,8 +62,13 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
|
||||
handleChange = (value: boolean, name: string) => {
|
||||
const { verbs } = this.state;
|
||||
const newVerbs = { ...verbs, [name]: value };
|
||||
this.setState({ verbs: newVerbs });
|
||||
const newVerbs = {
|
||||
...verbs,
|
||||
[name]: value
|
||||
};
|
||||
this.setState({
|
||||
verbs: newVerbs
|
||||
});
|
||||
};
|
||||
|
||||
onSubmit = () => {
|
||||
@@ -96,4 +80,4 @@ class AdvancedPermissionsDialog extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(AdvancedPermissionsDialog);
|
||||
export default withTranslation("repos")(AdvancedPermissionsDialog);
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import {translate} from "react-i18next";
|
||||
import type {PermissionCollection, PermissionCreateEntry, RepositoryRole, SelectValue} from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { PermissionCollection, PermissionCreateEntry, RepositoryRole, SelectValue } from "@scm-manager/ui-types";
|
||||
import {
|
||||
Button,
|
||||
GroupAutocomplete,
|
||||
@@ -14,29 +13,26 @@ import {
|
||||
import * as validator from "../components/permissionValidation";
|
||||
import RoleSelector from "../components/RoleSelector";
|
||||
import AdvancedPermissionsDialog from "./AdvancedPermissionsDialog";
|
||||
import {findVerbsForRole} from "../modules/permissions";
|
||||
import { findVerbsForRole } from "../modules/permissions";
|
||||
|
||||
type Props = {
|
||||
availableRoles: RepositoryRole[],
|
||||
availableVerbs: string[],
|
||||
createPermission: (permission: PermissionCreateEntry) => void,
|
||||
loading: boolean,
|
||||
currentPermissions: PermissionCollection,
|
||||
groupAutocompleteLink: string,
|
||||
userAutocompleteLink: string,
|
||||
|
||||
// Context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
availableRoles: RepositoryRole[];
|
||||
availableVerbs: string[];
|
||||
createPermission: (permission: PermissionCreateEntry) => void;
|
||||
loading: boolean;
|
||||
currentPermissions: PermissionCollection;
|
||||
groupAutocompleteLink: string;
|
||||
userAutocompleteLink: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
name: string,
|
||||
role?: string,
|
||||
verbs?: string[],
|
||||
groupPermission: boolean,
|
||||
valid: boolean,
|
||||
value?: SelectValue,
|
||||
showAdvancedDialog: boolean
|
||||
name: string;
|
||||
role?: string;
|
||||
verbs?: string[];
|
||||
groupPermission: boolean;
|
||||
valid: boolean;
|
||||
value?: SelectValue;
|
||||
showAdvancedDialog: boolean;
|
||||
};
|
||||
|
||||
class CreatePermissionForm extends React.Component<Props, State> {
|
||||
@@ -54,12 +50,23 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
permissionScopeChanged = event => {
|
||||
const groupPermission = event.target.value === "GROUP_PERMISSION";
|
||||
groupPermissionScopeChanged = (value: boolean) => {
|
||||
if (value) {
|
||||
this.permissionScopeChanged(true);
|
||||
}
|
||||
};
|
||||
|
||||
userPermissionScopeChanged = (value: boolean) => {
|
||||
if (value) {
|
||||
this.permissionScopeChanged(false);
|
||||
}
|
||||
};
|
||||
|
||||
permissionScopeChanged = (groupPermission: boolean) => {
|
||||
this.setState({
|
||||
value: undefined,
|
||||
name: "",
|
||||
groupPermission: groupPermission,
|
||||
groupPermission,
|
||||
valid: false
|
||||
});
|
||||
};
|
||||
@@ -88,11 +95,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
this.setState({
|
||||
value,
|
||||
name: value.value.id,
|
||||
valid: validator.isPermissionValid(
|
||||
value.value.id,
|
||||
this.state.groupPermission,
|
||||
this.props.currentPermissions
|
||||
)
|
||||
valid: validator.isPermissionValid(value.value.id, this.state.groupPermission, this.props.currentPermissions)
|
||||
});
|
||||
};
|
||||
|
||||
@@ -116,9 +119,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
return (
|
||||
<>
|
||||
<hr />
|
||||
<Subtitle
|
||||
subtitle={t("permission.add-permission.add-permission-heading")}
|
||||
/>
|
||||
<Subtitle subtitle={t("permission.add-permission.add-permission-heading")} />
|
||||
{advancedDialog}
|
||||
<form onSubmit={this.submit}>
|
||||
<div className="field is-grouped">
|
||||
@@ -128,21 +129,19 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
value="USER_PERMISSION"
|
||||
checked={!this.state.groupPermission}
|
||||
label={t("permission.user-permission")}
|
||||
onChange={this.permissionScopeChanged}
|
||||
onChange={this.userPermissionScopeChanged}
|
||||
/>
|
||||
<Radio
|
||||
name="permission_scope"
|
||||
value="GROUP_PERMISSION"
|
||||
checked={this.state.groupPermission}
|
||||
label={t("permission.group-permission")}
|
||||
onChange={this.permissionScopeChanged}
|
||||
onChange={this.groupPermissionScopeChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column is-three-fifths">
|
||||
{this.renderAutocompletionField()}
|
||||
</div>
|
||||
<div className="column is-three-fifths">{this.renderAutocompletionField()}</div>
|
||||
<div className="column is-two-fifths">
|
||||
<div className="columns">
|
||||
<div className="column is-narrow">
|
||||
@@ -159,10 +158,7 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
label={t("permission.permissions")}
|
||||
helpText={t("permission.help.permissionsHelpText")}
|
||||
/>
|
||||
<Button
|
||||
label={t("permission.advanced-button.label")}
|
||||
action={this.toggleAdvancedPermissionsDialog}
|
||||
/>
|
||||
<Button label={t("permission.advanced-button.label")} action={this.toggleAdvancedPermissionsDialog} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -232,4 +228,4 @@ class CreatePermissionForm extends React.Component<Props, State> {
|
||||
};
|
||||
}
|
||||
|
||||
export default translate("repos")(CreatePermissionForm);
|
||||
export default withTranslation("repos")(CreatePermissionForm);
|
||||
@@ -1,7 +1,6 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {connect} from "react-redux";
|
||||
import {translate} from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import {
|
||||
createPermission,
|
||||
createPermissionReset,
|
||||
@@ -23,12 +22,12 @@ import {
|
||||
isFetchPermissionsPending,
|
||||
modifyPermissionReset
|
||||
} from "../modules/permissions";
|
||||
import {ErrorPage, LabelWithHelpIcon, Loading, Subtitle} from "@scm-manager/ui-components";
|
||||
import type {Permission, PermissionCollection, PermissionCreateEntry, RepositoryRole} from "@scm-manager/ui-types";
|
||||
import { ErrorPage, LabelWithHelpIcon, Loading, Subtitle } from "@scm-manager/ui-components";
|
||||
import { Permission, PermissionCollection, PermissionCreateEntry, RepositoryRole } from "@scm-manager/ui-types";
|
||||
import SinglePermission from "./SinglePermission";
|
||||
import CreatePermissionForm from "./CreatePermissionForm";
|
||||
import type {History} from "history";
|
||||
import {getPermissionsLink} from "../../modules/repos";
|
||||
import { History } from "history";
|
||||
import { getPermissionsLink } from "../../modules/repos";
|
||||
import {
|
||||
getGroupAutoCompleteLink,
|
||||
getRepositoryRolesLink,
|
||||
@@ -36,43 +35,40 @@ import {
|
||||
getUserAutoCompleteLink
|
||||
} from "../../../modules/indexResource";
|
||||
|
||||
type Props = {
|
||||
availablePermissions: boolean,
|
||||
availableRepositoryRoles: RepositoryRole[],
|
||||
availableVerbs: string[],
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
permissions: PermissionCollection,
|
||||
hasPermissionToCreate: boolean,
|
||||
loadingCreatePermission: boolean,
|
||||
repositoryRolesLink: string,
|
||||
repositoryVerbsLink: string,
|
||||
permissionsLink: string,
|
||||
groupAutocompleteLink: string,
|
||||
userAutocompleteLink: string,
|
||||
type Props = WithTranslation & {
|
||||
availablePermissions: boolean;
|
||||
availableRepositoryRoles: RepositoryRole[];
|
||||
availableVerbs: string[];
|
||||
namespace: string;
|
||||
repoName: string;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
permissions: PermissionCollection;
|
||||
hasPermissionToCreate: boolean;
|
||||
loadingCreatePermission: boolean;
|
||||
repositoryRolesLink: string;
|
||||
repositoryVerbsLink: string;
|
||||
permissionsLink: string;
|
||||
groupAutocompleteLink: string;
|
||||
userAutocompleteLink: string;
|
||||
|
||||
//dispatch functions
|
||||
fetchAvailablePermissionsIfNeeded: (
|
||||
repositoryRolesLink: string,
|
||||
repositoryVerbsLink: string
|
||||
) => void,
|
||||
fetchPermissions: (link: string, namespace: string, repoName: string) => void,
|
||||
// dispatch functions
|
||||
fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => void;
|
||||
fetchPermissions: (link: string, namespace: string, repoName: string) => void;
|
||||
createPermission: (
|
||||
link: string,
|
||||
permission: PermissionCreateEntry,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
callback?: () => void
|
||||
) => void,
|
||||
createPermissionReset: (string, string) => void,
|
||||
modifyPermissionReset: (string, string) => void,
|
||||
deletePermissionReset: (string, string) => void,
|
||||
) => void;
|
||||
createPermissionReset: (p1: string, p2: string) => void;
|
||||
modifyPermissionReset: (p1: string, p2: string) => void;
|
||||
deletePermissionReset: (p1: string, p2: string) => void;
|
||||
|
||||
// context props
|
||||
t: string => string,
|
||||
match: any,
|
||||
history: History
|
||||
match: any;
|
||||
history: History;
|
||||
};
|
||||
|
||||
class Permissions extends React.Component<Props> {
|
||||
@@ -98,12 +94,7 @@ class Permissions extends React.Component<Props> {
|
||||
}
|
||||
|
||||
createPermission = (permission: Permission) => {
|
||||
this.props.createPermission(
|
||||
this.props.permissionsLink,
|
||||
permission,
|
||||
this.props.namespace,
|
||||
this.props.repoName
|
||||
);
|
||||
this.props.createPermission(this.props.permissionsLink, permission, this.props.namespace, this.props.repoName);
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -123,13 +114,7 @@ class Permissions extends React.Component<Props> {
|
||||
groupAutocompleteLink
|
||||
} = this.props;
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPage
|
||||
title={t("permission.error-title")}
|
||||
subtitle={t("permission.error-subtitle")}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
return <ErrorPage title={t("permission.error-title")} subtitle={t("permission.error-subtitle")} error={error} />;
|
||||
}
|
||||
|
||||
if (loading || !permissions || !availablePermissions) {
|
||||
@@ -155,16 +140,10 @@ class Permissions extends React.Component<Props> {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<LabelWithHelpIcon
|
||||
label={t("permission.name")}
|
||||
helpText={t("permission.help.nameHelpText")}
|
||||
/>
|
||||
<LabelWithHelpIcon label={t("permission.name")} helpText={t("permission.help.nameHelpText")} />
|
||||
</th>
|
||||
<th>
|
||||
<LabelWithHelpIcon
|
||||
label={t("permission.role")}
|
||||
helpText={t("permission.help.roleHelpText")}
|
||||
/>
|
||||
<LabelWithHelpIcon label={t("permission.role")} helpText={t("permission.help.roleHelpText")} />
|
||||
</th>
|
||||
<th>
|
||||
<LabelWithHelpIcon
|
||||
@@ -205,15 +184,9 @@ const mapStateToProps = (state, ownProps) => {
|
||||
getDeletePermissionsFailure(state, namespace, repoName) ||
|
||||
getModifyPermissionsFailure(state, namespace, repoName) ||
|
||||
getFetchAvailablePermissionsFailure(state);
|
||||
const loading =
|
||||
isFetchPermissionsPending(state, namespace, repoName) ||
|
||||
isFetchAvailablePermissionsPending(state);
|
||||
const loading = isFetchPermissionsPending(state, namespace, repoName) || isFetchAvailablePermissionsPending(state);
|
||||
const permissions = getPermissionsOfRepo(state, namespace, repoName);
|
||||
const loadingCreatePermission = isCreatePermissionPending(
|
||||
state,
|
||||
namespace,
|
||||
repoName
|
||||
);
|
||||
const loadingCreatePermission = isCreatePermissionPending(state, namespace, repoName);
|
||||
const hasPermissionToCreate = hasCreatePermission(state, namespace, repoName);
|
||||
const repositoryRolesLink = getRepositoryRolesLink(state);
|
||||
const repositoryVerbsLink = getRepositoryVerbsLink(state);
|
||||
@@ -248,16 +221,8 @@ const mapDispatchToProps = dispatch => {
|
||||
fetchPermissions: (link: string, namespace: string, repoName: string) => {
|
||||
dispatch(fetchPermissions(link, namespace, repoName));
|
||||
},
|
||||
fetchAvailablePermissionsIfNeeded: (
|
||||
repositoryRolesLink: string,
|
||||
repositoryVerbsLink: string
|
||||
) => {
|
||||
dispatch(
|
||||
fetchAvailablePermissionsIfNeeded(
|
||||
repositoryRolesLink,
|
||||
repositoryVerbsLink
|
||||
)
|
||||
);
|
||||
fetchAvailablePermissionsIfNeeded: (repositoryRolesLink: string, repositoryVerbsLink: string) => {
|
||||
dispatch(fetchAvailablePermissionsIfNeeded(repositoryRolesLink, repositoryVerbsLink));
|
||||
},
|
||||
createPermission: (
|
||||
link: string,
|
||||
@@ -266,9 +231,7 @@ const mapDispatchToProps = dispatch => {
|
||||
repoName: string,
|
||||
callback?: () => void
|
||||
) => {
|
||||
dispatch(
|
||||
createPermission(link, permission, namespace, repoName, callback)
|
||||
);
|
||||
dispatch(createPermission(link, permission, namespace, repoName, callback));
|
||||
},
|
||||
createPermissionReset: (namespace: string, repoName: string) => {
|
||||
dispatch(createPermissionReset(namespace, repoName));
|
||||
@@ -285,4 +248,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(Permissions));
|
||||
)(withTranslation("repos")(Permissions));
|
||||
@@ -1,10 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import type { History } from "history";
|
||||
import { translate } from "react-i18next";
|
||||
import { History } from "history";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import type { RepositoryRole, Permission } from "@scm-manager/ui-types";
|
||||
import { RepositoryRole, Permission } from "@scm-manager/ui-types";
|
||||
import { Button, Icon } from "@scm-manager/ui-components";
|
||||
import {
|
||||
modifyPermission,
|
||||
@@ -17,33 +16,24 @@ import DeletePermissionButton from "../components/buttons/DeletePermissionButton
|
||||
import RoleSelector from "../components/RoleSelector";
|
||||
import AdvancedPermissionsDialog from "./AdvancedPermissionsDialog";
|
||||
|
||||
type Props = {
|
||||
availableRepositoryRoles: RepositoryRole[],
|
||||
availableRepositoryVerbs: string[],
|
||||
submitForm: Permission => void,
|
||||
modifyPermission: (
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
name: string
|
||||
) => void,
|
||||
permission: Permission,
|
||||
t: string => string,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
match: any,
|
||||
history: History,
|
||||
loading: boolean,
|
||||
deletePermission: (
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
name: string
|
||||
) => void,
|
||||
deleteLoading: boolean
|
||||
type Props = WithTranslation & {
|
||||
availableRepositoryRoles: RepositoryRole[];
|
||||
availableRepositoryVerbs: string[];
|
||||
submitForm: (p: Permission) => void;
|
||||
modifyPermission: (permission: Permission, namespace: string, name: string) => void;
|
||||
permission: Permission;
|
||||
namespace: string;
|
||||
repoName: string;
|
||||
match: any;
|
||||
history: History;
|
||||
loading: boolean;
|
||||
deletePermission: (permission: Permission, namespace: string, name: string) => void;
|
||||
deleteLoading: boolean;
|
||||
};
|
||||
|
||||
type State = {
|
||||
permission: Permission,
|
||||
showAdvancedDialog: boolean
|
||||
permission: Permission;
|
||||
showAdvancedDialog: boolean;
|
||||
};
|
||||
|
||||
const FullWidthTr = styled.tr`
|
||||
@@ -59,9 +49,7 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
const defaultPermission = props.availableRepositoryRoles
|
||||
? props.availableRepositoryRoles[0]
|
||||
: {};
|
||||
const defaultPermission = props.availableRepositoryRoles ? props.availableRepositoryRoles[0] : {};
|
||||
|
||||
this.state = {
|
||||
permission: {
|
||||
@@ -92,25 +80,13 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
deletePermission = () => {
|
||||
this.props.deletePermission(
|
||||
this.props.permission,
|
||||
this.props.namespace,
|
||||
this.props.repoName
|
||||
);
|
||||
this.props.deletePermission(this.props.permission, this.props.namespace, this.props.repoName);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
availableRepositoryRoles,
|
||||
availableRepositoryVerbs,
|
||||
loading,
|
||||
namespace,
|
||||
repoName,
|
||||
t
|
||||
} = this.props;
|
||||
const { availableRepositoryRoles, availableRepositoryVerbs, loading, namespace, repoName, t } = this.props;
|
||||
const { permission, showAdvancedDialog } = this.state;
|
||||
const availableRoleNames =
|
||||
!!availableRepositoryRoles && availableRepositoryRoles.map(r => r.name);
|
||||
const availableRoleNames = !!availableRepositoryRoles && availableRepositoryRoles.map(r => r.name);
|
||||
const readOnly = !this.mayChangePermissions();
|
||||
const roleSelector = readOnly ? (
|
||||
<td>{permission.role ? permission.role : t("permission.custom")}</td>
|
||||
@@ -153,10 +129,7 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
</VCenteredTd>
|
||||
{roleSelector}
|
||||
<VCenteredTd>
|
||||
<Button
|
||||
label={t("permission.advanced-button.label")}
|
||||
action={this.handleDetailedPermissionsPressed}
|
||||
/>
|
||||
<Button label={t("permission.advanced-button.label")} action={this.handleDetailedPermissionsPressed} />
|
||||
</VCenteredTd>
|
||||
<VCenteredTd className="is-darker">
|
||||
<DeletePermissionButton
|
||||
@@ -177,11 +150,15 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
handleDetailedPermissionsPressed = () => {
|
||||
this.setState({ showAdvancedDialog: true });
|
||||
this.setState({
|
||||
showAdvancedDialog: true
|
||||
});
|
||||
};
|
||||
|
||||
closeAdvancedPermissionsDialog = () => {
|
||||
this.setState({ showAdvancedDialog: false });
|
||||
this.setState({
|
||||
showAdvancedDialog: false
|
||||
});
|
||||
};
|
||||
|
||||
submitAdvancedPermissionsDialog = (newVerbs: string[]) => {
|
||||
@@ -189,7 +166,11 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
this.setState(
|
||||
{
|
||||
showAdvancedDialog: false,
|
||||
permission: { ...permission, role: undefined, verbs: newVerbs }
|
||||
permission: {
|
||||
...permission,
|
||||
role: undefined,
|
||||
verbs: newVerbs
|
||||
}
|
||||
},
|
||||
() => this.modifyPermissionVerbs(newVerbs)
|
||||
);
|
||||
@@ -199,7 +180,11 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
const { permission } = this.state;
|
||||
this.setState(
|
||||
{
|
||||
permission: { ...permission, role: role, verbs: undefined }
|
||||
permission: {
|
||||
...permission,
|
||||
role: role,
|
||||
verbs: undefined
|
||||
}
|
||||
},
|
||||
() => this.modifyPermissionRole(role)
|
||||
);
|
||||
@@ -211,58 +196,35 @@ class SinglePermission extends React.Component<Props, State> {
|
||||
};
|
||||
|
||||
modifyPermissionRole = (role: string) => {
|
||||
let permission = this.state.permission;
|
||||
const permission = this.state.permission;
|
||||
permission.role = role;
|
||||
this.props.modifyPermission(
|
||||
permission,
|
||||
this.props.namespace,
|
||||
this.props.repoName
|
||||
);
|
||||
this.props.modifyPermission(permission, this.props.namespace, this.props.repoName);
|
||||
};
|
||||
|
||||
modifyPermissionVerbs = (verbs: string[]) => {
|
||||
let permission = this.state.permission;
|
||||
const permission = this.state.permission;
|
||||
permission.verbs = verbs;
|
||||
this.props.modifyPermission(
|
||||
permission,
|
||||
this.props.namespace,
|
||||
this.props.repoName
|
||||
);
|
||||
this.props.modifyPermission(permission, this.props.namespace, this.props.repoName);
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const permission = ownProps.permission;
|
||||
const loading = isModifyPermissionPending(
|
||||
state,
|
||||
ownProps.namespace,
|
||||
ownProps.repoName,
|
||||
permission
|
||||
);
|
||||
const deleteLoading = isDeletePermissionPending(
|
||||
state,
|
||||
ownProps.namespace,
|
||||
ownProps.repoName,
|
||||
permission
|
||||
);
|
||||
const loading = isModifyPermissionPending(state, ownProps.namespace, ownProps.repoName, permission);
|
||||
const deleteLoading = isDeletePermissionPending(state, ownProps.namespace, ownProps.repoName, permission);
|
||||
|
||||
return { loading, deleteLoading };
|
||||
return {
|
||||
loading,
|
||||
deleteLoading
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
modifyPermission: (
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) => {
|
||||
modifyPermission: (permission: Permission, namespace: string, repoName: string) => {
|
||||
dispatch(modifyPermission(permission, namespace, repoName));
|
||||
},
|
||||
deletePermission: (
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) => {
|
||||
deletePermission: (permission: Permission, namespace: string, repoName: string) => {
|
||||
dispatch(deletePermission(permission, namespace, repoName));
|
||||
}
|
||||
};
|
||||
@@ -270,4 +232,4 @@ const mapDispatchToProps = dispatch => {
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(translate("repos")(SinglePermission));
|
||||
)(withTranslation("repos")(SinglePermission));
|
||||
@@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
@@ -40,7 +39,7 @@ import reducer, {
|
||||
modifyPermission,
|
||||
modifyPermissionSuccess
|
||||
} from "./permissions";
|
||||
import type {Permission, PermissionCollection} from "@scm-manager/ui-types";
|
||||
import { Permission, PermissionCollection } from "@scm-manager/ui-types";
|
||||
|
||||
const hitchhiker_puzzle42Permission_user_eins: Permission = {
|
||||
name: "user_eins",
|
||||
@@ -48,16 +47,13 @@ const hitchhiker_puzzle42Permission_user_eins: Permission = {
|
||||
groupPermission: false,
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
},
|
||||
delete: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
},
|
||||
update: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_eins"
|
||||
}
|
||||
},
|
||||
verbs: []
|
||||
@@ -69,16 +65,13 @@ const hitchhiker_puzzle42Permission_user_zwei: Permission = {
|
||||
groupPermission: true,
|
||||
_links: {
|
||||
self: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
},
|
||||
delete: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
},
|
||||
update: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions/user_zwei"
|
||||
}
|
||||
},
|
||||
verbs: []
|
||||
@@ -95,8 +88,7 @@ const hitchhiker_puzzle42RepoPermissions = {
|
||||
},
|
||||
_links: {
|
||||
create: {
|
||||
href:
|
||||
"http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions"
|
||||
href: "http://localhost:8081/scm/api/rest/v2/repositories/hitchhiker/puzzle42/permissions"
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -112,10 +104,7 @@ describe("permission fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully fetch permissions to repo hitchhiker/puzzle42", () => {
|
||||
fetchMock.getOnce(
|
||||
REPOS_URL + "/hitchhiker/puzzle42/permissions",
|
||||
hitchhiker_puzzle42RepoPermissions
|
||||
);
|
||||
fetchMock.getOnce(REPOS_URL + "/hitchhiker/puzzle42/permissions", hitchhiker_puzzle42RepoPermissions);
|
||||
|
||||
const expectedActions = [
|
||||
{
|
||||
@@ -135,13 +124,7 @@ describe("permission fetch", () => {
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
fetchPermissions(
|
||||
URL + "/hitchhiker/puzzle42/permissions",
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
)
|
||||
.dispatch(fetchPermissions(URL + "/hitchhiker/puzzle42/permissions", "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
expect(store.getActions()).toEqual(expectedActions);
|
||||
});
|
||||
@@ -154,13 +137,7 @@ describe("permission fetch", () => {
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
fetchPermissions(
|
||||
URL + "/hitchhiker/puzzle42/permissions",
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
)
|
||||
.dispatch(fetchPermissions(URL + "/hitchhiker/puzzle42/permissions", "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(FETCH_PERMISSIONS_PENDING);
|
||||
@@ -170,35 +147,33 @@ describe("permission fetch", () => {
|
||||
});
|
||||
|
||||
it("should successfully modify user_eins permission", () => {
|
||||
fetchMock.putOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.update.href,
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.putOnce(hitchhiker_puzzle42Permission_user_eins._links.update.href, {
|
||||
status: 204
|
||||
});
|
||||
|
||||
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins, type: "OWNER" };
|
||||
const editedPermission = {
|
||||
...hitchhiker_puzzle42Permission_user_eins,
|
||||
type: "OWNER"
|
||||
};
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
return store
|
||||
.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
|
||||
});
|
||||
return store.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42")).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should successfully modify user_eins permission and call the callback", () => {
|
||||
fetchMock.putOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.update.href,
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.putOnce(hitchhiker_puzzle42Permission_user_eins._links.update.href, {
|
||||
status: 204
|
||||
});
|
||||
|
||||
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins, type: "OWNER" };
|
||||
const editedPermission = {
|
||||
...hitchhiker_puzzle42Permission_user_eins,
|
||||
type: "OWNER"
|
||||
};
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
@@ -207,38 +182,32 @@ describe("permission fetch", () => {
|
||||
called = true;
|
||||
};
|
||||
|
||||
return store
|
||||
.dispatch(
|
||||
modifyPermission(editedPermission, "hitchhiker", "puzzle42", callback)
|
||||
)
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
return store.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42", callback)).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_SUCCESS);
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail modifying on HTTP 500", () => {
|
||||
fetchMock.putOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.update.href,
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.putOnce(hitchhiker_puzzle42Permission_user_eins._links.update.href, {
|
||||
status: 500
|
||||
});
|
||||
|
||||
let editedPermission = { ...hitchhiker_puzzle42Permission_user_eins, type: "OWNER" };
|
||||
const editedPermission = {
|
||||
...hitchhiker_puzzle42Permission_user_eins,
|
||||
type: "OWNER"
|
||||
};
|
||||
|
||||
const store = mockStore({});
|
||||
|
||||
return store
|
||||
.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
return store.dispatch(modifyPermission(editedPermission, "hitchhiker", "puzzle42")).then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(MODIFY_PERMISSION_PENDING);
|
||||
expect(actions[1].type).toEqual(MODIFY_PERMISSION_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it("should add a permission successfully", () => {
|
||||
@@ -330,40 +299,26 @@ describe("permission fetch", () => {
|
||||
});
|
||||
});
|
||||
it("should delete successfully permission user_eins", () => {
|
||||
fetchMock.deleteOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.delete.href,
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.deleteOnce(hitchhiker_puzzle42Permission_user_eins._links.delete.href, {
|
||||
status: 204
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
deletePermission(
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
)
|
||||
.dispatch(deletePermission(hitchhiker_puzzle42Permission_user_eins, "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions.length).toBe(2);
|
||||
expect(actions[0].type).toEqual(DELETE_PERMISSION_PENDING);
|
||||
expect(actions[0].payload).toBe(
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
);
|
||||
expect(actions[0].payload).toBe(hitchhiker_puzzle42Permission_user_eins);
|
||||
expect(actions[1].type).toEqual(DELETE_PERMISSION_SUCCESS);
|
||||
});
|
||||
});
|
||||
|
||||
it("should call the callback, after successful delete", () => {
|
||||
fetchMock.deleteOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.delete.href,
|
||||
{
|
||||
status: 204
|
||||
}
|
||||
);
|
||||
fetchMock.deleteOnce(hitchhiker_puzzle42Permission_user_eins._links.delete.href, {
|
||||
status: 204
|
||||
});
|
||||
|
||||
let called = false;
|
||||
const callMe = () => {
|
||||
@@ -372,42 +327,24 @@ describe("permission fetch", () => {
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
deletePermission(
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
callMe
|
||||
)
|
||||
)
|
||||
.dispatch(deletePermission(hitchhiker_puzzle42Permission_user_eins, "hitchhiker", "puzzle42", callMe))
|
||||
.then(() => {
|
||||
expect(called).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it("should fail to delete permission", () => {
|
||||
fetchMock.deleteOnce(
|
||||
hitchhiker_puzzle42Permission_user_eins._links.delete.href,
|
||||
{
|
||||
status: 500
|
||||
}
|
||||
);
|
||||
fetchMock.deleteOnce(hitchhiker_puzzle42Permission_user_eins._links.delete.href, {
|
||||
status: 500
|
||||
});
|
||||
|
||||
const store = mockStore({});
|
||||
return store
|
||||
.dispatch(
|
||||
deletePermission(
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
)
|
||||
.dispatch(deletePermission(hitchhiker_puzzle42Permission_user_eins, "hitchhiker", "puzzle42"))
|
||||
.then(() => {
|
||||
const actions = store.getActions();
|
||||
expect(actions[0].type).toEqual(DELETE_PERMISSION_PENDING);
|
||||
expect(actions[0].payload).toBe(
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
);
|
||||
expect(actions[0].payload).toBe(hitchhiker_puzzle42Permission_user_eins);
|
||||
expect(actions[1].type).toEqual(DELETE_PERMISSION_FAILURE);
|
||||
expect(actions[1].payload).toBeDefined();
|
||||
});
|
||||
@@ -420,28 +357,27 @@ describe("permissions reducer", () => {
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is undefined", () => {
|
||||
const state = { x: true };
|
||||
const state = {
|
||||
x: true
|
||||
};
|
||||
expect(reducer(state)).toBe(state);
|
||||
});
|
||||
|
||||
it("should return the same state, if the action is unknown to the reducer", () => {
|
||||
const state = { x: true };
|
||||
expect(reducer(state, { type: "EL_SPECIALE" })).toBe(state);
|
||||
const state = {
|
||||
x: true
|
||||
};
|
||||
expect(
|
||||
reducer(state, {
|
||||
type: "EL_SPECIALE"
|
||||
})
|
||||
).toBe(state);
|
||||
});
|
||||
|
||||
it("should store the permissions on FETCH_PERMISSION_SUCCESS", () => {
|
||||
const newState = reducer(
|
||||
{},
|
||||
fetchPermissionsSuccess(
|
||||
hitchhiker_puzzle42RepoPermissions,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
);
|
||||
const newState = reducer({}, fetchPermissionsSuccess(hitchhiker_puzzle42RepoPermissions, "hitchhiker", "puzzle42"));
|
||||
|
||||
expect(newState["hitchhiker/puzzle42"].entries).toBe(
|
||||
hitchhiker_puzzle42Permissions
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"].entries).toBe(hitchhiker_puzzle42Permissions);
|
||||
});
|
||||
|
||||
it("should update permission", () => {
|
||||
@@ -450,28 +386,23 @@ describe("permissions reducer", () => {
|
||||
entries: [hitchhiker_puzzle42Permission_user_eins]
|
||||
}
|
||||
};
|
||||
let permissionEdited = { ...hitchhiker_puzzle42Permission_user_eins, type: "OWNER" };
|
||||
let expectedState = {
|
||||
const permissionEdited = {
|
||||
...hitchhiker_puzzle42Permission_user_eins,
|
||||
type: "OWNER"
|
||||
};
|
||||
const expectedState = {
|
||||
"hitchhiker/puzzle42": {
|
||||
entries: [permissionEdited]
|
||||
}
|
||||
};
|
||||
const newState = reducer(
|
||||
oldState,
|
||||
modifyPermissionSuccess(permissionEdited, "hitchhiker", "puzzle42")
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(
|
||||
expectedState["hitchhiker/puzzle42"]
|
||||
);
|
||||
const newState = reducer(oldState, modifyPermissionSuccess(permissionEdited, "hitchhiker", "puzzle42"));
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(expectedState["hitchhiker/puzzle42"]);
|
||||
});
|
||||
|
||||
it("should remove permission from state when delete succeeds", () => {
|
||||
const state = {
|
||||
"hitchhiker/puzzle42": {
|
||||
entries: [
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
hitchhiker_puzzle42Permission_user_zwei
|
||||
]
|
||||
entries: [hitchhiker_puzzle42Permission_user_eins, hitchhiker_puzzle42Permission_user_zwei]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -483,15 +414,9 @@ describe("permissions reducer", () => {
|
||||
|
||||
const newState = reducer(
|
||||
state,
|
||||
deletePermissionSuccess(
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(
|
||||
expectedState["hitchhiker/puzzle42"]
|
||||
deletePermissionSuccess(hitchhiker_puzzle42Permission_user_eins, "hitchhiker", "puzzle42")
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(expectedState["hitchhiker/puzzle42"]);
|
||||
});
|
||||
|
||||
it("should add permission", () => {
|
||||
@@ -501,25 +426,16 @@ describe("permissions reducer", () => {
|
||||
entries: [hitchhiker_puzzle42Permission_user_eins]
|
||||
}
|
||||
};
|
||||
let expectedState = {
|
||||
const expectedState = {
|
||||
"hitchhiker/puzzle42": {
|
||||
entries: [
|
||||
hitchhiker_puzzle42Permission_user_eins,
|
||||
hitchhiker_puzzle42Permission_user_zwei
|
||||
]
|
||||
entries: [hitchhiker_puzzle42Permission_user_eins, hitchhiker_puzzle42Permission_user_zwei]
|
||||
}
|
||||
};
|
||||
const newState = reducer(
|
||||
oldState,
|
||||
createPermissionSuccess(
|
||||
hitchhiker_puzzle42Permission_user_zwei,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
)
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(
|
||||
expectedState["hitchhiker/puzzle42"]
|
||||
createPermissionSuccess(hitchhiker_puzzle42Permission_user_zwei, "hitchhiker", "puzzle42")
|
||||
);
|
||||
expect(newState["hitchhiker/puzzle42"]).toEqual(expectedState["hitchhiker/puzzle42"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -535,11 +451,7 @@ describe("permissions selectors", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const repoPermissions = getPermissionsOfRepo(
|
||||
state,
|
||||
"hitchhiker",
|
||||
"puzzle42"
|
||||
);
|
||||
const repoPermissions = getPermissionsOfRepo(state, "hitchhiker", "puzzle42");
|
||||
expect(repoPermissions).toEqual(hitchhiker_puzzle42Permissions);
|
||||
});
|
||||
|
||||
@@ -549,15 +461,11 @@ describe("permissions selectors", () => {
|
||||
[FETCH_PERMISSIONS + "/hitchhiker/puzzle42"]: true
|
||||
}
|
||||
};
|
||||
expect(isFetchPermissionsPending(state, "hitchhiker", "puzzle42")).toEqual(
|
||||
true
|
||||
);
|
||||
expect(isFetchPermissionsPending(state, "hitchhiker", "puzzle42")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false, when fetch permissions is not pending", () => {
|
||||
expect(isFetchPermissionsPending({}, "hitchiker", "puzzle42")).toEqual(
|
||||
false
|
||||
);
|
||||
expect(isFetchPermissionsPending({}, "hitchiker", "puzzle42")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return error when fetch permissions did fail", () => {
|
||||
@@ -566,15 +474,11 @@ describe("permissions selectors", () => {
|
||||
[FETCH_PERMISSIONS + "/hitchhiker/puzzle42"]: error
|
||||
}
|
||||
};
|
||||
expect(getFetchPermissionsFailure(state, "hitchhiker", "puzzle42")).toEqual(
|
||||
error
|
||||
);
|
||||
expect(getFetchPermissionsFailure(state, "hitchhiker", "puzzle42")).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when fetch permissions did not fail", () => {
|
||||
expect(getFetchPermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(
|
||||
undefined
|
||||
);
|
||||
expect(getFetchPermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should return true, when modify permission is pending", () => {
|
||||
@@ -583,25 +487,15 @@ describe("permissions selectors", () => {
|
||||
[MODIFY_PERMISSION + "/hitchhiker/puzzle42/user_eins"]: true
|
||||
}
|
||||
};
|
||||
expect(
|
||||
isModifyPermissionPending(
|
||||
state,
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toEqual(true);
|
||||
expect(isModifyPermissionPending(state, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toEqual(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("should return false, when modify permission is not pending", () => {
|
||||
expect(
|
||||
isModifyPermissionPending(
|
||||
{},
|
||||
"hitchiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toEqual(false);
|
||||
expect(isModifyPermissionPending({}, "hitchiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toEqual(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("should return error when modify permission did fail", () => {
|
||||
@@ -611,44 +505,32 @@ describe("permissions selectors", () => {
|
||||
}
|
||||
};
|
||||
expect(
|
||||
getModifyPermissionFailure(
|
||||
state,
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
getModifyPermissionFailure(state, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)
|
||||
).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when modify permission did not fail", () => {
|
||||
expect(
|
||||
getModifyPermissionFailure(
|
||||
{},
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toBe(undefined);
|
||||
expect(getModifyPermissionFailure({}, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it("should return error when one of the modify permissions did fail", () => {
|
||||
const state = {
|
||||
permissions: {
|
||||
"hitchhiker/puzzle42": { entries: hitchhiker_puzzle42Permissions }
|
||||
"hitchhiker/puzzle42": {
|
||||
entries: hitchhiker_puzzle42Permissions
|
||||
}
|
||||
},
|
||||
failure: {
|
||||
[MODIFY_PERMISSION + "/hitchhiker/puzzle42/user_eins"]: error
|
||||
}
|
||||
};
|
||||
expect(
|
||||
getModifyPermissionsFailure(state, "hitchhiker", "puzzle42")
|
||||
).toEqual(error);
|
||||
expect(getModifyPermissionsFailure(state, "hitchhiker", "puzzle42")).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when no modify permissions did not fail", () => {
|
||||
expect(getModifyPermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(
|
||||
undefined
|
||||
);
|
||||
expect(getModifyPermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should return true, when createPermission is true", () => {
|
||||
@@ -679,25 +561,15 @@ describe("permissions selectors", () => {
|
||||
[DELETE_PERMISSION + "/hitchhiker/puzzle42/user_eins"]: true
|
||||
}
|
||||
};
|
||||
expect(
|
||||
isDeletePermissionPending(
|
||||
state,
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toEqual(true);
|
||||
expect(isDeletePermissionPending(state, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toEqual(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it("should return false, when delete permission is not pending", () => {
|
||||
expect(
|
||||
isDeletePermissionPending(
|
||||
{},
|
||||
"hitchiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toEqual(false);
|
||||
expect(isDeletePermissionPending({}, "hitchiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toEqual(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("should return error when delete permission did fail", () => {
|
||||
@@ -707,44 +579,32 @@ describe("permissions selectors", () => {
|
||||
}
|
||||
};
|
||||
expect(
|
||||
getDeletePermissionFailure(
|
||||
state,
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
getDeletePermissionFailure(state, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)
|
||||
).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when delete permission did not fail", () => {
|
||||
expect(
|
||||
getDeletePermissionFailure(
|
||||
{},
|
||||
"hitchhiker",
|
||||
"puzzle42",
|
||||
hitchhiker_puzzle42Permission_user_eins
|
||||
)
|
||||
).toBe(undefined);
|
||||
expect(getDeletePermissionFailure({}, "hitchhiker", "puzzle42", hitchhiker_puzzle42Permission_user_eins)).toBe(
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
it("should return error when one of the delete permissions did fail", () => {
|
||||
const state = {
|
||||
permissions: {
|
||||
"hitchhiker/puzzle42": { entries: hitchhiker_puzzle42Permissions }
|
||||
"hitchhiker/puzzle42": {
|
||||
entries: hitchhiker_puzzle42Permissions
|
||||
}
|
||||
},
|
||||
failure: {
|
||||
[DELETE_PERMISSION + "/hitchhiker/puzzle42/user_eins"]: error
|
||||
}
|
||||
};
|
||||
expect(
|
||||
getDeletePermissionsFailure(state, "hitchhiker", "puzzle42")
|
||||
).toEqual(error);
|
||||
expect(getDeletePermissionsFailure(state, "hitchhiker", "puzzle42")).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when no delete permissions did not fail", () => {
|
||||
expect(getDeletePermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(
|
||||
undefined
|
||||
);
|
||||
expect(getDeletePermissionsFailure({}, "hitchhiker", "puzzle42")).toBe(undefined);
|
||||
});
|
||||
|
||||
it("should return true, when create permission is pending", () => {
|
||||
@@ -753,15 +613,11 @@ describe("permissions selectors", () => {
|
||||
[CREATE_PERMISSION + "/hitchhiker/puzzle42"]: true
|
||||
}
|
||||
};
|
||||
expect(isCreatePermissionPending(state, "hitchhiker", "puzzle42")).toEqual(
|
||||
true
|
||||
);
|
||||
expect(isCreatePermissionPending(state, "hitchhiker", "puzzle42")).toEqual(true);
|
||||
});
|
||||
|
||||
it("should return false, when create permissions is not pending", () => {
|
||||
expect(isCreatePermissionPending({}, "hitchiker", "puzzle42")).toEqual(
|
||||
false
|
||||
);
|
||||
expect(isCreatePermissionPending({}, "hitchiker", "puzzle42")).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return error when create permissions did fail", () => {
|
||||
@@ -770,14 +626,10 @@ describe("permissions selectors", () => {
|
||||
[CREATE_PERMISSION + "/hitchhiker/puzzle42"]: error
|
||||
}
|
||||
};
|
||||
expect(getCreatePermissionFailure(state, "hitchhiker", "puzzle42")).toEqual(
|
||||
error
|
||||
);
|
||||
expect(getCreatePermissionFailure(state, "hitchhiker", "puzzle42")).toEqual(error);
|
||||
});
|
||||
|
||||
it("should return undefined when create permissions did not fail", () => {
|
||||
expect(getCreatePermissionFailure({}, "hitchhiker", "puzzle42")).toBe(
|
||||
undefined
|
||||
);
|
||||
expect(getCreatePermissionFailure({}, "hitchhiker", "puzzle42")).toBe(undefined);
|
||||
});
|
||||
});
|
||||
@@ -1,101 +1,50 @@
|
||||
// @flow
|
||||
|
||||
import type { Action } from "@scm-manager/ui-components";
|
||||
import { Action } from "@scm-manager/ui-components";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import * as types from "../../../modules/types";
|
||||
import type {
|
||||
RepositoryRole,
|
||||
Permission,
|
||||
PermissionCollection,
|
||||
PermissionCreateEntry
|
||||
} from "@scm-manager/ui-types";
|
||||
import { RepositoryRole, Permission, PermissionCollection, PermissionCreateEntry } from "@scm-manager/ui-types";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
import { Dispatch } from "redux";
|
||||
|
||||
export const FETCH_AVAILABLE = "scm/permissions/FETCH_AVAILABLE";
|
||||
export const FETCH_AVAILABLE_PENDING = `${FETCH_AVAILABLE}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const FETCH_AVAILABLE_SUCCESS = `${FETCH_AVAILABLE}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const FETCH_AVAILABLE_FAILURE = `${FETCH_AVAILABLE}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const FETCH_AVAILABLE_PENDING = `${FETCH_AVAILABLE}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_AVAILABLE_SUCCESS = `${FETCH_AVAILABLE}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_AVAILABLE_FAILURE = `${FETCH_AVAILABLE}_${types.FAILURE_SUFFIX}`;
|
||||
export const FETCH_PERMISSIONS = "scm/permissions/FETCH_PERMISSIONS";
|
||||
export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PERMISSIONS_SUCCESS = `${FETCH_PERMISSIONS}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PERMISSIONS_FAILURE = `${FETCH_PERMISSIONS}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const FETCH_PERMISSIONS_PENDING = `${FETCH_PERMISSIONS}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_PERMISSIONS_SUCCESS = `${FETCH_PERMISSIONS}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_PERMISSIONS_FAILURE = `${FETCH_PERMISSIONS}_${types.FAILURE_SUFFIX}`;
|
||||
export const MODIFY_PERMISSION = "scm/permissions/MODFIY_PERMISSION";
|
||||
export const MODIFY_PERMISSION_PENDING = `${MODIFY_PERMISSION}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const MODIFY_PERMISSION_SUCCESS = `${MODIFY_PERMISSION}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const MODIFY_PERMISSION_FAILURE = `${MODIFY_PERMISSION}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const MODIFY_PERMISSION_RESET = `${MODIFY_PERMISSION}_${
|
||||
types.RESET_SUFFIX
|
||||
}`;
|
||||
export const MODIFY_PERMISSION_PENDING = `${MODIFY_PERMISSION}_${types.PENDING_SUFFIX}`;
|
||||
export const MODIFY_PERMISSION_SUCCESS = `${MODIFY_PERMISSION}_${types.SUCCESS_SUFFIX}`;
|
||||
export const MODIFY_PERMISSION_FAILURE = `${MODIFY_PERMISSION}_${types.FAILURE_SUFFIX}`;
|
||||
export const MODIFY_PERMISSION_RESET = `${MODIFY_PERMISSION}_${types.RESET_SUFFIX}`;
|
||||
export const CREATE_PERMISSION = "scm/permissions/CREATE_PERMISSION";
|
||||
export const CREATE_PERMISSION_PENDING = `${CREATE_PERMISSION}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const CREATE_PERMISSION_SUCCESS = `${CREATE_PERMISSION}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const CREATE_PERMISSION_FAILURE = `${CREATE_PERMISSION}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const CREATE_PERMISSION_RESET = `${CREATE_PERMISSION}_${
|
||||
types.RESET_SUFFIX
|
||||
}`;
|
||||
export const CREATE_PERMISSION_PENDING = `${CREATE_PERMISSION}_${types.PENDING_SUFFIX}`;
|
||||
export const CREATE_PERMISSION_SUCCESS = `${CREATE_PERMISSION}_${types.SUCCESS_SUFFIX}`;
|
||||
export const CREATE_PERMISSION_FAILURE = `${CREATE_PERMISSION}_${types.FAILURE_SUFFIX}`;
|
||||
export const CREATE_PERMISSION_RESET = `${CREATE_PERMISSION}_${types.RESET_SUFFIX}`;
|
||||
export const DELETE_PERMISSION = "scm/permissions/DELETE_PERMISSION";
|
||||
export const DELETE_PERMISSION_PENDING = `${DELETE_PERMISSION}_${
|
||||
types.PENDING_SUFFIX
|
||||
}`;
|
||||
export const DELETE_PERMISSION_SUCCESS = `${DELETE_PERMISSION}_${
|
||||
types.SUCCESS_SUFFIX
|
||||
}`;
|
||||
export const DELETE_PERMISSION_FAILURE = `${DELETE_PERMISSION}_${
|
||||
types.FAILURE_SUFFIX
|
||||
}`;
|
||||
export const DELETE_PERMISSION_RESET = `${DELETE_PERMISSION}_${
|
||||
types.RESET_SUFFIX
|
||||
}`;
|
||||
export const DELETE_PERMISSION_PENDING = `${DELETE_PERMISSION}_${types.PENDING_SUFFIX}`;
|
||||
export const DELETE_PERMISSION_SUCCESS = `${DELETE_PERMISSION}_${types.SUCCESS_SUFFIX}`;
|
||||
export const DELETE_PERMISSION_FAILURE = `${DELETE_PERMISSION}_${types.FAILURE_SUFFIX}`;
|
||||
export const DELETE_PERMISSION_RESET = `${DELETE_PERMISSION}_${types.RESET_SUFFIX}`;
|
||||
|
||||
const CONTENT_TYPE = "application/vnd.scmm-repositoryPermission+json";
|
||||
|
||||
// fetch available permissions
|
||||
|
||||
export function fetchAvailablePermissionsIfNeeded(
|
||||
repositoryRolesLink: string,
|
||||
repositoryVerbsLink: string
|
||||
) {
|
||||
return function(dispatch: any, getState: () => Object) {
|
||||
export function fetchAvailablePermissionsIfNeeded(repositoryRolesLink: string, repositoryVerbsLink: string) {
|
||||
return function(dispatch: any, getState: () => object) {
|
||||
if (shouldFetchAvailablePermissions(getState())) {
|
||||
return fetchAvailablePermissions(
|
||||
dispatch,
|
||||
getState,
|
||||
repositoryRolesLink,
|
||||
repositoryVerbsLink
|
||||
);
|
||||
return fetchAvailablePermissions(dispatch, getState, repositoryRolesLink, repositoryVerbsLink);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAvailablePermissions(
|
||||
dispatch: any,
|
||||
getState: () => Object,
|
||||
getState: () => object,
|
||||
repositoryRolesLink: string,
|
||||
repositoryVerbsLink: string
|
||||
) {
|
||||
@@ -124,11 +73,8 @@ export function fetchAvailablePermissions(
|
||||
});
|
||||
}
|
||||
|
||||
export function shouldFetchAvailablePermissions(state: Object) {
|
||||
if (
|
||||
isFetchAvailablePermissionsPending(state) ||
|
||||
getFetchAvailablePermissionsFailure(state)
|
||||
) {
|
||||
export function shouldFetchAvailablePermissions(state: object) {
|
||||
if (isFetchAvailablePermissionsPending(state) || getFetchAvailablePermissionsFailure(state)) {
|
||||
return false;
|
||||
}
|
||||
return !state.available;
|
||||
@@ -142,9 +88,7 @@ export function fetchAvailablePending(): Action {
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchAvailableSuccess(
|
||||
available: [RepositoryRole[], string[]]
|
||||
): Action {
|
||||
export function fetchAvailableSuccess(available: [RepositoryRole[], string[]]): Action {
|
||||
return {
|
||||
type: FETCH_AVAILABLE_SUCCESS,
|
||||
payload: available,
|
||||
@@ -164,11 +108,7 @@ export function fetchAvailableFailure(error: Error): Action {
|
||||
|
||||
// fetch permissions
|
||||
|
||||
export function fetchPermissions(
|
||||
link: string,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function fetchPermissions(link: string, namespace: string, repoName: string) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchPermissionsPending(namespace, repoName));
|
||||
return apiClient
|
||||
@@ -183,10 +123,7 @@ export function fetchPermissions(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsPending(
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function fetchPermissionsPending(namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_PENDING,
|
||||
payload: {
|
||||
@@ -197,11 +134,7 @@ export function fetchPermissionsPending(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsSuccess(
|
||||
permissions: any,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function fetchPermissionsSuccess(permissions: any, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_SUCCESS,
|
||||
payload: permissions,
|
||||
@@ -209,11 +142,7 @@ export function fetchPermissionsSuccess(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchPermissionsFailure(
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
error: Error
|
||||
): Action {
|
||||
export function fetchPermissionsFailure(namespace: string, repoName: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_PERMISSIONS_FAILURE,
|
||||
payload: {
|
||||
@@ -227,12 +156,7 @@ export function fetchPermissionsFailure(
|
||||
|
||||
// modify permission
|
||||
|
||||
export function modifyPermission(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
callback?: () => void
|
||||
) {
|
||||
export function modifyPermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(modifyPermissionPending(permission, namespace, repoName));
|
||||
return apiClient
|
||||
@@ -249,11 +173,7 @@ export function modifyPermission(
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyPermissionPending(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function modifyPermissionPending(permission: Permission, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_PENDING,
|
||||
payload: permission,
|
||||
@@ -261,11 +181,7 @@ export function modifyPermissionPending(
|
||||
};
|
||||
}
|
||||
|
||||
export function modifyPermissionSuccess(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function modifyPermissionSuccess(permission: Permission, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_SUCCESS,
|
||||
payload: {
|
||||
@@ -284,15 +200,15 @@ export function modifyPermissionFailure(
|
||||
): Action {
|
||||
return {
|
||||
type: MODIFY_PERMISSION_FAILURE,
|
||||
payload: { error, permission },
|
||||
payload: {
|
||||
error,
|
||||
permission
|
||||
},
|
||||
itemId: createItemId(permission, namespace, repoName)
|
||||
};
|
||||
}
|
||||
|
||||
function newPermissions(
|
||||
oldPermissions: PermissionCollection,
|
||||
newPermission: Permission
|
||||
) {
|
||||
function newPermissions(oldPermissions: PermissionCollection, newPermission: Permission) {
|
||||
for (let i = 0; i < oldPermissions.length; i++) {
|
||||
if (oldPermissions[i].name === newPermission.name) {
|
||||
oldPermissions.splice(i, 1, newPermission);
|
||||
@@ -330,16 +246,12 @@ export function createPermission(
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(createdPermission => {
|
||||
dispatch(
|
||||
createPermissionSuccess(createdPermission, namespace, repoName)
|
||||
);
|
||||
dispatch(createPermissionSuccess(createdPermission, namespace, repoName));
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
})
|
||||
.catch(err =>
|
||||
dispatch(createPermissionFailure(err, namespace, repoName))
|
||||
);
|
||||
.catch(err => dispatch(createPermissionFailure(err, namespace, repoName)));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -370,11 +282,7 @@ export function createPermissionSuccess(
|
||||
};
|
||||
}
|
||||
|
||||
export function createPermissionFailure(
|
||||
error: Error,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function createPermissionFailure(error: Error, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: CREATE_PERMISSION_FAILURE,
|
||||
payload: error,
|
||||
@@ -391,12 +299,7 @@ export function createPermissionReset(namespace: string, repoName: string) {
|
||||
|
||||
// delete permission
|
||||
|
||||
export function deletePermission(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
callback?: () => void
|
||||
) {
|
||||
export function deletePermission(permission: Permission, namespace: string, repoName: string, callback?: () => void) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(deletePermissionPending(permission, namespace, repoName));
|
||||
return apiClient
|
||||
@@ -413,11 +316,7 @@ export function deletePermission(
|
||||
};
|
||||
}
|
||||
|
||||
export function deletePermissionPending(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function deletePermissionPending(permission: Permission, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: DELETE_PERMISSION_PENDING,
|
||||
payload: permission,
|
||||
@@ -425,11 +324,7 @@ export function deletePermissionPending(
|
||||
};
|
||||
}
|
||||
|
||||
export function deletePermissionSuccess(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
): Action {
|
||||
export function deletePermissionSuccess(permission: Permission, namespace: string, repoName: string): Action {
|
||||
return {
|
||||
type: DELETE_PERMISSION_SUCCESS,
|
||||
payload: {
|
||||
@@ -467,11 +362,8 @@ export function deletePermissionReset(namespace: string, repoName: string) {
|
||||
};
|
||||
}
|
||||
|
||||
function deletePermissionFromState(
|
||||
oldPermissions: PermissionCollection,
|
||||
permission: Permission
|
||||
) {
|
||||
let newPermission = [];
|
||||
function deletePermissionFromState(oldPermissions: PermissionCollection, permission: Permission) {
|
||||
const newPermission = [];
|
||||
for (let i = 0; i < oldPermissions.length; i++) {
|
||||
if (
|
||||
oldPermissions[i].name !== permission.name ||
|
||||
@@ -483,20 +375,18 @@ function deletePermissionFromState(
|
||||
return newPermission;
|
||||
}
|
||||
|
||||
function createItemId(
|
||||
permission: Permission,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
let groupPermission = permission.groupPermission ? "@" : "";
|
||||
function createItemId(permission: Permission, namespace: string, repoName: string) {
|
||||
const groupPermission = permission.groupPermission ? "@" : "";
|
||||
return namespace + "/" + repoName + "/" + groupPermission + permission.name;
|
||||
}
|
||||
|
||||
// reducer
|
||||
export default function reducer(
|
||||
state: Object = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
): Object {
|
||||
state: object = {},
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): object {
|
||||
if (!action.payload) {
|
||||
return state;
|
||||
}
|
||||
@@ -516,10 +406,7 @@ export default function reducer(
|
||||
};
|
||||
case MODIFY_PERMISSION_SUCCESS:
|
||||
const positionOfPermission = action.payload.position;
|
||||
const newPermission = newPermissions(
|
||||
state[action.payload.position].entries,
|
||||
action.payload.permission
|
||||
);
|
||||
const newPermission = newPermissions(state[action.payload.position].entries, action.payload.permission);
|
||||
return {
|
||||
...state,
|
||||
[positionOfPermission]: {
|
||||
@@ -559,194 +446,109 @@ export default function reducer(
|
||||
|
||||
// selectors
|
||||
|
||||
export function getAvailablePermissions(state: Object) {
|
||||
export function getAvailablePermissions(state: object) {
|
||||
if (state.permissions) {
|
||||
return state.permissions.available;
|
||||
}
|
||||
}
|
||||
|
||||
export function getAvailableRepositoryRoles(state: Object) {
|
||||
export function getAvailableRepositoryRoles(state: object) {
|
||||
return available(state).repositoryRoles;
|
||||
}
|
||||
|
||||
export function getAvailableRepositoryVerbs(state: Object) {
|
||||
export function getAvailableRepositoryVerbs(state: object) {
|
||||
return available(state).repositoryVerbs;
|
||||
}
|
||||
|
||||
function available(state: Object) {
|
||||
function available(state: object) {
|
||||
if (state.permissions && state.permissions.available) {
|
||||
return state.permissions.available;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export function getPermissionsOfRepo(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function getPermissionsOfRepo(state: object, namespace: string, repoName: string) {
|
||||
if (state.permissions && state.permissions[namespace + "/" + repoName]) {
|
||||
return state.permissions[namespace + "/" + repoName].entries;
|
||||
}
|
||||
}
|
||||
|
||||
export function isFetchAvailablePermissionsPending(state: Object) {
|
||||
export function isFetchAvailablePermissionsPending(state: object) {
|
||||
return isPending(state, FETCH_AVAILABLE, "available");
|
||||
}
|
||||
|
||||
export function isFetchPermissionsPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function isFetchPermissionsPending(state: object, namespace: string, repoName: string) {
|
||||
return isPending(state, FETCH_PERMISSIONS, namespace + "/" + repoName);
|
||||
}
|
||||
|
||||
export function getFetchAvailablePermissionsFailure(state: Object) {
|
||||
export function getFetchAvailablePermissionsFailure(state: object) {
|
||||
return getFailure(state, FETCH_AVAILABLE, "available");
|
||||
}
|
||||
|
||||
export function getFetchPermissionsFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function getFetchPermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
return getFailure(state, FETCH_PERMISSIONS, namespace + "/" + repoName);
|
||||
}
|
||||
|
||||
export function isModifyPermissionPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return isPending(
|
||||
state,
|
||||
MODIFY_PERMISSION,
|
||||
createItemId(permission, namespace, repoName)
|
||||
);
|
||||
export function isModifyPermissionPending(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return isPending(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function getModifyPermissionFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return getFailure(
|
||||
state,
|
||||
MODIFY_PERMISSION,
|
||||
createItemId(permission, namespace, repoName)
|
||||
);
|
||||
export function getModifyPermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return getFailure(state, MODIFY_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function hasCreatePermission(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function hasCreatePermission(state: object, namespace: string, repoName: string) {
|
||||
if (state.permissions && state.permissions[namespace + "/" + repoName])
|
||||
return state.permissions[namespace + "/" + repoName].createPermission;
|
||||
else return null;
|
||||
}
|
||||
|
||||
export function isCreatePermissionPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function isCreatePermissionPending(state: object, namespace: string, repoName: string) {
|
||||
return isPending(state, CREATE_PERMISSION, namespace + "/" + repoName);
|
||||
}
|
||||
|
||||
export function getCreatePermissionFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function getCreatePermissionFailure(state: object, namespace: string, repoName: string) {
|
||||
return getFailure(state, CREATE_PERMISSION, namespace + "/" + repoName);
|
||||
}
|
||||
|
||||
export function isDeletePermissionPending(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return isPending(
|
||||
state,
|
||||
DELETE_PERMISSION,
|
||||
createItemId(permission, namespace, repoName)
|
||||
);
|
||||
export function isDeletePermissionPending(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return isPending(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function getDeletePermissionFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string,
|
||||
permission: Permission
|
||||
) {
|
||||
return getFailure(
|
||||
state,
|
||||
DELETE_PERMISSION,
|
||||
createItemId(permission, namespace, repoName)
|
||||
);
|
||||
export function getDeletePermissionFailure(state: object, namespace: string, repoName: string, permission: Permission) {
|
||||
return getFailure(state, DELETE_PERMISSION, createItemId(permission, namespace, repoName));
|
||||
}
|
||||
|
||||
export function getDeletePermissionsFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function getDeletePermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
const permissions =
|
||||
state.permissions && state.permissions[namespace + "/" + repoName]
|
||||
? state.permissions[namespace + "/" + repoName].entries
|
||||
: null;
|
||||
if (permissions == null) return undefined;
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
if (
|
||||
getDeletePermissionFailure(state, namespace, repoName, permissions[i])
|
||||
) {
|
||||
return getFailure(
|
||||
state,
|
||||
DELETE_PERMISSION,
|
||||
createItemId(permissions[i], namespace, repoName)
|
||||
);
|
||||
if (getDeletePermissionFailure(state, namespace, repoName, permissions[i])) {
|
||||
return getFailure(state, DELETE_PERMISSION, createItemId(permissions[i], namespace, repoName));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getModifyPermissionsFailure(
|
||||
state: Object,
|
||||
namespace: string,
|
||||
repoName: string
|
||||
) {
|
||||
export function getModifyPermissionsFailure(state: object, namespace: string, repoName: string) {
|
||||
const permissions =
|
||||
state.permissions && state.permissions[namespace + "/" + repoName]
|
||||
? state.permissions[namespace + "/" + repoName].entries
|
||||
: null;
|
||||
if (permissions == null) return undefined;
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
if (
|
||||
getModifyPermissionFailure(state, namespace, repoName, permissions[i])
|
||||
) {
|
||||
return getFailure(
|
||||
state,
|
||||
MODIFY_PERMISSION,
|
||||
createItemId(permissions[i], namespace, repoName)
|
||||
);
|
||||
if (getModifyPermissionFailure(state, namespace, repoName, permissions[i])) {
|
||||
return getFailure(state, MODIFY_PERMISSION, createItemId(permissions[i], namespace, repoName));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findVerbsForRole(
|
||||
availableRepositoryRoles: RepositoryRole[],
|
||||
roleName: string
|
||||
) {
|
||||
const matchingRole = availableRepositoryRoles.find(
|
||||
role => roleName === role.name
|
||||
);
|
||||
export function findVerbsForRole(availableRepositoryRoles: RepositoryRole[], roleName: string) {
|
||||
const matchingRole = availableRepositoryRoles.find(role => roleName === role.name);
|
||||
if (matchingRole) {
|
||||
return matchingRole.verbs;
|
||||
} else {
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
file: File
|
||||
file: File;
|
||||
};
|
||||
|
||||
class FileIcon extends React.Component<Props> {
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
import { findParent } from "./FileTree";
|
||||
|
||||
describe("find parent tests", () => {
|
||||
@@ -1,36 +1,26 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { compose } from "redux";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import type { Repository, File } from "@scm-manager/ui-types";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
Notification
|
||||
} from "@scm-manager/ui-components";
|
||||
import {
|
||||
getFetchSourcesFailure,
|
||||
isFetchSourcesPending,
|
||||
getSources
|
||||
} from "../modules/sources";
|
||||
import { Repository, File } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading, Notification } from "@scm-manager/ui-components";
|
||||
import { getFetchSourcesFailure, isFetchSourcesPending, getSources } from "../modules/sources";
|
||||
import FileTreeLeaf from "./FileTreeLeaf";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
error: Error,
|
||||
tree: File,
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string,
|
||||
baseUrl: string,
|
||||
type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
tree: File;
|
||||
repository: Repository;
|
||||
revision: string;
|
||||
path: string;
|
||||
baseUrl: string;
|
||||
|
||||
// context props
|
||||
t: string => string,
|
||||
match: any
|
||||
match: any;
|
||||
};
|
||||
|
||||
const FixedWidthTh = styled.th`
|
||||
@@ -114,27 +104,15 @@ class FileTree extends React.Component<Props> {
|
||||
<tr>
|
||||
<FixedWidthTh />
|
||||
<th>{t("sources.file-tree.name")}</th>
|
||||
<th className="is-hidden-mobile">
|
||||
{t("sources.file-tree.length")}
|
||||
</th>
|
||||
<th className="is-hidden-mobile">
|
||||
{t("sources.file-tree.lastModified")}
|
||||
</th>
|
||||
<th className="is-hidden-mobile">
|
||||
{t("sources.file-tree.description")}
|
||||
</th>
|
||||
{binder.hasExtension("repos.sources.tree.row.right") && (
|
||||
<th className="is-hidden-mobile" />
|
||||
)}
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.length")}</th>
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.lastModified")}</th>
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.description")}</th>
|
||||
{binder.hasExtension("repos.sources.tree.row.right") && <th className="is-hidden-mobile" />}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{files.map(file => (
|
||||
<FileTreeLeaf
|
||||
key={file.name}
|
||||
file={file}
|
||||
baseUrl={baseUrlWithRevision}
|
||||
/>
|
||||
<FileTreeLeaf key={file.name} file={file} baseUrl={baseUrlWithRevision} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -163,4 +141,4 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps)
|
||||
)(translate("repos")(FileTree));
|
||||
)(withTranslation("repos")(FileTree));
|
||||
@@ -1,7 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import { createLink } from "./FileTreeLeaf";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
|
||||
describe("create link tests", () => {
|
||||
function dir(path: string): File {
|
||||
@@ -1,16 +1,15 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
import { DateFromNow, FileSize } from "@scm-manager/ui-components";
|
||||
import FileIcon from "./FileIcon";
|
||||
|
||||
type Props = {
|
||||
file: File,
|
||||
baseUrl: string
|
||||
file: File;
|
||||
baseUrl: string;
|
||||
};
|
||||
|
||||
const MinWidthTd = styled.td`
|
||||
@@ -67,22 +66,20 @@ export default class FileTreeLeaf extends React.Component<Props> {
|
||||
return (
|
||||
<tr>
|
||||
<td>{this.createFileIcon(file)}</td>
|
||||
<MinWidthTd className="is-word-break">
|
||||
{this.createFileName(file)}
|
||||
</MinWidthTd>
|
||||
<MinWidthTd className="is-word-break">{this.createFileName(file)}</MinWidthTd>
|
||||
<td className="is-hidden-mobile">{fileSize}</td>
|
||||
<td className="is-hidden-mobile">
|
||||
<DateFromNow date={file.lastModified} />
|
||||
</td>
|
||||
<MinWidthTd className={classNames("is-word-break", "is-hidden-mobile")}>
|
||||
{file.description}
|
||||
</MinWidthTd>
|
||||
<MinWidthTd className={classNames("is-word-break", "is-hidden-mobile")}>{file.description}</MinWidthTd>
|
||||
{binder.hasExtension("repos.sources.tree.row.right") && (
|
||||
<td className="is-hidden-mobile">
|
||||
{!file.directory && (
|
||||
<ExtensionPoint
|
||||
name="repos.sources.tree.row.right"
|
||||
props={{ file }}
|
||||
props={{
|
||||
file
|
||||
}}
|
||||
renderAll={true}
|
||||
/>
|
||||
)}
|
||||
@@ -1,26 +0,0 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { DownloadButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
file: File
|
||||
};
|
||||
|
||||
class DownloadViewer extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, file } = this.props;
|
||||
return (
|
||||
<div className="has-text-centered">
|
||||
<DownloadButton
|
||||
url={file._links.self.href}
|
||||
displayName={t("sources.content.downloadButton")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(DownloadViewer);
|
||||
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
import { DownloadButton } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
file: File;
|
||||
};
|
||||
|
||||
class DownloadViewer extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, file } = this.props;
|
||||
return (
|
||||
<div className="has-text-centered">
|
||||
<DownloadButton url={file._links.self.href} displayName={t("sources.content.downloadButton")} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("repos")(DownloadViewer);
|
||||
@@ -1,13 +1,11 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { ButtonAddons, Button } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
className?: string,
|
||||
t: string => string,
|
||||
historyIsSelected: boolean,
|
||||
showHistory: boolean => void
|
||||
type Props = WithTranslation & {
|
||||
className?: string;
|
||||
historyIsSelected: boolean;
|
||||
showHistory: (p: boolean) => void;
|
||||
};
|
||||
|
||||
class FileButtonAddons extends React.Component<Props> {
|
||||
@@ -29,22 +27,14 @@ class FileButtonAddons extends React.Component<Props> {
|
||||
return (
|
||||
<ButtonAddons className={className}>
|
||||
<div title={t("sources.content.sourcesButton")}>
|
||||
<Button
|
||||
action={this.showSources}
|
||||
className="reduced"
|
||||
color={this.color(!historyIsSelected)}
|
||||
>
|
||||
<Button action={this.showSources} className="reduced" color={this.color(!historyIsSelected)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-code" />
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div title={t("sources.content.historyButton")}>
|
||||
<Button
|
||||
action={this.showHistory}
|
||||
className="reduced"
|
||||
color={this.color(historyIsSelected)}
|
||||
>
|
||||
<Button action={this.showHistory} className="reduced" color={this.color(historyIsSelected)}>
|
||||
<span className="icon">
|
||||
<i className="fas fa-history" />
|
||||
</span>
|
||||
@@ -55,4 +45,4 @@ class FileButtonAddons extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(FileButtonAddons);
|
||||
export default withTranslation("repos")(FileButtonAddons);
|
||||
@@ -1,11 +1,9 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
file: File
|
||||
type Props = WithTranslation & {
|
||||
file: File;
|
||||
};
|
||||
|
||||
class ImageViewer extends React.Component<Props> {
|
||||
@@ -21,4 +19,4 @@ class ImageViewer extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("repos")(ImageViewer);
|
||||
export default withTranslation("repos")(ImageViewer);
|
||||
@@ -1,9 +1,5 @@
|
||||
//@flow
|
||||
import fetchMock from "fetch-mock";
|
||||
import {
|
||||
getContent,
|
||||
getLanguage
|
||||
} from "./SourcecodeViewer";
|
||||
import { getContent, getLanguage } from "./SourcecodeViewer";
|
||||
|
||||
describe("get content", () => {
|
||||
const CONTENT_URL = "/repositories/scmadmin/TestRepo/content/testContent";
|
||||
@@ -1,20 +1,18 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { apiClient, SyntaxHighlighter } from "@scm-manager/ui-components";
|
||||
import type { File } from "@scm-manager/ui-types";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
file: File,
|
||||
language: string
|
||||
type Props = WithTranslation & {
|
||||
file: File;
|
||||
language: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
content: string,
|
||||
error?: Error,
|
||||
loaded: boolean
|
||||
content: string;
|
||||
error?: Error;
|
||||
loaded: boolean;
|
||||
};
|
||||
|
||||
class SourcecodeViewer extends React.Component<Props, State> {
|
||||
@@ -64,12 +62,7 @@ class SourcecodeViewer extends React.Component<Props, State> {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SyntaxHighlighter
|
||||
language={getLanguage(language)}
|
||||
value= {content}
|
||||
/>
|
||||
);
|
||||
return <SyntaxHighlighter language={getLanguage(language)} value={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +78,10 @@ export function getContent(url: string) {
|
||||
return response;
|
||||
})
|
||||
.catch(err => {
|
||||
return { error: err };
|
||||
return {
|
||||
error: err
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export default translate("repos")(SourcecodeViewer);
|
||||
export default withTranslation("repos")(SourcecodeViewer);
|
||||
@@ -1,32 +1,28 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import {connect} from "react-redux";
|
||||
import {translate} from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import {ExtensionPoint} from "@scm-manager/ui-extensions";
|
||||
import type {File, Repository} from "@scm-manager/ui-types";
|
||||
import {DateFromNow, ErrorNotification, FileSize, Icon} from "@scm-manager/ui-components";
|
||||
import {getSources} from "../modules/sources";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { File, Repository } from "@scm-manager/ui-types";
|
||||
import { DateFromNow, ErrorNotification, FileSize, Icon } from "@scm-manager/ui-components";
|
||||
import { getSources } from "../modules/sources";
|
||||
import FileButtonAddons from "../components/content/FileButtonAddons";
|
||||
import SourcesView from "./SourcesView";
|
||||
import HistoryView from "./HistoryView";
|
||||
|
||||
type Props = {
|
||||
loading: boolean,
|
||||
file: File,
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
type Props = WithTranslation & {
|
||||
loading: boolean;
|
||||
file: File;
|
||||
repository: Repository;
|
||||
revision: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
collapsed: boolean,
|
||||
showHistory: boolean,
|
||||
errorFromExtension?: Error
|
||||
collapsed: boolean;
|
||||
showHistory: boolean;
|
||||
errorFromExtension?: Error;
|
||||
};
|
||||
|
||||
const VCenteredChild = styled.div`
|
||||
@@ -73,7 +69,9 @@ class Content extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
handleExtensionError = (error: Error) => {
|
||||
this.setState({ errorFromExtension: error });
|
||||
this.setState({
|
||||
errorFromExtension: error
|
||||
});
|
||||
};
|
||||
|
||||
showHeader() {
|
||||
@@ -85,9 +83,7 @@ class Content extends React.Component<Props, State> {
|
||||
<RightMarginFileButtonAddons
|
||||
file={file}
|
||||
historyIsSelected={showHistory}
|
||||
showHistory={(changeShowHistory: boolean) =>
|
||||
this.setShowHistoryState(changeShowHistory)
|
||||
}
|
||||
showHistory={(changeShowHistory: boolean) => this.setShowHistoryState(changeShowHistory)}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
@@ -160,7 +156,11 @@ class Content extends React.Component<Props, State> {
|
||||
<ExtensionPoint
|
||||
name="repos.content.metadata"
|
||||
renderAll={true}
|
||||
props={{ file, repository, revision }}
|
||||
props={{
|
||||
file,
|
||||
repository,
|
||||
revision
|
||||
}}
|
||||
/>
|
||||
</tbody>
|
||||
</LighterGreyBackgroundTable>
|
||||
@@ -179,12 +179,7 @@ class Content extends React.Component<Props, State> {
|
||||
showHistory && file._links.history ? (
|
||||
<HistoryView file={file} repository={repository} />
|
||||
) : (
|
||||
<SourcesView
|
||||
revision={revision}
|
||||
file={file}
|
||||
repository={repository}
|
||||
path={path}
|
||||
/>
|
||||
<SourcesView revision={revision} file={file} repository={repository} path={path} />
|
||||
);
|
||||
const moreInformation = this.showMoreInformation();
|
||||
|
||||
@@ -211,4 +206,4 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(translate("repos")(Content));
|
||||
export default connect(mapStateToProps)(withTranslation("repos")(Content));
|
||||
@@ -1,30 +1,19 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import type {
|
||||
File,
|
||||
Changeset,
|
||||
Repository,
|
||||
PagedCollection
|
||||
} from "@scm-manager/ui-types";
|
||||
import {
|
||||
ErrorNotification,
|
||||
Loading,
|
||||
StatePaginator,
|
||||
ChangesetList
|
||||
} from "@scm-manager/ui-components";
|
||||
import { File, Changeset, Repository, PagedCollection } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading, StatePaginator, ChangesetList } from "@scm-manager/ui-components";
|
||||
import { getHistory } from "./history";
|
||||
|
||||
type Props = {
|
||||
file: File,
|
||||
repository: Repository
|
||||
file: File;
|
||||
repository: Repository;
|
||||
};
|
||||
|
||||
type State = {
|
||||
loaded: boolean,
|
||||
changesets: Changeset[],
|
||||
page: number,
|
||||
pageCollection?: PagedCollection,
|
||||
error?: Error
|
||||
loaded: boolean;
|
||||
changesets: Changeset[];
|
||||
page: number;
|
||||
pageCollection?: PagedCollection;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
class HistoryView extends React.Component<Props, State> {
|
||||
@@ -68,9 +57,7 @@ class HistoryView extends React.Component<Props, State> {
|
||||
updatePage(page: number) {
|
||||
const { file } = this.props;
|
||||
const internalPage = page - 1;
|
||||
this.updateHistory(
|
||||
file._links.history.href + "?page=" + internalPage.toString()
|
||||
);
|
||||
this.updateHistory(file._links.history.href + "?page=" + internalPage.toString());
|
||||
}
|
||||
|
||||
showHistory() {
|
||||
211
scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx
Normal file
211
scm-ui/ui-webapp/src/repos/sources/containers/Sources.tsx
Normal file
@@ -0,0 +1,211 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import { Branch, Repository } from "@scm-manager/ui-types";
|
||||
import { BranchSelector, Breadcrumb, ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
import FileTree from "../components/FileTree";
|
||||
import {
|
||||
fetchBranches,
|
||||
getBranches,
|
||||
getFetchBranchesFailure,
|
||||
isFetchBranchesPending
|
||||
} from "../../branches/modules/branches";
|
||||
import { compose } from "redux";
|
||||
import Content from "./Content";
|
||||
import { fetchSources, isDirectory } from "../modules/sources";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
repository: Repository;
|
||||
loading: boolean;
|
||||
error: Error;
|
||||
baseUrl: string;
|
||||
branches: Branch[];
|
||||
revision: string;
|
||||
path: string;
|
||||
currentFileIsDirectory: boolean;
|
||||
|
||||
// dispatch props
|
||||
fetchBranches: (p: Repository) => void;
|
||||
fetchSources: (p1: Repository, p2: string, p3: string) => void;
|
||||
|
||||
// Context props
|
||||
history: any;
|
||||
match: any;
|
||||
location: any;
|
||||
};
|
||||
|
||||
type State = {
|
||||
selectedBranch: any;
|
||||
};
|
||||
|
||||
class Sources extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
selectedBranch: null
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { fetchBranches, repository, revision, path, fetchSources } = this.props;
|
||||
|
||||
fetchBranches(repository);
|
||||
fetchSources(repository, revision, path);
|
||||
|
||||
this.redirectToDefaultBranch();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { fetchSources, repository, revision, path } = this.props;
|
||||
if (prevProps.revision !== revision || prevProps.path !== path) {
|
||||
fetchSources(repository, revision, path);
|
||||
}
|
||||
|
||||
this.redirectToDefaultBranch();
|
||||
}
|
||||
|
||||
redirectToDefaultBranch = () => {
|
||||
const { branches } = this.props;
|
||||
if (this.shouldRedirectToDefaultBranch()) {
|
||||
const defaultBranches = branches.filter(b => b.defaultBranch);
|
||||
|
||||
if (defaultBranches.length > 0) {
|
||||
this.branchSelected(defaultBranches[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
shouldRedirectToDefaultBranch = () => {
|
||||
const { branches, revision } = this.props;
|
||||
return branches && !revision;
|
||||
};
|
||||
|
||||
branchSelected = (branch?: Branch) => {
|
||||
const { baseUrl, history, path } = this.props;
|
||||
let url;
|
||||
if (branch) {
|
||||
this.setState({
|
||||
selectedBranch: branch
|
||||
});
|
||||
if (path) {
|
||||
url = `${baseUrl}/${encodeURIComponent(branch.name)}/${path}`;
|
||||
} else {
|
||||
url = `${baseUrl}/${encodeURIComponent(branch.name)}/`;
|
||||
}
|
||||
} else {
|
||||
this.setState({
|
||||
selectedBranch: null
|
||||
});
|
||||
url = `${baseUrl}/`;
|
||||
}
|
||||
history.push(url);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { repository, baseUrl, loading, error, revision, path, currentFileIsDirectory } = this.props;
|
||||
|
||||
if (error) {
|
||||
return <ErrorNotification error={error} />;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (currentFileIsDirectory) {
|
||||
return (
|
||||
<div className="panel">
|
||||
{this.renderBranchSelector()}
|
||||
{this.renderBreadcrumb()}
|
||||
<FileTree repository={repository} revision={revision} path={path} baseUrl={baseUrl} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return <Content repository={repository} revision={revision} path={path} />;
|
||||
}
|
||||
}
|
||||
|
||||
renderBranchSelector = () => {
|
||||
const { branches, revision, t } = this.props;
|
||||
|
||||
if (branches) {
|
||||
return (
|
||||
<div className="panel-heading">
|
||||
<BranchSelector
|
||||
branches={branches}
|
||||
selectedBranch={revision}
|
||||
label={t("changesets.branchSelectorLabel")}
|
||||
selected={(b: Branch) => {
|
||||
this.branchSelected(b);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
renderBreadcrumb = () => {
|
||||
const { revision, path, baseUrl, branches, repository } = this.props;
|
||||
const { selectedBranch } = this.state;
|
||||
|
||||
if (revision) {
|
||||
return (
|
||||
<Breadcrumb
|
||||
revision={encodeURIComponent(revision)}
|
||||
path={path}
|
||||
baseUrl={baseUrl}
|
||||
branch={selectedBranch}
|
||||
defaultBranch={branches && branches.filter(b => b.defaultBranch === true)[0]}
|
||||
branches={branches}
|
||||
repository={repository}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const { repository, match } = ownProps;
|
||||
const { revision, path } = match.params;
|
||||
const decodedRevision = revision ? decodeURIComponent(revision) : undefined;
|
||||
const loading = isFetchBranchesPending(state, repository);
|
||||
const error = getFetchBranchesFailure(state, repository);
|
||||
const branches = getBranches(state, repository);
|
||||
const currentFileIsDirectory = decodedRevision
|
||||
? isDirectory(state, repository, decodedRevision, path)
|
||||
: isDirectory(state, repository, revision, path);
|
||||
|
||||
return {
|
||||
repository,
|
||||
revision: decodedRevision,
|
||||
path,
|
||||
loading,
|
||||
error,
|
||||
branches,
|
||||
currentFileIsDirectory
|
||||
};
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
return {
|
||||
fetchBranches: (repository: Repository) => {
|
||||
dispatch(fetchBranches(repository));
|
||||
},
|
||||
fetchSources: (repository: Repository, revision: string, path: string) => {
|
||||
dispatch(fetchSources(repository, revision, path));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default compose(
|
||||
withTranslation("repos"),
|
||||
withRouter,
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)
|
||||
)(Sources);
|
||||
@@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
import SourcecodeViewer from "../components/content/SourcecodeViewer";
|
||||
@@ -6,21 +5,21 @@ import ImageViewer from "../components/content/ImageViewer";
|
||||
import DownloadViewer from "../components/content/DownloadViewer";
|
||||
import { ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { getContentType } from "./contentType";
|
||||
import type { File, Repository } from "@scm-manager/ui-types";
|
||||
import { File, Repository } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
repository: Repository,
|
||||
file: File,
|
||||
revision: string,
|
||||
path: string
|
||||
repository: Repository;
|
||||
file: File;
|
||||
revision: string;
|
||||
path: string;
|
||||
};
|
||||
|
||||
type State = {
|
||||
contentType: string,
|
||||
language: string,
|
||||
loaded: boolean,
|
||||
error?: Error
|
||||
contentType: string;
|
||||
language: string;
|
||||
loaded: boolean;
|
||||
error?: Error;
|
||||
};
|
||||
|
||||
class SourcesView extends React.Component<Props, State> {
|
||||
@@ -69,7 +68,11 @@ class SourcesView extends React.Component<Props, State> {
|
||||
return (
|
||||
<ExtensionPoint
|
||||
name="repos.sources.view"
|
||||
props={{ file, contentType, revision }}
|
||||
props={{
|
||||
file,
|
||||
contentType,
|
||||
revision
|
||||
}}
|
||||
>
|
||||
<DownloadViewer file={file} />
|
||||
</ExtensionPoint>
|
||||
@@ -1,4 +1,3 @@
|
||||
//@flow
|
||||
import fetchMock from "fetch-mock";
|
||||
import { getContentType } from "./contentType";
|
||||
|
||||
@@ -11,7 +10,7 @@ describe("get content type", () => {
|
||||
});
|
||||
|
||||
it("should return content", done => {
|
||||
let headers = {
|
||||
const headers = {
|
||||
"Content-Type": "application/text",
|
||||
"X-Programming-Language": "JAVA"
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
//@flow
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
|
||||
export function getContentType(url: string) {
|
||||
@@ -11,6 +10,8 @@ export function getContentType(url: string) {
|
||||
};
|
||||
})
|
||||
.catch(err => {
|
||||
return { error: err };
|
||||
return {
|
||||
error: err
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
//@flow
|
||||
import fetchMock from "fetch-mock";
|
||||
import { getHistory } from "./history";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
//@flow
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
|
||||
export function getHistory(url: string) {
|
||||
@@ -17,6 +16,8 @@ export function getHistory(url: string) {
|
||||
};
|
||||
})
|
||||
.catch(err => {
|
||||
return { error: err };
|
||||
return {
|
||||
error: err
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
// @flow
|
||||
|
||||
import type { Repository, File } from "@scm-manager/ui-types";
|
||||
import { Repository, File } from "@scm-manager/ui-types";
|
||||
import configureMockStore from "redux-mock-store";
|
||||
import thunk from "redux-thunk";
|
||||
import fetchMock from "fetch-mock";
|
||||
@@ -18,8 +16,7 @@ import {
|
||||
isDirectory
|
||||
} from "./sources";
|
||||
|
||||
const sourcesUrl =
|
||||
"http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/";
|
||||
const sourcesUrl = "http://localhost:8081/scm/rest/api/v2/repositories/scm/core/sources/";
|
||||
|
||||
const repository: Repository = {
|
||||
name: "core",
|
||||
@@ -123,7 +120,10 @@ describe("sources fetch", () => {
|
||||
fetchMock.getOnce(sourcesUrl, collection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_SOURCES_PENDING, itemId: "scm/core/_/" },
|
||||
{
|
||||
type: FETCH_SOURCES_PENDING,
|
||||
itemId: "scm/core/_/"
|
||||
},
|
||||
{
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
itemId: "scm/core/_/",
|
||||
@@ -141,7 +141,10 @@ describe("sources fetch", () => {
|
||||
fetchMock.getOnce(sourcesUrl + "abc/src", collection);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: FETCH_SOURCES_PENDING, itemId: "scm/core/abc/src" },
|
||||
{
|
||||
type: FETCH_SOURCES_PENDING,
|
||||
itemId: "scm/core/abc/src"
|
||||
},
|
||||
{
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
itemId: "scm/core/abc/src",
|
||||
@@ -181,21 +184,14 @@ describe("reducer tests", () => {
|
||||
const expectedState = {
|
||||
"scm/core/_/": collection
|
||||
};
|
||||
expect(
|
||||
reducer({}, fetchSourcesSuccess(repository, "", "", collection))
|
||||
).toEqual(expectedState);
|
||||
expect(reducer({}, fetchSourcesSuccess(repository, "", "", collection))).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it("should store the collection, with revision and path", () => {
|
||||
const expectedState = {
|
||||
"scm/core/abc/src/main": collection
|
||||
};
|
||||
expect(
|
||||
reducer(
|
||||
{},
|
||||
fetchSourcesSuccess(repository, "abc", "src/main", collection)
|
||||
)
|
||||
).toEqual(expectedState);
|
||||
expect(reducer({}, fetchSourcesSuccess(repository, "abc", "src/main", collection))).toEqual(expectedState);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -208,9 +204,7 @@ describe("selector tests", () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
expect(
|
||||
isDirectory(state, repository, "abc", "src/main/package.json")
|
||||
).toBeFalsy();
|
||||
expect(isDirectory(state, repository, "abc", "src/main/package.json")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should return true if it is directory", () => {
|
||||
@@ -1,7 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import * as types from "../../../modules/types";
|
||||
import type { Repository, File, Action } from "@scm-manager/ui-types";
|
||||
import { Repository, File, Action } from "@scm-manager/ui-types";
|
||||
import { apiClient } from "@scm-manager/ui-components";
|
||||
import { isPending } from "../../../modules/pending";
|
||||
import { getFailure } from "../../../modules/failure";
|
||||
@@ -11,11 +9,7 @@ export const FETCH_SOURCES_PENDING = `${FETCH_SOURCES}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_SOURCES_SUCCESS = `${FETCH_SOURCES}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_SOURCES_FAILURE = `${FETCH_SOURCES}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export function fetchSources(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
) {
|
||||
export function fetchSources(repository: Repository, revision: string, path: string) {
|
||||
return function(dispatch: any) {
|
||||
dispatch(fetchSourcesPending(repository, revision, path));
|
||||
return apiClient
|
||||
@@ -41,23 +35,14 @@ function createUrl(repository: Repository, revision: string, path: string) {
|
||||
return `${base}${encodeURIComponent(revision)}/${pathDefined}`;
|
||||
}
|
||||
|
||||
export function fetchSourcesPending(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
): Action {
|
||||
export function fetchSourcesPending(repository: Repository, revision: string, path: string): Action {
|
||||
return {
|
||||
type: FETCH_SOURCES_PENDING,
|
||||
itemId: createItemId(repository, revision, path)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesSuccess(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string,
|
||||
sources: File
|
||||
) {
|
||||
export function fetchSourcesSuccess(repository: Repository, revision: string, path: string, sources: File) {
|
||||
return {
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
payload: sources,
|
||||
@@ -65,12 +50,7 @@ export function fetchSourcesSuccess(
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesFailure(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string,
|
||||
error: Error
|
||||
): Action {
|
||||
export function fetchSourcesFailure(repository: Repository, revision: string, path: string, error: Error): Action {
|
||||
return {
|
||||
type: FETCH_SOURCES_FAILURE,
|
||||
payload: error,
|
||||
@@ -88,7 +68,9 @@ function createItemId(repository: Repository, revision: string, path: string) {
|
||||
|
||||
export default function reducer(
|
||||
state: any = {},
|
||||
action: Action = { type: "UNKNOWN" }
|
||||
action: Action = {
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): any {
|
||||
if (action.itemId && action.type === FETCH_SOURCES_SUCCESS) {
|
||||
return {
|
||||
@@ -101,12 +83,7 @@ export default function reducer(
|
||||
|
||||
// selectors
|
||||
|
||||
export function isDirectory(
|
||||
state: any,
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
): boolean {
|
||||
export function isDirectory(state: any, repository: Repository, revision: string, path: string): boolean {
|
||||
const currentFile = getSources(state, repository, revision, path);
|
||||
if (currentFile && !currentFile.directory) {
|
||||
return false;
|
||||
@@ -120,24 +97,15 @@ export function getSources(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
): ?File {
|
||||
): File | null | undefined {
|
||||
if (state.sources) {
|
||||
return state.sources[createItemId(repository, revision, path)];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isFetchSourcesPending(
|
||||
state: any,
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
): boolean {
|
||||
return isPending(
|
||||
state,
|
||||
FETCH_SOURCES,
|
||||
createItemId(repository, revision, path)
|
||||
);
|
||||
export function isFetchSourcesPending(state: any, repository: Repository, revision: string, path: string): boolean {
|
||||
return isPending(state, FETCH_SOURCES, createItemId(repository, revision, path));
|
||||
}
|
||||
|
||||
export function getFetchSourcesFailure(
|
||||
@@ -145,10 +113,6 @@ export function getFetchSourcesFailure(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string
|
||||
): ?Error {
|
||||
return getFailure(
|
||||
state,
|
||||
FETCH_SOURCES,
|
||||
createItemId(repository, revision, path)
|
||||
);
|
||||
): Error | null | undefined {
|
||||
return getFailure(state, FETCH_SOURCES, createItemId(repository, revision, path));
|
||||
}
|
||||
Reference in New Issue
Block a user