You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by am...@apache.org on 2018/10/10 14:39:04 UTC
[couchdb-fauxton] branch master updated: update cluster module to
redux (#1133)
This is an automated email from the ASF dual-hosted git repository.
amaranhao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb-fauxton.git
The following commit(s) were added to refs/heads/master by this push:
new 05dc83a update cluster module to redux (#1133)
05dc83a is described below
commit 05dc83a0c08fefad650d0fa7da36fdff529fa74f
Author: garren smith <ga...@gmail.com>
AuthorDate: Wed Oct 10 16:38:58 2018 +0200
update cluster module to redux (#1133)
* update cluster module to redux
* add error messages
---
app/addons/auth/actions.js | 10 ++--
app/addons/auth/components/changepasswordform.js | 15 +++---
app/addons/auth/components/createadminform.js | 12 +++--
app/addons/auth/routes/auth.js | 2 +-
app/addons/auth/routes/user.js | 2 +-
app/addons/cluster/__tests__/cluster.test.js | 14 ++----
app/addons/cluster/__tests__/resources.test.js | 56 ---------------------
.../cluster/{cluster.actions.js => actions.js} | 46 ++++++++++-------
.../{cluster.actiontypes.js => actiontypes.js} | 0
app/addons/cluster/{resources.js => api.js} | 46 ++++++++---------
app/addons/cluster/base.js | 6 +++
app/addons/cluster/cluster.js | 40 +++++----------
app/addons/cluster/cluster.stores.js | 57 ----------------------
.../{cluster.actiontypes.js => reducers.js} | 18 ++++++-
app/addons/cluster/routes.js | 23 ++++-----
app/addons/config/routes.js | 2 +-
app/addons/setup/route.js | 2 +-
app/constants.js | 3 +-
18 files changed, 123 insertions(+), 231 deletions(-)
diff --git a/app/addons/auth/actions.js b/app/addons/auth/actions.js
index a58cd09..1e094c9 100644
--- a/app/addons/auth/actions.js
+++ b/app/addons/auth/actions.js
@@ -11,7 +11,6 @@
// the License.
import FauxtonAPI from "../../core/api";
import app from "../../app";
-import ClusterStore from "../cluster/cluster.stores";
import ActionTypes from './actiontypes';
import Api from './api';
@@ -19,8 +18,6 @@ const {
AUTH_HIDE_PASSWORD_MODAL,
} = ActionTypes;
-const nodesStore = ClusterStore.nodesStore;
-
const errorHandler = ({ message }) => {
FauxtonAPI.addNotification({
msg: message,
@@ -69,11 +66,10 @@ export const login = (username, password, urlBack) => {
.catch(errorHandler);
};
-export const changePassword = (username, password, passwordConfirm) => () => {
+export const changePassword = (username, password, passwordConfirm, nodes) => () => {
if (!validatePasswords(password, passwordConfirm)) {
return errorHandler({message: app.i18n.en_US['auth-passwords-not-matching']});
}
- var nodes = nodesStore.getNodes();
//To change an admin's password is the same as creating an admin. So we just use the
//same api function call here.
Api.createAdmin({
@@ -90,8 +86,8 @@ export const changePassword = (username, password, passwordConfirm) => () => {
);
};
-export const createAdmin = (username, password, loginAfter) => () => {
- const node = nodesStore.getNodes()[0].node;
+export const createAdmin = (username, password, loginAfter, nodes) => () => {
+ const node = nodes[0].node;
if (!validateUser(username, password)) {
return errorHandler({message: app.i18n.en_US['auth-missing-credentials']});
}
diff --git a/app/addons/auth/components/changepasswordform.js b/app/addons/auth/components/changepasswordform.js
index 7ca0b8f..14943d7 100644
--- a/app/addons/auth/components/changepasswordform.js
+++ b/app/addons/auth/components/changepasswordform.js
@@ -43,7 +43,7 @@ export class ChangePasswordForm extends React.Component {
changePassword(e) {
e.preventDefault();
- this.props.changePassword(this.props.username, this.state.password, this.state.passwordConfirm);
+ this.props.changePassword(this.props.username, this.state.password, this.state.passwordConfirm, this.props.nodes);
}
render() {
@@ -87,11 +87,14 @@ export class ChangePasswordForm extends React.Component {
}
}
+const mapStateToProps = ({clusters}) => {
+ return {
+ nodes: clusters.nodes,
+ username: FauxtonAPI.session.user().name
+ };
+};
+
export default connect(
- () => {
- return {
- username: FauxtonAPI.session.user().name
- };
- },
+ mapStateToProps,
{changePassword}
)(ChangePasswordForm);
diff --git a/app/addons/auth/components/createadminform.js b/app/addons/auth/components/createadminform.js
index f92049b..333b419 100644
--- a/app/addons/auth/components/createadminform.js
+++ b/app/addons/auth/components/createadminform.js
@@ -13,7 +13,6 @@
import PropTypes from 'prop-types';
import React from "react";
-import ReactDOM from "react-dom";
import {
createAdmin
} from "./../actions";
@@ -45,7 +44,8 @@ export class CreateAdminForm extends React.Component {
this.props.createAdmin(
this.state.username,
this.state.password,
- this.props.loginAfter
+ this.props.loginAfter,
+ this.props.nodes
);
}
@@ -111,8 +111,14 @@ CreateAdminForm.defaultProps = {
loginAfter: false
};
+const mapStateToProps = ({clusters}) => {
+ return {
+ nodes: clusters.nodes
+ };
+};
+
export default connect(
- null,
+ mapStateToProps,
{createAdmin}
)(CreateAdminForm);
diff --git a/app/addons/auth/routes/auth.js b/app/addons/auth/routes/auth.js
index d2ebd64..d274c08 100644
--- a/app/addons/auth/routes/auth.js
+++ b/app/addons/auth/routes/auth.js
@@ -12,7 +12,7 @@
import React from "react";
import FauxtonAPI from "../../../core/api";
-import ClusterActions from "../../cluster/cluster.actions";
+import ClusterActions from "../../cluster/actions";
import { AuthLayout } from "./../layout";
import app from "../../../app";
import Components from "./../components";
diff --git a/app/addons/auth/routes/user.js b/app/addons/auth/routes/user.js
index 1934b44..36a8f53 100644
--- a/app/addons/auth/routes/user.js
+++ b/app/addons/auth/routes/user.js
@@ -12,7 +12,7 @@
import React from "react";
import FauxtonAPI from "../../../core/api";
-import ClusterActions from "../../cluster/cluster.actions";
+import ClusterActions from "../../cluster/actions";
import { AdminLayout } from "./../layout";
export default FauxtonAPI.RouteObject.extend({
diff --git a/app/addons/cluster/__tests__/cluster.test.js b/app/addons/cluster/__tests__/cluster.test.js
index dd4e313..d215e99 100644
--- a/app/addons/cluster/__tests__/cluster.test.js
+++ b/app/addons/cluster/__tests__/cluster.test.js
@@ -9,13 +9,13 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-import ClusterComponent from "../cluster";
-import ClusterActions from "../cluster.actions";
-import ClusterStores from "../cluster.stores";
+import FauxtonAPI from "../../../core/api";
+import {DisabledConfigController} from "../cluster";
import utils from "../../../../test/mocha/testUtils";
import React from "react";
import ReactDOM from "react-dom";
import {mount} from 'enzyme';
+import sinon from 'sinon';
const assert = utils.assert;
@@ -23,6 +23,7 @@ describe('Cluster Controller', () => {
let controller;
beforeEach(() => {
+ FauxtonAPI.reduxDispatch = sinon.stub();
var nodeList = [
{'node': 'node1@127.0.0.1', 'isInCluster': true},
@@ -33,16 +34,11 @@ describe('Cluster Controller', () => {
{'node': 'node3@127.0.0.1', 'isInCluster': false}
];
- ClusterActions.updateNodes({nodes: nodeList});
controller = mount(
- <ClusterComponent.DisabledConfigController />
+ <DisabledConfigController nodes={nodeList} />
);
});
- afterEach(() => {
- ClusterStores.nodesStore.reset();
- });
-
it('renders the amount of nodes', () => {
assert.ok(/6 nodes/.test(controller.text()), 'finds 6 nodes');
});
diff --git a/app/addons/cluster/__tests__/resources.test.js b/app/addons/cluster/__tests__/resources.test.js
deleted file mode 100644
index 4dd3774..0000000
--- a/app/addons/cluster/__tests__/resources.test.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-import testUtils from "../../../../test/mocha/testUtils";
-import Resources from "../resources";
-var assert = testUtils.assert;
-
-
-describe('Membership Model', () => {
- const data = {
- 'all_nodes': ['node1@127.0.0.1', 'node2@127.0.0.1', 'node3@127.0.0.1', 'notpartofclusternode'],
- 'cluster_nodes': ['node1@127.0.0.1', 'node2@127.0.0.1', 'node3@127.0.0.1']
- };
-
- it('reorders the data', () => {
- const memberships = new Resources.ClusterNodes();
- const res = memberships.parse(data);
-
- assert.deepEqual([
- {node: 'node1@127.0.0.1', isInCluster: true},
- {node: 'node2@127.0.0.1', isInCluster: true},
- {node: 'node3@127.0.0.1', isInCluster: true},
- {node: 'notpartofclusternode', isInCluster: false}
- ],
- res.nodes_mapped);
- });
-
- it('keeps the exiting data', () => {
- const memberships = new Resources.ClusterNodes();
- const res = memberships.parse(data);
-
- assert.deepEqual([
- 'node1@127.0.0.1',
- 'node2@127.0.0.1',
- 'node3@127.0.0.1',
- 'notpartofclusternode'
- ],
- res.all_nodes);
-
- assert.deepEqual([
- 'node1@127.0.0.1',
- 'node2@127.0.0.1',
- 'node3@127.0.0.1'
- ],
- res.cluster_nodes);
- });
-});
diff --git a/app/addons/cluster/cluster.actions.js b/app/addons/cluster/actions.js
similarity index 52%
rename from app/addons/cluster/cluster.actions.js
rename to app/addons/cluster/actions.js
index b0ca2a5..60ec0c9 100644
--- a/app/addons/cluster/cluster.actions.js
+++ b/app/addons/cluster/actions.js
@@ -11,38 +11,48 @@
// the License.
import FauxtonAPI from "../../core/api";
-import ClusterResources from "./resources";
-import ActionTypes from "./cluster.actiontypes";
+import getNodes from "./api";
+import ActionTypes from "./actiontypes";
export default {
- fetchNodes: function () {
- var memberships = new ClusterResources.ClusterNodes();
-
- memberships.fetch().then(() => {
+ fetchNodes () {
+ getNodes().then((nodes) => {
this.updateNodes({
- nodes: memberships.get('nodes_mapped')
+ nodes: nodes.nodes_mapped
+ });
+ })
+ .catch(err => {
+ FauxtonAPI.addNotification({
+ type: 'error',
+ msg: err.message,
+ clear: true
+ });
});
- });
},
- updateNodes: function (options) {
- FauxtonAPI.dispatch({
+ updateNodes (options) {
+ FauxtonAPI.reduxDispatch({
type: ActionTypes.CLUSTER_FETCH_NODES,
options: options
});
},
- navigateToNodeBasedOnNodeCount: function (successtarget) {
- var memberships = new ClusterResources.ClusterNodes();
+ navigateToNodeBasedOnNodeCount (successtarget) {
+ getNodes().then((nodes) => {
+ const allNodes = nodes.all_nodes;
- memberships.fetch().then(function () {
- const nodes = memberships.get('all_nodes');
-
- if (nodes.length === 1) {
- return FauxtonAPI.navigate(successtarget + nodes[0]);
+ if (allNodes.length === 1) {
+ return FauxtonAPI.navigate(successtarget + allNodes[0]);
}
return FauxtonAPI.navigate('/cluster/disabled', {trigger: true});
- });
+ })
+ .catch(err => {
+ FauxtonAPI.addNotification({
+ type: 'error',
+ msg: err.message,
+ clear: true
+ });
+ });
}
};
diff --git a/app/addons/cluster/cluster.actiontypes.js b/app/addons/cluster/actiontypes.js
similarity index 100%
copy from app/addons/cluster/cluster.actiontypes.js
copy to app/addons/cluster/actiontypes.js
diff --git a/app/addons/cluster/resources.js b/app/addons/cluster/api.js
similarity index 51%
rename from app/addons/cluster/resources.js
rename to app/addons/cluster/api.js
index 95b7ccb..a6c05a1 100644
--- a/app/addons/cluster/resources.js
+++ b/app/addons/cluster/api.js
@@ -10,29 +10,25 @@
// License for the specific language governing permissions and limitations under
// the License.
-import FauxtonAPI from "../../core/api";
import Helpers from "../../helpers";
-
-var Cluster = FauxtonAPI.addon();
-
-Cluster.ClusterNodes = Backbone.Model.extend({
- url: function () {
- return Helpers.getServerUrl('/_membership');
- },
-
- parse: function (res) {
- var list;
-
- list = res.all_nodes.reduce(function (acc, node) {
- var isInCluster = res.cluster_nodes.indexOf(node) !== -1;
-
- acc.push({node: node, isInCluster: isInCluster});
- return acc;
- }, []);
-
- res.nodes_mapped = list;
- return res;
- }
-});
-
-export default Cluster;
+import {get} from '../../core/ajax';
+
+export default () => {
+ return get(Helpers.getServerUrl('/_membership'))
+ .then(res => {
+ if (!res.all_nodes) {
+ const details = res.reason ? res.reason : '';
+ throw new Error('Failed to load list of nodes.' + details);
+ }
+
+ const list = res.all_nodes.reduce(function (acc, node) {
+ var isInCluster = res.cluster_nodes.indexOf(node) !== -1;
+
+ acc.push({node: node, isInCluster: isInCluster});
+ return acc;
+ }, []);
+
+ res.nodes_mapped = list;
+ return res;
+ });
+};
diff --git a/app/addons/cluster/base.js b/app/addons/cluster/base.js
index 338334d..d61ca99 100644
--- a/app/addons/cluster/base.js
+++ b/app/addons/cluster/base.js
@@ -11,7 +11,13 @@
// the License.
import Cluster from "./routes";
+import FauxtonAPI from "../../core/api";
+import reducers from './reducers';
Cluster.initialize = function () {};
+FauxtonAPI.addReducers({
+ clusters: reducers
+});
+
export default Cluster;
diff --git a/app/addons/cluster/cluster.js b/app/addons/cluster/cluster.js
index 625e866..573e37a 100644
--- a/app/addons/cluster/cluster.js
+++ b/app/addons/cluster/cluster.js
@@ -11,32 +11,9 @@
// the License.
import React from "react";
-import ClusterStore from "./cluster.stores";
-
-var nodesStore = ClusterStore.nodesStore;
-
-
-class DisabledConfigController extends React.Component {
- getStoreState = () => {
- return {
- nodes: nodesStore.getNodes()
- };
- };
-
- componentDidMount() {
- nodesStore.on('change', this.onChange, this);
- }
-
- componentWillUnmount() {
- nodesStore.off('change', this.onChange);
- }
-
- onChange = () => {
- this.setState(this.getStoreState());
- };
-
- state = this.getStoreState();
+import {connect} from 'react-redux';
+export class DisabledConfigController extends React.Component {
render() {
return (
<div className="config-warning-cluster-wrapper">
@@ -45,7 +22,7 @@ class DisabledConfigController extends React.Component {
<div className="config-warning-icon-container pull-left">
<i className="fonticon-attention-circled"></i>
</div>
- It seems that you are running a cluster with {this.state.nodes.length} nodes. For CouchDB 2.0
+ It seems that you are running a cluster with {this.props.nodes.length} nodes. For CouchDB 2.0
we recommend using a configuration management tools like Chef, Ansible,
Puppet or Salt (in no particular order) to configure your nodes in a cluster.
<br/>
@@ -61,8 +38,13 @@ class DisabledConfigController extends React.Component {
}
}
-var Views = {
- DisabledConfigController: DisabledConfigController
+const mapStateToProps = ({clusters}) => {
+ return {
+ nodes: clusters.nodes
+ };
};
-export default Views;
+export default connect(
+ mapStateToProps,
+ null,
+)(DisabledConfigController);
diff --git a/app/addons/cluster/cluster.stores.js b/app/addons/cluster/cluster.stores.js
deleted file mode 100644
index 27d4557..0000000
--- a/app/addons/cluster/cluster.stores.js
+++ /dev/null
@@ -1,57 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-import FauxtonAPI from "../../core/api";
-import ActionTypes from "./cluster.actiontypes";
-
-var NodesStore = FauxtonAPI.Store.extend({
-
- initialize: function () {
- this.reset();
- },
-
- reset: function () {
- this._nodes = [];
- },
-
- setNodes: function (options) {
- this._nodes = options.nodes;
- },
-
- getNodes: function () {
- return this._nodes;
- },
-
- dispatch: function (action) {
-
- switch (action.type) {
- case ActionTypes.CLUSTER_FETCH_NODES:
- this.setNodes(action.options);
- break;
-
- default:
- return;
- }
-
- this.triggerChange();
- }
-
-});
-
-
-var nodesStore = new NodesStore();
-
-nodesStore.dispatchToken = FauxtonAPI.dispatcher.register(nodesStore.dispatch.bind(nodesStore));
-
-export default {
- nodesStore: nodesStore
-};
diff --git a/app/addons/cluster/cluster.actiontypes.js b/app/addons/cluster/reducers.js
similarity index 65%
rename from app/addons/cluster/cluster.actiontypes.js
rename to app/addons/cluster/reducers.js
index 525e7f6..8a48c7a 100644
--- a/app/addons/cluster/cluster.actiontypes.js
+++ b/app/addons/cluster/reducers.js
@@ -10,6 +10,20 @@
// License for the specific language governing permissions and limitations under
// the License.
-export default {
- CLUSTER_FETCH_NODES: 'CLUSTER_FETCH_NODES'
+import ActionTypes from "./actiontypes";
+
+const initialState = {
+ nodes: []
+};
+
+export default (state = initialState, {type, options}) => {
+ switch (type) {
+ case ActionTypes.CLUSTER_FETCH_NODES:
+ return {
+ ...state,
+ nodes: options.nodes
+ };
+ }
+
+ return state;
};
diff --git a/app/addons/cluster/routes.js b/app/addons/cluster/routes.js
index 339360f..37ed665 100644
--- a/app/addons/cluster/routes.js
+++ b/app/addons/cluster/routes.js
@@ -12,9 +12,9 @@
import React from 'react';
import FauxtonAPI from "../../core/api";
-import Cluster from "./resources";
-import ClusterComponents from "./cluster";
-import ClusterActions from "./cluster.actions";
+import Helpers from "../../helpers";
+import DisabledConfigController from "./cluster";
+import ClusterActions from "./actions";
import {OnePaneSimpleLayout} from '../components/layouts';
@@ -25,17 +25,12 @@ var ConfigDisabledRouteObject = FauxtonAPI.RouteObject.extend({
'cluster/disabled': 'showDisabledFeatureScreen'
},
- apiUrl: function () {
- return [this.memberships.url('apiurl'), this.memberships.documentation];
- },
-
showDisabledFeatureScreen: function () {
- const memberships = new Cluster.ClusterNodes();
ClusterActions.fetchNodes();
return <OnePaneSimpleLayout
- component={<ClusterComponents.DisabledConfigController/>}
- endpoint={memberships.url('apiurl')}
- docURL={memberships.documentation}
+ component={<DisabledConfigController/>}
+ endpoint={Helpers.getServerUrl('/_membership')}
+ docURL={FauxtonAPI.constants.DOC_URLS.MEMBERSHIP}
crumbs={[
{ name: 'Config disabled' }
]}
@@ -43,6 +38,6 @@ var ConfigDisabledRouteObject = FauxtonAPI.RouteObject.extend({
}
});
-Cluster.RouteObjects = [ConfigDisabledRouteObject];
-
-export default Cluster;
+export default {
+ RouteObjects: [ConfigDisabledRouteObject]
+};
diff --git a/app/addons/config/routes.js b/app/addons/config/routes.js
index 4b924d4..4a21749 100644
--- a/app/addons/config/routes.js
+++ b/app/addons/config/routes.js
@@ -13,7 +13,7 @@
import React from 'react';
import FauxtonAPI from "../../core/api";
import Config from "./resources";
-import ClusterActions from "../cluster/cluster.actions";
+import ClusterActions from "../cluster/actions";
import ConfigActions from "./actions";
import Layout from './layout';
diff --git a/app/addons/setup/route.js b/app/addons/setup/route.js
index 945f3e4..38752a1 100644
--- a/app/addons/setup/route.js
+++ b/app/addons/setup/route.js
@@ -13,7 +13,7 @@
import React from 'react';
import app from "../../app";
import FauxtonAPI from "../../core/api";
-import ClusterActions from "../cluster/cluster.actions";
+import ClusterActions from "../cluster/actions";
import {OnePaneSimpleLayout} from '../components/layouts';
import ConfiguredScreenContainer from './container/ConfiguredSceenContainer';
diff --git a/app/constants.js b/app/constants.js
index 66fdc2e..7f02640 100644
--- a/app/constants.js
+++ b/app/constants.js
@@ -46,6 +46,7 @@ export default {
MANGO_INDEX:'./docs/intro/api.html#documents',
MANGO_SEARCH:'./docs/intro/api.html#documents',
CHANGES:'./docs/api/database/changes.html?highlight=changes#post--db-_changes',
- SETUP: './docs/cluster/setup.html#the-cluster-setup-wizard'
+ SETUP: './docs/cluster/setup.html#the-cluster-setup-wizard',
+ MEMBERSHIP: './docs/cluster/nodes.html'
}
};