Merge pull request #646 from influxdata/redux_sources

store sources in redux
pull/657/head
Nathan Haugo 2016-12-06 11:37:24 -08:00 committed by GitHub
commit 08018ea577
8 changed files with 166 additions and 37 deletions

View File

@ -1,7 +1,10 @@
import React, {PropTypes} from 'react';
import {withRouter} from 'react-router';
import {connect} from 'react-redux';
import {getSources} from 'src/shared/apis';
import {loadSources as loadSourcesAction} from 'src/shared/actions/sources';
import {showDatabases} from 'src/shared/apis/metaQuery';
import {bindActionCreators} from 'redux';
const {bool, number, string, node, func, shape} = PropTypes;
@ -21,6 +24,8 @@ const CheckSources = React.createClass({
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
sources: PropTypes.array.isRequired,
loadSourcesAction: PropTypes.func.isRequired,
},
contextTypes: {
@ -35,13 +40,13 @@ const CheckSources = React.createClass({
getInitialState() {
return {
isFetching: true,
sources: [],
};
},
componentDidMount() {
getSources().then(({data: {sources}}) => {
this.setState({sources, isFetching: false});
this.props.loadSourcesAction(sources);
this.setState({isFetching: false});
}).catch(() => {
this.props.addFlashMessage({type: 'error', text: "Unable to connect to Chronograf server"});
this.setState({isFetching: false});
@ -49,8 +54,8 @@ const CheckSources = React.createClass({
},
componentWillUpdate(nextProps, nextState) {
const {router, location, params, addFlashMessage} = nextProps;
const {isFetching, sources} = nextState;
const {router, location, params, addFlashMessage, sources} = nextProps;
const {isFetching} = nextState;
const source = sources.find((s) => s.id === params.sourceID);
if (!isFetching && !source) {
return router.push(`/sources/new?redirectPath=${location.pathname}`);
@ -65,8 +70,8 @@ const CheckSources = React.createClass({
},
render() {
const {params} = this.props;
const {isFetching, sources} = this.state;
const {params, sources} = this.props;
const {isFetching} = this.state;
const source = sources.find((s) => s.id === params.sourceID);
if (isFetching || !source) {
@ -79,4 +84,16 @@ const CheckSources = React.createClass({
},
});
export default withRouter(CheckSources);
function mapStateToProps(state) {
return {
sources: state.sources,
};
}
function mapDispatchToProps(dispatch) {
return {
loadSourcesAction: bindActionCreators(loadSourcesAction, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(CheckSources));

View File

@ -0,0 +1,35 @@
export function loadSources(sources) {
return {
type: 'LOAD_SOURCES',
payload: {
sources,
},
};
}
export function updateSource(source) {
return {
type: 'SOURCE_UPDATED',
payload: {
source,
},
};
}
export function removeSource(source) {
return {
type: 'SOURCE_REMOVED',
payload: {
source,
},
};
}
export function addSource(source) {
return {
type: 'SOURCE_ADDED',
payload: {
source,
},
};
}

View File

@ -1,7 +1,9 @@
import me from './me';
import notifications from './notifications';
import sources from './sources';
export {
me,
notifications,
sources,
};

View File

@ -0,0 +1,29 @@
export default function sources(state = [], action) {
switch (action.type) {
case 'LOAD_SOURCES': {
return action.payload.sources;
}
case 'SOURCE_UPDATED': {
const {source} = action.payload;
const updatedIndex = state.findIndex((s) => s.id === source.id);
const updatedSources = Object.assign({}, state, {
[updatedIndex]: source,
});
return updatedSources;
}
case 'SOURCE_REMOVED': {
const {source} = action.payload;
const updatedSources = state.filter((s) => s.id !== source.id);
return updatedSources;
}
case 'SOURCE_ADDED': {
const {source} = action.payload;
return state.concat([source]);
}
}
return state;
}

View File

@ -1,6 +1,9 @@
import React, {PropTypes} from 'react';
import {withRouter} from 'react-router';
import {addSource as addSourceAction} from 'src/shared/actions/sources';
import {createSource} from 'shared/apis';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
export const CreateSource = React.createClass({
propTypes: {
@ -12,6 +15,7 @@ export const CreateSource = React.createClass({
redirectPath: PropTypes.string,
}).isRequired,
}).isRequired,
addSourceAction: PropTypes.func,
},
handleNewSource(e) {
@ -25,6 +29,7 @@ export const CreateSource = React.createClass({
telegraf: this.sourceTelegraf.value,
};
createSource(source).then(({data: sourceFromServer}) => {
this.props.addSourceAction(sourceFromServer);
this.redirectToApp(sourceFromServer);
});
},
@ -90,4 +95,14 @@ export const CreateSource = React.createClass({
},
});
export default withRouter(CreateSource);
function mapStateToProps(_) {
return {};
}
function mapDispatchToProps(dispatch) {
return {
addSourceAction: bindActionCreators(addSourceAction, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(CreateSource));

View File

@ -1,7 +1,12 @@
import React, {PropTypes} from 'react';
import _ from 'lodash';
import {withRouter, Link} from 'react-router';
import {getSources, getKapacitor, deleteSource} from 'shared/apis';
import {getKapacitor, deleteSource} from 'shared/apis';
import {
loadSources as loadSourcesAction,
removeSource as removeSourceAction,
} from 'src/shared/actions/sources';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
export const ManageSources = React.createClass({
propTypes: {
@ -15,33 +20,24 @@ export const ManageSources = React.createClass({
self: PropTypes.string.isRequired,
}),
}),
sources: PropTypes.array,
addFlashMessage: PropTypes.func,
loadSourcesAction: PropTypes.func.isRequired,
removeSourceAction: PropTypes.func.isRequired,
},
getInitialState() {
return {
sources: [],
kapacitors: {},
};
},
componentDidMount() {
getSources().then(({data: {sources}}) => {
this.setState({sources}, () => {
sources.forEach((source) => {
getKapacitor(source).then((kapacitor) => {
this.setState((prevState) => {
const newSources = prevState.sources.map((newSource) => {
if (newSource.id !== source.id) {
return newSource;
}
return Object.assign({}, newSource, {kapacitor});
});
return Object.assign({}, prevState, {sources: newSources});
});
});
});
this.props.sources.forEach((source) => {
const kapacitors = {};
getKapacitor(source).then((kapacitor) => {
kapacitors[source.id] = kapacitor;
});
this.setState(kapacitors);
});
},
@ -49,8 +45,7 @@ export const ManageSources = React.createClass({
const {addFlashMessage} = this.props;
deleteSource(source).then(() => {
const updatedSourceList = this.state.sources.filter((s) => s.id !== source.id);
this.setState({sources: updatedSourceList});
removeSourceAction(source);
addFlashMessage({type: 'success', text: 'Source removed from Chronograf'});
}).catch(() => {
addFlashMessage({type: 'error', text: 'Could not remove source from Chronograf'});
@ -58,7 +53,8 @@ export const ManageSources = React.createClass({
},
render() {
const {sources} = this.state;
const {kapacitors} = this.state;
const {sources} = this.props;
const {pathname} = this.props.location;
const numSources = sources.length;
const sourcesTitle = `${numSources} ${numSources === 1 ? 'Source' : 'Sources'}`;
@ -96,11 +92,12 @@ export const ManageSources = React.createClass({
<tbody>
{
sources.map((source) => {
const kapacitorName = kapacitors[source.id] ? kapacitors[source.id].name : '';
return (
<tr key={source.id}>
<td>{source.name}{source.default ? <span className="default-source-label">Default</span> : null}</td>
<td>{source.url}</td>
<td>{_.get(source, ['kapacitor', 'name'], '')}</td>
<td>{kapacitorName}</td>
<td className="text-right">
<Link className="btn btn-info btn-xs" to={`${pathname}/${source.id}/edit`}><span className="icon pencil"></span></Link>
<Link className="btn btn-success btn-xs" to={`/sources/${source.id}/hosts`}>Connect</Link>
@ -124,4 +121,17 @@ export const ManageSources = React.createClass({
},
});
export default withRouter(ManageSources);
function mapStateToProps(state) {
return {
sources: state.sources,
};
}
function mapDispatchToProps(dispatch) {
return {
loadSourcesAction: bindActionCreators(loadSourcesAction, dispatch),
removeSourceAction: bindActionCreators(removeSourceAction, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ManageSources));

View File

@ -1,7 +1,13 @@
import React, {PropTypes} from 'react';
import {withRouter} from 'react-router';
import {getSource, createSource, updateSource} from 'shared/apis';
import {
addSource as addSourceAction,
updateSource as updateSourceAction,
} from 'shared/actions/sources';
import classNames from 'classnames';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
export const SourceForm = React.createClass({
propTypes: {
@ -17,6 +23,8 @@ export const SourceForm = React.createClass({
}).isRequired,
}).isRequired,
addFlashMessage: PropTypes.func.isRequired,
addSourceAction: PropTypes.func,
updateSourceAction: PropTypes.func,
},
getInitialState() {
@ -47,14 +55,16 @@ export const SourceForm = React.createClass({
telegraf: this.sourceTelegraf.value,
});
if (this.state.editMode) {
updateSource(newSource).then(() => {
updateSource(newSource).then(({data: sourceFromServer}) => {
this.props.updateSourceAction(sourceFromServer);
router.push(`/sources/${params.sourceID}/manage-sources`);
addFlashMessage({type: 'success', text: 'The source was successfully updated'});
}).catch(() => {
addFlashMessage({type: 'error', text: 'There was a problem updating the source. Check the settings'});
});
} else {
createSource(newSource).then(() => {
createSource(newSource).then(({data: sourceFromServer}) => {
this.props.addSourceAction(sourceFromServer);
router.push(`/sources/${params.sourceID}/manage-sources`);
addFlashMessage({type: 'success', text: 'The source was successfully created'});
}).catch(() => {
@ -142,4 +152,16 @@ export const SourceForm = React.createClass({
);
},
});
export default withRouter(SourceForm);
function mapStateToProps(_) {
return {};
}
function mapDispatchToProps(dispatch) {
return {
addSourceAction: bindActionCreators(addSourceAction, dispatch),
updateSourceAction: bindActionCreators(updateSourceAction, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(SourceForm));

View File

@ -28,4 +28,3 @@ export default function configureStore(initialState) {
initialState,
);
}