Skip to content

Commit

Permalink
refactor: add mediator pattern for deleting repositories
Browse files Browse the repository at this point in the history
Moves deleting repositories away from mixings and flux stores
by using the new data-layer patterns (streams and mediators).
This is also done in a preparation to use graphql

CLOSES DCOS-21641
  • Loading branch information
Fabs committed May 11, 2018
1 parent f4a81fc commit b360bc8
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 152 deletions.
74 changes: 74 additions & 0 deletions plugins/catalog/src/js/repositories/RepositoriesDelete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/* eslint-disable no-unused-vars */
import React from "react";
/* eslint-enable no-unused-vars */

import { componentFromStream } from "data-service";
import { Subject } from "rxjs/Subject";
import { Observable } from "rxjs/Observable";
import "rxjs/add/operator/combineLatest";
import "rxjs/add/operator/startWith";
import "rxjs/add/operator/map";
import "rxjs/add/operator/catch";

import { deleteRepository } from "./data/repositoriesStream";

import RepositoriesDeleteConfirm from "./components/RepositoriesDeleteConfirm";
import RepositoriesError from "./components/RepositoriesError";

const getErrorMessage = (response = {}) => {
if (typeof response === "string") {
return response;
}

return response.description || response.message || "An error has occurred.";
};

const deleteEvent$ = new Subject();
const deleteRepository$ = deleteEvent$
.switchMap(repository => {
return deleteRepository(repository.name, repository.url)
.startWith({ pendingRequest: true })
.map(result => {
return {
result,
pendingRequest: false
};
})
.do(() => {
repository.complete();
});
})
.startWith({ pendingRequest: false })
.catch(error => {
return Observable.of({
error: getErrorMessage(error.response),
pendingRequest: false
});
});

const RepositoriesDelete = componentFromStream(props$ => {
return props$
.combineLatest(deleteRepository$, (props, response) => {
return {
open: !!props.repository || response.pendingRequest,
...props,
...response
};
})
.map(props => {
return (
<RepositoriesDeleteConfirm
onCancel={props.onClose}
onDelete={() =>
deleteEvent$.next({ complete: props.onClose, ...props.repository })}
pendingRequest={props.pendingRequest}
repository={props.repository}
deleteError={props.error}
open={props.open}
/>
);
})
.catch(err => Observable.of(<RepositoriesError err={err} />));
});

export default RepositoriesDelete;
6 changes: 4 additions & 2 deletions plugins/catalog/src/js/repositories/RepositoriesList.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { typeDefs, resolvers } from "./data/repositoriesModel";

// UI components
import RepositoriesTabUI from "./components/RepositoriesTabUI";
import RepositoriesLoading from "./components/RepositoriesLoading";
import RepositoriesError from "./components/RepositoriesError";

// Using the data layer

Expand Down Expand Up @@ -81,9 +83,9 @@ const components$ = searchTerm$
);
})
// The first component is the loading
.startWith(<RepositoriesTabUI.Loading />)
.startWith(<RepositoriesLoading />)
// If anything goes wrong, we render an error component
.catch(err => Observable.of(<RepositoriesTabUI.Error err={err} />));
.catch(err => Observable.of(<RepositoriesError err={err} />));

// componentFromStream create a single component who render the latest emitted
// component from the stream
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from "react";
import Config from "#SRC/js/config/Config";
import ModalHeading from "#SRC/js/components/modals/ModalHeading";
import { Confirm } from "reactjs-components";
import UserActions from "#SRC/js/constants/UserActions";
import StringUtil from "#SRC/js/utils/StringUtil";

const RepositoriesDeleteConfirmMessage = ({ repository, error }) => {
const label = repository ? repository.get("name") : "This repository";
const errorMessage = <p className="text-error-state">{error}</p>;

return (
<div>
<p>
{`Repository (${label}) will be ${UserActions.DELETED}
from ${Config.productName}. You will not be able to install any
packages belonging to that repository anymore.`}
</p>
{error ? errorMessage : ""}
</div>
);
};

const RepositoriesDeleteConfirm = ({
onCancel,
onDelete,
open,
pendingRequest,
repository,
deleteError
}) => {
const heading = <ModalHeading>Delete Repository</ModalHeading>;
const rightButtonText = `${StringUtil.capitalize(UserActions.DELETE)}
Repository`;

return (
<Confirm
closeByBackdropClick={true}
header={heading}
leftButtonClassName="button button-primary-link flush-left"
rightButtonClassName="button button-danger"
rightButtonText={rightButtonText}
showHeader={true}
onClose={onCancel}
leftButtonCallback={onCancel}
rightButtonCallback={onDelete}
disabled={pendingRequest}
open={open}
>
<RepositoriesDeleteConfirmMessage
repository={repository}
error={deleteError}
/>
</Confirm>
);
};

export default RepositoriesDeleteConfirm;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

import RequestErrorMsg from "#SRC/js/components/RequestErrorMsg";

import RepositoriesPage from "./RepositoriesPage";

const RepositoriesError = () => {
return (
<RepositoriesPage>
<RequestErrorMsg />
</RepositoriesPage>
);
};

export default RepositoriesError;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

import Loader from "#SRC/js/components/Loader";

import RepositoriesPage from "./RepositoriesPage";

const RepositoriesLoading = () => {
return (
<RepositoriesPage>
<Loader />
</RepositoriesPage>
);
};

export default RepositoriesLoading;
38 changes: 38 additions & 0 deletions plugins/catalog/src/js/repositories/components/RepositoriesPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Link } from "react-router";
import React from "react";

import Breadcrumb from "#SRC/js/components/Breadcrumb";
import BreadcrumbTextContent from "#SRC/js/components/BreadcrumbTextContent";
import Page from "#SRC/js/components/Page";

const RepositoriesBreadcrumbs = addButton => {
const crumbs = [
<Breadcrumb key={-1} title="Repositories">
<BreadcrumbTextContent>
<Link to="/settings/repositories">Package Repositories</Link>
</BreadcrumbTextContent>
</Breadcrumb>
];

return (
<Page.Header.Breadcrumbs
iconID="settings"
breadcrumbs={crumbs}
addButton={addButton}
/>
);
};

const RepositoriesPage = ({ addButton, children }) => {
return (
<Page>
<Page.Header
addButton={addButton}
breadcrumbs={<RepositoriesBreadcrumbs />}
/>
{children}
</Page>
);
};

export default RepositoriesPage;
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
import { Link } from "react-router";
import React from "react";

import AddRepositoryFormModal
from "#SRC/js/components/modals/AddRepositoryFormModal";
import Breadcrumb from "#SRC/js/components/Breadcrumb";
import BreadcrumbTextContent from "#SRC/js/components/BreadcrumbTextContent";

import FilterBar from "#SRC/js/components/FilterBar";
import FilterInputText from "#SRC/js/components/FilterInputText";
import Loader from "#SRC/js/components/Loader";
import Page from "#SRC/js/components/Page";
import RepositoriesTable from "#SRC/js/components/RepositoriesTable";
import RequestErrorMsg from "#SRC/js/components/RequestErrorMsg";

const RepositoriesBreadcrumbs = addButton => {
const crumbs = [
<Breadcrumb key={-1} title="Repositories">
<BreadcrumbTextContent>
<Link to="/settings/repositories">Package Repositories</Link>
</BreadcrumbTextContent>
</Breadcrumb>
];
import RepositoriesTable from "./RepositoriesTable";

return (
<Page.Header.Breadcrumbs
iconID="settings"
breadcrumbs={crumbs}
addButton={addButton}
/>
);
};
import RepositoriesPage from "./RepositoriesPage";

const METHODS_TO_BIND = ["handleCloseAddRepository", "handleOpenAddRepository"];

Expand Down Expand Up @@ -80,42 +59,14 @@ export default class RepositoriesTabUI extends React.Component {

render() {
return (
<RepositoriesTabUI.Page
<RepositoriesPage
addButton={{
onItemSelect: this.handleOpenAddRepository,
label: "Add Repository"
}}
>
{this.getContent()}
</RepositoriesTabUI.Page>
</RepositoriesPage>
);
}
}

RepositoriesTabUI.Loading = () => {
return (
<RepositoriesTabUI.Page>
<Loader />
</RepositoriesTabUI.Page>
);
};

RepositoriesTabUI.Error = () => {
return (
<RepositoriesTabUI.Page>
<RequestErrorMsg />
</RepositoriesTabUI.Page>
);
};

RepositoriesTabUI.Page = ({ addButton, children }) => {
return (
<Page>
<Page.Header
addButton={addButton}
breadcrumbs={<RepositoriesBreadcrumbs />}
/>
{children}
</Page>
);
};
Loading

0 comments on commit b360bc8

Please sign in to comment.