implement MultiPluginActions for UpdateAll and CancelPending

This commit is contained in:
Eduard Heimbuch
2019-09-27 14:05:19 +02:00
parent fd4070b1b1
commit 32cb67f92e
5 changed files with 236 additions and 91 deletions

View File

@@ -17,6 +17,7 @@ export type Plugin = {
};
export type PluginCollection = Collection & {
_links: Links,
_embedded: {
plugins: Plugin[] | string[]
}

View File

@@ -1,68 +0,0 @@
// @flow
import React from "react";
import { Button } from "@scm-manager/ui-components";
import type { PendingPlugins } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import ExecutePendingModal from "./ExecutePendingModal";
type Props = {
pendingPlugins: PendingPlugins,
// context props
t: string => string
};
type State = {
showModal: boolean
};
class ExecutePendingAction extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
showModal: false
};
}
openModal = () => {
this.setState({
showModal: true
});
};
closeModal = () => {
this.setState({
showModal: false
});
};
renderModal = () => {
const { showModal } = this.state;
const { pendingPlugins } = this.props;
if (showModal) {
return (
<ExecutePendingModal
pendingPlugins={pendingPlugins}
onClose={this.closeModal}
/>
);
}
return null;
};
render() {
const { t } = this.props;
return (
<>
{this.renderModal()}
<Button
color="primary"
label={t("plugins.executePending")}
action={this.openModal}
/>
</>
);
}
}
export default translate("admin")(ExecutePendingAction);

View File

@@ -0,0 +1,90 @@
// @flow
import React from "react";
import { Button } from "@scm-manager/ui-components";
import type {PendingPlugins, PluginCollection} from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import MultiPluginActionModal from "./MultiPluginActionModal";
export const MultiPluginActionType = {
UPDATE_ALL: "updateAll",
CANCEL_PENDING: "cancelPending",
EXECUTE_PENDING: "executePending"
};
type Props = {
actionType: string,
pendingPlugins?: PendingPlugins,
installedPlugins?: PluginCollection,
// context props
t: string => string
};
type State = {
showModal: boolean
};
class MultiPluginAction extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
showModal: false
};
}
toggleModal = () => {
this.setState(state => ({
showModal: !state.showModal
}));
};
renderLabel = () => {
const {t, actionType} = this.props;
if (actionType === MultiPluginActionType.EXECUTE_PENDING) {
return t("plugins.executePending");
} else if (actionType === MultiPluginActionType.CANCEL_PENDING) {
return t("plugins.cancelPending");
} else {
return t("plugins.updateAll");
}
};
renderModal = () => {
const { showModal } = this.state;
const {pendingPlugins, installedPlugins, actionType} = this.props;
if (showModal) {
return (
<MultiPluginActionModal
pendingPlugins={
actionType === MultiPluginActionType.UPDATE_ALL
? null
: pendingPlugins
}
installedPlugins={
actionType !== MultiPluginActionType.UPDATE_ALL
? null
: installedPlugins
}
onClose={this.toggleModal}
actionType={actionType}
/>
);
}
return null;
};
render() {
return (
<>
{this.renderModal()}
<Button
color="primary"
label={this.renderLabel()}
action={this.toggleModal}
/>
</>
);
}
}
export default translate("admin")(MultiPluginAction);

View File

@@ -8,14 +8,17 @@ import {
Modal,
Notification
} from "@scm-manager/ui-components";
import type { PendingPlugins } from "@scm-manager/ui-types";
import type { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
import { translate } from "react-i18next";
import waitForRestart from "./waitForRestart";
import SuccessNotification from "./SuccessNotification";
import { MultiPluginActionType } from "./MultiPluginAction";
type Props = {
onClose: () => void,
pendingPlugins: PendingPlugins,
actionType: string,
pendingPlugins?: PendingPlugins,
installedPlugins?: PluginCollection,
// context props
t: string => string
@@ -27,7 +30,7 @@ type State = {
error?: Error
};
class ExecutePendingModal extends React.Component<Props, State> {
class MultiPluginActionModal extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
@@ -52,6 +55,18 @@ class ExecutePendingModal extends React.Component<Props, State> {
}
};
executeAction = () => {
const { actionType } = this.props;
if (actionType === MultiPluginActionType.EXECUTE_PENDING) {
this.executeAndRestart();
} else if (actionType === MultiPluginActionType.CANCEL_PENDING) {
this.cancelPending();
} else if (actionType === MultiPluginActionType.UPDATE_ALL) {
this.updateAllWithRestart();
}
};
executeAndRestart = () => {
const { pendingPlugins } = this.props;
this.setState({
@@ -64,8 +79,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
.then(() => {
this.setState({
success: true,
loading: false,
error: undefined
loading: false
});
})
.catch(error => {
@@ -77,11 +91,96 @@ class ExecutePendingModal extends React.Component<Props, State> {
});
};
cancelPending = () => {
const { pendingPlugins } = this.props;
this.setState({
loading: true
});
apiClient
.post(pendingPlugins._links.cancel.href)
.then(() => {
this.setState({
success: true,
loading: false
});
})
.catch(error => {
this.setState({
success: false,
loading: false,
error: error
});
});
};
updateAllWithRestart = () => {
const { installedPlugins } = this.props;
this.setState({
loading: true
});
apiClient
.post(installedPlugins._links.update.href)
.then(waitForRestart)
.then(() => {
this.setState({
success: true,
loading: false
});
})
.catch(error => {
this.setState({
success: false,
loading: false,
error: error
});
});
};
renderModalContent = () => {
const { actionType } = this.props;
if (actionType === MultiPluginActionType.UPDATE_ALL) {
return <>{this.renderUpdatable()}</>;
} else {
return (
<>
{this.renderInstallQueue()}
{this.renderUpdateQueue()}
{this.renderUninstallQueue()}
</>
);
}
};
renderUpdatable = () => {
const { installedPlugins } = this.props;
return (
<>
{installedPlugins &&
installedPlugins._embedded &&
installedPlugins._embedded.plugins && (
<>
<ul>
{installedPlugins._embedded.plugins
.filter(plugin => plugin._links && plugin._links.update)
.map(plugin => (
<li key={plugin.name}>{plugin.name}</li>
))}
</ul>
</>
)}
</>
);
};
renderInstallQueue = () => {
const { pendingPlugins, t } = this.props;
return (
<>
{pendingPlugins._embedded &&
{pendingPlugins &&
pendingPlugins._embedded &&
pendingPlugins._embedded.new.length > 0 && (
<>
<strong>{t("plugins.modal.installQueue")}</strong>
@@ -100,7 +199,8 @@ class ExecutePendingModal extends React.Component<Props, State> {
const { pendingPlugins, t } = this.props;
return (
<>
{pendingPlugins._embedded &&
{pendingPlugins &&
pendingPlugins._embedded &&
pendingPlugins._embedded.update.length > 0 && (
<>
<strong>{t("plugins.modal.updateQueue")}</strong>
@@ -119,7 +219,8 @@ class ExecutePendingModal extends React.Component<Props, State> {
const { pendingPlugins, t } = this.props;
return (
<>
{pendingPlugins._embedded &&
{pendingPlugins &&
pendingPlugins._embedded &&
pendingPlugins._embedded.uninstall.length > 0 && (
<>
<strong>{t("plugins.modal.uninstallQueue")}</strong>
@@ -141,9 +242,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
<div className="media">
<div className="content">
<p>{t("plugins.modal.executePending")}</p>
{this.renderInstallQueue()}
{this.renderUpdateQueue()}
{this.renderUninstallQueue()}
{this.renderModalContent()}
</div>
</div>
<div className="media">{this.renderNotifications()}</div>
@@ -160,7 +259,7 @@ class ExecutePendingModal extends React.Component<Props, State> {
color="warning"
label={t("plugins.modal.executeAndRestart")}
loading={loading}
action={this.executeAndRestart}
action={this.executeAction}
disabled={error || success}
/>
<Button label={t("plugins.modal.abort")} action={onClose} />
@@ -182,4 +281,4 @@ class ExecutePendingModal extends React.Component<Props, State> {
}
}
export default translate("admin")(ExecutePendingModal);
export default translate("admin")(MultiPluginActionModal);

View File

@@ -5,6 +5,7 @@ import { translate } from "react-i18next";
import { compose } from "redux";
import type { PendingPlugins, PluginCollection } from "@scm-manager/ui-types";
import {
ButtonGroup,
ErrorNotification,
Loading,
Notification,
@@ -27,7 +28,9 @@ import {
} from "../../../modules/indexResource";
import PluginTopActions from "../components/PluginTopActions";
import PluginBottomActions from "../components/PluginBottomActions";
import ExecutePendingAction from "../components/ExecutePendingAction";
import MultiPluginAction, {
MultiPluginActionType
} from "../components/MultiPluginAction";
type Props = {
loading: boolean,
@@ -109,15 +112,35 @@ class PluginsOverview extends React.Component<Props> {
};
createActions = () => {
const { pendingPlugins } = this.props;
if (
pendingPlugins &&
pendingPlugins._links &&
pendingPlugins._links.execute
) {
return <ExecutePendingAction pendingPlugins={pendingPlugins} />;
}
return null;
const {pendingPlugins, collection} = this.props;
return (
<ButtonGroup>
{pendingPlugins &&
pendingPlugins._links &&
pendingPlugins._links.execute && (
<MultiPluginAction
pendingPlugins={pendingPlugins}
actionType={MultiPluginActionType.EXECUTE_PENDING}
/>
)}
{pendingPlugins &&
pendingPlugins._links &&
pendingPlugins._links.cancel && (
<MultiPluginAction
pendingPlugins={pendingPlugins}
actionType={MultiPluginActionType.CANCEL_PENDING}
/>
)}
{collection &&
collection._links &&
!collection._links.update && (
<MultiPluginAction
installedPlugins={collection}
actionType={MultiPluginActionType.UPDATE_ALL}
/>
)}
</ButtonGroup>
);
};
render() {