Manual Reference Source

basic/maintenance/MaintenanceModeModal.js

/**
 * Created by pposel on 16/02/2017.
 */
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { connect } from 'react-redux';

import Consts from '../../../utils/consts';
import {
    ApproveButton,
    CancelButton,
    Checkmark,
    DataTable,
    ErrorMessage,
    ExecutionStatus,
    Icon,
    Menu,
    Modal,
    PopupMenu
} from '../index';
import StageUtils from '../../../utils/stageUtils';
import {
    switchMaintenance,
    getActiveExecutions,
    setActiveExecutions,
    doCancelExecution
} from '../../../actions/managers';
import ExecutionUtils from '../../../utils/shared/ExecutionUtils';

const POLLING_INTERVAL = 2000;

class MaintenanceModeModal extends Component {
    constructor(props, context) {
        super(props, context);

        this.state = MaintenanceModeModal.initialState;
        this.pollingTimeout = null;
    }

    static initialState = {
        cancelling: [],
        loading: false,
        error: ''
    };

    static propTypes = {
        show: PropTypes.bool.isRequired,
        manager: PropTypes.object.isRequired,
        activeExecutions: PropTypes.object,
        onHide: PropTypes.func.isRequired,
        onMaintenanceActivate: PropTypes.func.isRequired,
        onMaintenanceDeactivate: PropTypes.func.isRequired,
        onFetchActiveExecutions: PropTypes.func.isRequired
    };

    static defaultProps = {
        activeExecutions: {}
    };

    componentDidUpdate(prevProps) {
        if (!prevProps.show && this.props.show) {
            this.setState(MaintenanceModeModal.initialState);

            this._loadPendingExecutions();
        } else if (prevProps.show && !this.props.show) {
            this._stopPolling();
            this._stopFetchingData();
            this.props.onClose();
        }
    }

    componentWillUnmount() {
        this._stopPolling();
    }

    _loadPendingExecutions() {
        if (this.props.manager.maintenance !== Consts.MAINTENANCE_DEACTIVATED) {
            return;
        }

        this.fetchDataPromise = StageUtils.makeCancelable(this.props.onFetchActiveExecutions());
        this.fetchDataPromise.promise
            .then(data => {
                console.log('Maintenance data fetched');
                this._startPolling();
            })
            .catch(err => {
                this.setState({ error: err.message });
                this._startPolling();
            });
    }

    _stopPolling() {
        console.log('Stop polling maintenance data');
        clearTimeout(this.pollingTimeout);
    }

    _stopFetchingData() {
        if (this.fetchDataPromise) {
            this.fetchDataPromise.cancel();
        }
    }

    _startPolling() {
        this._stopPolling();
        this._stopFetchingData();

        if (this.props.show) {
            console.log(`Polling maintenance data - time interval: ${POLLING_INTERVAL / 1000} sec`);
            this.pollingTimeout = setTimeout(() => {
                this._loadPendingExecutions();
            }, POLLING_INTERVAL);
        }
    }

    onApprove() {
        this.setState({ loading: true });

        if (this.props.manager.maintenance === Consts.MAINTENANCE_DEACTIVATED) {
            this._activate();
        } else {
            this._deactivate();
        }

        return false;
    }

    onDeny() {
        this.props.onHide();
        return true;
    }

    _activate() {
        this.props
            .onMaintenanceActivate()
            .then(() => {
                this.setState({ error: '', loading: false });
                this.props.onHide();
            })
            .catch(err => {
                this.setState({ error: err.message, loading: false });
            });
    }

    _deactivate() {
        this.props
            .onMaintenanceDeactivate()
            .then(() => {
                this.setState({ error: '', loading: false });
                this.props.onHide();
            })
            .catch(err => {
                this.setState({ error: err.message, loading: false });
            });
    }

    _cancelExecution(execution, action) {
        this.setState({ cancelling: [...this.state.cancelling, execution.id] }, () =>
            this.props
                .onCancelExecution(execution, action)
                .then(() => {
                    this._loadPendingExecutions();
                    this.setState({ error: '', cancelling: _.without(this.state.cancelling, execution.id) });
                })
                .catch(err => {
                    this.setState({ error: err.message });
                })
        );
    }

    render() {
        return (
            <Modal open={this.props.show} onClose={() => this.props.onHide()}>
                <Modal.Header>
                    <Icon name="doctor" />
                    {this.props.manager.maintenance === Consts.MAINTENANCE_DEACTIVATED
                        ? 'Are you sure you want to enter maintenance mode?'
                        : 'Are you sure you want to exit maintenance mode?'}
                </Modal.Header>

                {this.state.error || !_.isEmpty(this.props.activeExecutions.items) ? (
                    <Modal.Content>
                        <ErrorMessage error={this.state.error} />

                        {!_.isEmpty(this.props.activeExecutions.items) && (
                            <DataTable>
                                <DataTable.Column label="Blueprint" width="15%" />
                                <DataTable.Column label="Deployment" width="15%" />
                                <DataTable.Column label="Workflow" width="15%" />
                                <DataTable.Column label="Id" width="20%" />
                                <DataTable.Column label="System" width="5%" />
                                <DataTable.Column label="Status" width="15%" />
                                <DataTable.Column label="Action" />

                                {this.props.activeExecutions.items.map(item => {
                                    return (
                                        <DataTable.Row key={item.id}>
                                            <DataTable.Data>{item.blueprint_id}</DataTable.Data>
                                            <DataTable.Data>{item.deployment_id}</DataTable.Data>
                                            <DataTable.Data>{item.workflow_id}</DataTable.Data>
                                            <DataTable.Data>{item.id}</DataTable.Data>
                                            <DataTable.Data>
                                                <Checkmark value={item.is_system_workflow} />
                                            </DataTable.Data>
                                            <DataTable.Data>
                                                <ExecutionStatus execution={item} />
                                            </DataTable.Data>
                                            <DataTable.Data className="center aligned">
                                                <PopupMenu className="menuAction">
                                                    <Menu pointing vertical>
                                                        <Menu.Item
                                                            content="Cancel"
                                                            icon="cancel"
                                                            name={ExecutionUtils.CANCEL_ACTION}
                                                            onClick={() =>
                                                                this._cancelExecution(
                                                                    item,
                                                                    ExecutionUtils.CANCEL_ACTION
                                                                )
                                                            }
                                                        />
                                                        <Menu.Item
                                                            content="Force Cancel"
                                                            icon={<Icon name="cancel" color="red" />}
                                                            name={ExecutionUtils.FORCE_CANCEL_ACTION}
                                                            onClick={() =>
                                                                this._cancelExecution(
                                                                    item,
                                                                    ExecutionUtils.FORCE_CANCEL_ACTION
                                                                )
                                                            }
                                                        />
                                                        <Menu.Item
                                                            content="Kill Cancel"
                                                            icon={<Icon name="stop" color="red" />}
                                                            name={ExecutionUtils.KILL_CANCEL_EXECUTION}
                                                            onClick={() =>
                                                                this._cancelExecution(
                                                                    item,
                                                                    ExecutionUtils.KILL_CANCEL_EXECUTION
                                                                )
                                                            }
                                                        />
                                                    </Menu>
                                                </PopupMenu>
                                            </DataTable.Data>
                                        </DataTable.Row>
                                    );
                                })}
                            </DataTable>
                        )}
                    </Modal.Content>
                ) : (
                    ''
                )}

                <Modal.Actions>
                    <CancelButton onClick={this.onDeny.bind(this)} content="No" disabled={this.state.loading} />
                    <ApproveButton
                        onClick={this.onApprove.bind(this)}
                        content="Yes"
                        icon="doctor"
                        color="green"
                        disabled={this.state.loading}
                    />
                </Modal.Actions>
            </Modal>
        );
    }
}

const mapStateToProps = (state, ownProps) => {
    return {
        manager: state.manager,
        show: ownProps.show,
        onHide: ownProps.onHide,
        activeExecutions: state.manager.activeExecutions
    };
};

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onMaintenanceActivate: manager => {
            return dispatch(switchMaintenance(manager, true));
        },
        onMaintenanceDeactivate: manager => {
            return dispatch(switchMaintenance(manager, false));
        },
        onFetchActiveExecutions: manager => {
            return dispatch(getActiveExecutions(manager));
        },
        onCancelExecution: (manager, execution, action) => {
            return dispatch(doCancelExecution(manager, execution, action));
        },
        onClose: () => {
            return dispatch(setActiveExecutions({}));
        }
    };
};

const mergeProps = (stateProps, dispatchProps, ownProps) => {
    return {
        ...stateProps,
        ...ownProps,
        ...dispatchProps,
        onMaintenanceActivate: () => dispatchProps.onMaintenanceActivate(stateProps.manager),
        onMaintenanceDeactivate: () => dispatchProps.onMaintenanceDeactivate(stateProps.manager),
        onFetchActiveExecutions: () => dispatchProps.onFetchActiveExecutions(stateProps.manager),
        onCancelExecution: (execution, action) => dispatchProps.onCancelExecution(stateProps.manager, execution, action)
    };
};

export default connect(
    mapStateToProps,
    mapDispatchToProps,
    mergeProps
)(MaintenanceModeModal);