Unverified Commit 790b5f20 authored by Jesse Hallam's avatar Jesse Hallam Committed by GitHub
Browse files

MM-8622: Improved plugin error reporting (#1180)

* reflect plugin statuses on plugin management page

* dispatch plugin_status_changed websocket events

* s/cluster_discovery/cluster/

* fix plugin status ordering

* include cluster_id when sorting plugin statuses

* update icons for plugin statuses

* use a checkmark when the plugin is running
* use a ban icon when it's deactivated

* tweak icons, sorting

* linting
parent 692c44ea
......@@ -3,7 +3,7 @@
import $ from 'jquery';
import {batchActions} from 'redux-batched-actions';
import {ChannelTypes, EmojiTypes, PostTypes, TeamTypes, UserTypes, RoleTypes, GeneralTypes} from 'mattermost-redux/action_types';
import {ChannelTypes, EmojiTypes, PostTypes, TeamTypes, UserTypes, RoleTypes, GeneralTypes, AdminTypes} from 'mattermost-redux/action_types';
import {WebsocketEvents, General} from 'mattermost-redux/constants';
import {getChannelAndMyMember, getChannelStats, viewChannel} from 'mattermost-redux/actions/channels';
import {setServerVersion} from 'mattermost-redux/actions/general';
......@@ -287,6 +287,10 @@ function handleEvent(msg) {
handleLicenseChanged(msg);
break;
case SocketEvents.PLUGIN_STATUSES_CHANGED:
handlePluginStatusesChangedEvent(msg);
break;
default:
}
}
......@@ -702,3 +706,7 @@ function handleConfigChanged(msg) {
function handleLicenseChanged(msg) {
store.dispatch({type: GeneralTypes.CLIENT_LICENSE_RECEIVED, data: msg.data.license});
}
function handlePluginStatusesChangedEvent(msg) {
store.dispatch({type: AdminTypes.RECEIVED_PLUGIN_STATUSES, data: msg.data.plugin_statuses});
}
......@@ -3,7 +3,7 @@
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getPlugins, removePlugin, uploadPlugin, activatePlugin, deactivatePlugin} from 'mattermost-redux/actions/admin';
import {getPluginStatuses, removePlugin, uploadPlugin, activatePlugin, deactivatePlugin} from 'mattermost-redux/actions/admin';
import PluginManagement from './plugin_management.jsx';
......@@ -11,6 +11,7 @@ function mapStateToProps(state, ownProps) {
return {
...ownProps,
plugins: state.entities.admin.plugins,
pluginStatuses: state.entities.admin.pluginStatuses,
};
}
......@@ -19,7 +20,7 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
uploadPlugin,
removePlugin,
getPlugins,
getPluginStatuses,
activatePlugin,
deactivatePlugin,
}, dispatch),
......
......@@ -782,6 +782,7 @@
"admin.plugin.activating": "Activating...",
"admin.plugin.banner": "Plugins are experimental and not recommended for use in production.",
"admin.plugin.choose": "Choose File",
"admin.plugin.cluster_instance": "Cluster Instance",
"admin.plugin.deactivate": "Deactivate",
"admin.plugin.deactivating": "Deactivating...",
"admin.plugin.desc": "Description:",
......@@ -792,18 +793,34 @@
"admin.plugin.installedTitle": "Installed Plugins: ",
"admin.plugin.management.banner": "Plugins are disabled on your server. To enable them, go to <strong>Plugins > Configuration</strong>.",
"admin.plugin.management.title": "Management",
"admin.plugin.multiple_versions_warning": "There are multiple versions of this plugin installed across your cluster. Re-install this plugin to ensure it works consistently.",
"admin.plugin.name": "Name:",
"admin.plugin.no_plugins": "No installed plugins.",
"admin.plugin.prepackaged": "Pre-packaged",
"admin.plugin.prepackaged": "pre-packaged",
"admin.plugin.remove": "Remove",
"admin.plugin.removing": "Removing...",
"admin.plugin.settingsButton": "Settings",
"admin.plugin.state": "State",
"admin.plugin.state.failed_to_start": "Failed to start",
"admin.plugin.state.failed_to_start.description": "This plugin failed to start. Check your system logs for errors.",
"admin.plugin.state.failed_to_stay_running": "Crashing",
"admin.plugin.state.failed_to_stay_running.description": "This plugin crashed multiple times and is no longer running. Check your system logs for errors.",
"admin.plugin.state.not_running": "Not running",
"admin.plugin.state.not_running.description": "This plugin is not activated.",
"admin.plugin.state.running": "Running",
"admin.plugin.state.running.description": "This plugin is running.",
"admin.plugin.state.starting": "Starting",
"admin.plugin.state.starting.description": "This plugin is starting.",
"admin.plugin.state.stopping": "Stopping",
"admin.plugin.state.stopping.description": "This plugin is stopping.",
"admin.plugin.state.unknown": "Unknown",
"admin.plugin.upload": "Upload",
"admin.plugin.uploadDesc": "Upload a plugin for your Mattermost server. See <a href=\"https://about.mattermost.com/default-plugin-uploads\" target=\"_blank\">documentation</a> to learn more.",
"admin.plugin.uploadDisabledDesc": "To enable plugin uploads, go to <strong>Plugins > Configuration</strong>. See <a href=\"https://about.mattermost.com/default-plugin-uploads\" target=\"_blank\">documentation</a> to learn more.",
"admin.plugin.uploadTitle": "Upload Plugin: ",
"admin.plugin.uploading": "Uploading...",
"admin.plugin.version": "Version:",
"admin.plugin.version_title": "Version",
"admin.plugins.settings.enable": "Enable Plugins: ",
"admin.plugins.settings.enableDesc": "When true, enables plugins on your Mattermost server. Use plugins to integrate with third-party systems, extend functionality or customize the user interface of your Mattermost server. See <a href=\"https://about.mattermost.com/default-plugins\" target=\"_blank\">documentation</a> to learn more.",
"admin.plugins.settings.title": "Configuration",
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`components/PluginManagement should match snapshot 1`] = `
<div
className="wrapper--fixed"
>
<h3
className="admin-console-header"
>
<FormattedMessage
defaultMessage="Management"
id="admin.plugin.management.title"
values={Object {}}
/>
</h3>
<form
className="form-horizontal"
role="form"
>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
>
<FormattedMessage
defaultMessage="Upload Plugin: "
id="admin.plugin.uploadTitle"
values={Object {}}
/>
</label>
<div
className="col-sm-8"
>
<div
className="file__upload"
>
<button
className="btn btn-primary"
disabled={false}
>
<FormattedMessage
defaultMessage="Choose File"
id="admin.plugin.choose"
values={Object {}}
/>
</button>
<input
accept=".gz"
disabled={false}
onChange={[Function]}
type="file"
/>
</div>
<button
className="btn"
disabled={true}
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Upload"
id="admin.plugin.upload"
values={Object {}}
/>
</button>
<div
className="help-text no-margin"
/>
<p
className="help-text"
>
<FormattedHTMLMessage
defaultMessage="Upload a plugin for your Mattermost server. See <a href=\\"https://about.mattermost.com/default-plugin-uploads\\" target=\\"_blank\\">documentation</a> to learn more."
id="admin.plugin.uploadDesc"
values={Object {}}
/>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
>
<FormattedMessage
defaultMessage="Installed Plugins: "
id="admin.plugin.installedTitle"
values={Object {}}
/>
</label>
<div
className="col-sm-8"
>
<p
className="help-text"
>
<FormattedHTMLMessage
defaultMessage="Installed plugins on your Mattermost server. Pre-packaged plugins are installed by default, and can be deactivated but not removed."
id="admin.plugin.installedDesc"
values={Object {}}
/>
</p>
<br />
</div>
</div>
</form>
</div>
`;
exports[`components/PluginManagement should match snapshot, disabled 1`] = `
<div
className="wrapper--fixed"
>
<h3
className="admin-console-header"
>
<FormattedMessage
defaultMessage="Management"
id="admin.plugin.management.title"
values={Object {}}
/>
</h3>
<Banner
description={
<FormattedHTMLMessage
defaultMessage="Plugins are disabled on your server. To enable them, go to <strong>Plugins > Configuration</strong>."
id="admin.plugin.management.banner"
values={Object {}}
/>
}
title={<div />}
/>
</div>
`;
exports[`components/PluginManagement should match snapshot, upload disabled 1`] = `
<div
className="wrapper--fixed"
>
<h3
className="admin-console-header"
>
<FormattedMessage
defaultMessage="Management"
id="admin.plugin.management.title"
values={Object {}}
/>
</h3>
<form
className="form-horizontal"
role="form"
>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
>
<FormattedMessage
defaultMessage="Upload Plugin: "
id="admin.plugin.uploadTitle"
values={Object {}}
/>
</label>
<div
className="col-sm-8"
>
<div
className="file__upload"
>
<button
className="btn"
disabled={true}
>
<FormattedMessage
defaultMessage="Choose File"
id="admin.plugin.choose"
values={Object {}}
/>
</button>
<input
accept=".gz"
disabled={true}
onChange={[Function]}
type="file"
/>
</div>
<button
className="btn"
disabled={true}
onClick={[Function]}
>
<FormattedMessage
defaultMessage="Upload"
id="admin.plugin.upload"
values={Object {}}
/>
</button>
<div
className="help-text no-margin"
/>
<p
className="help-text"
>
<FormattedHTMLMessage
defaultMessage="To enable plugin uploads, go to <strong>Plugins > Configuration</strong>. See <a href=\\"https://about.mattermost.com/default-plugin-uploads\\" target=\\"_blank\\">documentation</a> to learn more."
id="admin.plugin.uploadDisabledDesc"
values={Object {}}
/>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="control-label col-sm-4"
>
<FormattedMessage
defaultMessage="Installed Plugins: "
id="admin.plugin.installedTitle"
values={Object {}}
/>
</label>
<div
className="col-sm-8"
>
<p
className="help-text"
>
<FormattedHTMLMessage
defaultMessage="Installed plugins on your Mattermost server. Pre-packaged plugins are installed by default, and can be deactivated but not removed."
id="admin.plugin.installedDesc"
values={Object {}}
/>
</p>
<br />
</div>
</div>
</form>
</div>
`;
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import PluginState from 'mattermost-redux/constants/plugins';
import PluginManagement from 'components/admin_console/plugin_management/plugin_management.jsx';
describe('components/PluginManagement', () => {
const defaultProps = {
config: {
PluginSettings: {
Enable: true,
EnableUploads: true,
},
},
pluginStatuses: {
plugin_0: {
id: 'plugin_0',
version: '0.1.0',
state: PluginState.PLUGIN_STATE_NOT_RUNNING,
name: 'Plugin 0',
description: 'The plugin 0.',
is_prepackaged: false,
active: false,
instances: [
{
cluster_id: 'cluster_id_1',
state: PluginState.PLUGIN_STATE_NOT_RUNNING,
version: '0.1.0',
},
],
},
plugin_1: {
id: 'plugin_1',
version: '0.0.1',
state: PluginState.PLUGIN_STATE_STOPPING,
name: 'Plugin 1',
description: 'The plugin.',
is_prepackaged: false,
active: true,
instances: [
{
cluster_id: 'cluster_id_1',
state: PluginState.PLUGIN_STATE_NOT_RUNNING,
version: '0.0.1',
},
{
cluster_id: 'cluster_id_2',
state: PluginState.PLUGIN_STATE_RUNNING,
version: '0.0.2',
},
],
},
},
actions: {
uploadPlugin: jest.fn(),
removePlugin: jest.fn(),
getPluginStatuses: jest.fn().mockResolvedValue([]),
activatePlugin: jest.fn(),
deactivatePlugin: jest.fn(),
},
};
test('should match snapshot', () => {
const props = {...defaultProps};
const wrapper = shallow(<PluginManagement {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, disabled', () => {
const props = {
...defaultProps,
config: {
...defaultProps.config,
PluginSettings: {
...defaultProps.config.PluginSettings,
Enable: false,
},
},
};
const wrapper = shallow(<PluginManagement {...props}/>);
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot, upload disabled', () => {
const props = {
...defaultProps,
config: {
...defaultProps.config,
PluginSettings: {
...defaultProps.config.PluginSettings,
EnableUploads: false,
},
},
};
const wrapper = shallow(<PluginManagement {...props}/>);
expect(wrapper).toMatchSnapshot();
});
});
......@@ -345,6 +345,7 @@ export const SocketEvents = {
PLUGIN_DEACTIVATED: 'plugin_deactivated',
LICENSE_CHANGED: 'license_changed',
CONFIG_CHANGED: 'config_changed',
PLUGIN_STATUSES_CHANGED: 'plugin_statuses_changed',
};
export const TutorialSteps = {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment