Merge pull request #879 from influxdata/kap-defaults

Refactor Kapacitor Settings Page
pull/10616/head
Chris Goller 2017-02-15 09:04:37 -06:00 committed by GitHub
commit fe8756ad5c
4 changed files with 232 additions and 172 deletions

View File

@ -1,6 +1,9 @@
## v1.2.0 [unreleased]
### Bug Fixes
1. [#879](https://github.com/influxdata/chronograf/pull/879): Fix several kapacitor configuration page state bugs; [#875](https://github.com/influxdata/chronograf/issues/875), [#876](https://github.com/influxdata/chronograf/issues/876), [#878](https://github.com/influxdata/chronograf/issues/878)
2. [#872](https://github.com/influxdata/chronograf/pull/872): Fix incorrect data source response
### Features
### UI Improvements

View File

@ -0,0 +1,146 @@
import React, {PropTypes} from 'react';
import AlertOutputs from './AlertOutputs';
const {
func,
shape,
string,
bool,
} = PropTypes;
const KapacitorForm = React.createClass({
propTypes: {
onSubmit: func.isRequired,
onInputChange: func.isRequired,
onReset: func.isRequired,
kapacitor: shape({
url: string.isRequired,
name: string.isRequired,
username: string,
password: string,
}).isRequired,
source: shape({}).isRequired,
addFlashMessage: func.isRequired,
exists: bool.isRequired,
},
render() {
const {onInputChange, onReset, kapacitor, source, onSubmit} = this.props;
const {url, name, username, password} = kapacitor;
return (
<div className="page">
<div className="page-header">
<div className="page-header__container">
<div className="page-header__left">
<h1>
Configure Kapacitor
</h1>
</div>
</div>
</div>
<div className="page-contents">
<div className="container-fluid">
<div className="row">
<div className="col-md-8 col-md-offset-2">
<div className="panel panel-minimal">
<div className="panel-body">
<p>
Kapacitor is used as the monitoring and alerting agent.
This page will let you configure which Kapacitor to use and
set up alert end points like email, Slack, and others.
</p>
<hr/>
<h4 className="text-center">Connect Kapacitor to Source</h4>
<h4 className="text-center">{source.url}</h4>
<br/>
<form onSubmit={onSubmit}>
<div>
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-2">
<label htmlFor="url">Kapacitor URL</label>
<input
className="form-control"
id="url"
name="url"
placeholder={url}
value={url}
onChange={onInputChange}>
</input>
</div>
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-0">
<label htmlFor="name">Name</label>
<input
className="form-control"
id="name"
name="name"
placeholder={name}
value={name}
onChange={onInputChange}>
</input>
</div>
<div className="form-group col-xs-12 col-sm-4 col-sm-offset-2 col-md-4 col-md-offset-2">
<label htmlFor="username">Username</label>
<input
className="form-control"
id="username"
name="username"
placeholder="username"
value={username}
onChange={onInputChange}>
</input>
</div>
<div className="form-group col-xs-12 col-sm-4 col-md-4">
<label htmlFor="password">Password</label>
<input
className="form-control"
id="password"
type="password"
name="password"
placeholder="password"
value={password}
onChange={onInputChange}
/>
</div>
</div>
<div className="form-group form-group-submit col-xs-12 text-center">
<button className="btn btn-info" onClick={onReset}>Reset to Default</button>
<button className="btn btn-success" type="submit">Connect Kapacitor</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md-8 col-md-offset-2">
{this.renderAlertOutputs()}
</div>
</div>
</div>
</div>
</div>
);
},
// TODO: move these to another page. they dont belong on this page
renderAlertOutputs() {
const {exists, kapacitor, addFlashMessage, source} = this.props;
if (exists) {
return <AlertOutputs source={source} kapacitor={kapacitor} addFlashMessage={addFlashMessage} />;
}
return (
<div className="panel panel-minimal">
<div className="panel-body">
<h4 className="text-center">Configure Alert Endpoints</h4>
<br/>
<p className="text-center">Set your Kapacitor connection info to configure alerting endpoints.</p>
</div>
</div>
);
},
});
export default KapacitorForm;

View File

@ -1,36 +1,50 @@
import React, {PropTypes} from 'react';
import {getKapacitor, getKapacitorConfigSection, createKapacitor, updateKapacitor, pingKapacitor} from 'shared/apis';
import AlertOutputs from '../components/AlertOutputs';
// default values for name & url
const defaultKapacitorName = "My Kapacitor";
const defaultKapacitorUrl = "http://localhost:9092";
import {
getKapacitor,
createKapacitor,
updateKapacitor,
pingKapacitor,
} from 'shared/apis';
import KapacitorForm from '../components/KapacitorForm';
const defaultName = "My Kapacitor";
const kapacitorPort = "9092";
const {
func,
shape,
string,
} = PropTypes;
export const KapacitorPage = React.createClass({
propTypes: {
source: PropTypes.shape({
id: PropTypes.string.isRequired,
source: shape({
id: string.isRequired,
url: string.isRequired,
}),
addFlashMessage: PropTypes.func,
addFlashMessage: func,
},
getInitialState() {
return {
kapacitor: null,
canConnect: false,
kapacitor: {
url: this._parseKapacitorURL(),
name: defaultName,
username: '',
password: '',
},
exists: false,
};
},
componentDidMount() {
this.fetchKapacitor();
},
fetchKapacitor() {
const {source} = this.props;
getKapacitor(source).then((kapacitor) => {
if (!kapacitor) {
return;
}
this.setState({kapacitor}, () => {
this.setState({kapacitor, exists: true}, () => {
pingKapacitor(kapacitor).catch(() => {
this.props.addFlashMessage({type: 'error', text: 'Could not connect to Kapacitor. Check settings.'});
});
@ -38,176 +52,73 @@ export const KapacitorPage = React.createClass({
});
},
componentDidUpdate(prevProps, prevState) {
if (!prevState.kapacitor || !this.state.kapacitor) {
return;
}
if (prevState.kapacitor.url !== this.state.kapacitor.url) {
this.checkKapacitorSetup();
}
render() {
const {source, addFlashMessage} = this.props;
const {kapacitor, exists} = this.state;
return (
<KapacitorForm
onSubmit={this.handleSubmit}
onInputChange={this.handleInputChange}
onReset={this.handleResetToDefaults}
kapacitor={kapacitor}
source={source}
addFlashMessage={addFlashMessage}
exists={exists}
/>
);
},
checkKapacitorSetup() {
const {addFlashMessage, source} = this.props;
getKapacitorConfigSection(this.state.kapacitor, 'influxdb').then(({data: {elements}}) => {
const sourceMatch = elements[0].options.urls.some((url) => url === source.url);
if (!sourceMatch) {
addFlashMessage({type: 'warning', text: `Warning: Kapacitor is configured to use an instance of InfluxDB which does not match the URL of your current source. Please ensure your InfluxDB source and Kapacitor's InfluxDB configuration point to the same server.`});
}
}).catch(() => {
addFlashMessage({type: 'error', text: `Could not connect to Kapacitor. Check connection settings.`});
handleInputChange(e) {
const val = e.target.value;
const name = e.target.name;
this.setState((prevState) => {
const update = {[name]: val.trim()};
return {kapacitor: {...prevState.kapacitor, ...update}};
});
},
handleKapacitorUpdate(e) {
handleSubmit(e) {
e.preventDefault();
if (this.state.kapacitor) {
this.handleUpdateKapacitor();
} else {
this.handleCreateKapacitor();
}
},
handleCreateKapacitor() {
const {addFlashMessage, source} = this.props;
const {newURL, newName, newUsername} = this.state;
createKapacitor(source, {
url: newURL.trim(),
name: newName.trim(),
username: newUsername,
password: this.kapacitorPassword.value,
}).then(({data: createdKapacitor}) => {
addFlashMessage({type: 'success', text: 'Kapacitor Created!'});
this.setState({kapacitor: createdKapacitor});
}).catch(() => {
this.props.addFlashMessage({type: 'error', text: 'There was a problem creating the Kapacitor record'});
});
},
const {kapacitor, exists} = this.state;
handleUpdateKapacitor() {
const {addFlashMessage} = this.props;
const {kapacitor, newURL, newName, newUsername} = this.state;
updateKapacitor(kapacitor, {
url: (newURL || kapacitor.url).trim(),
name: (newName || kapacitor.name).trim(),
username: newUsername || kapacitor.username,
password: this.kapacitorPassword.value,
}).then(({data: newKapacitor}) => {
if (exists) {
updateKapacitor(kapacitor).then(() => {
addFlashMessage({type: 'success', text: 'Kapacitor Updated!'});
this.setState({kapacitor: newKapacitor});
}).catch(() => {
addFlashMessage({type: 'error', text: 'There was a problem updating the Kapacitor record'});
});
},
updateName() {
this.setState({newName: this.kapacitorName.value});
},
updateURL() {
this.setState({newURL: this.kapacitorURL.value});
},
updateUsername() {
this.setState({newUsername: this.kapacitorUser.value});
} else {
createKapacitor(source, kapacitor).then(({data}) => {
// need up update kapacitor with info from server to AlertOutputs
this.setState({kapacitor: data, exists: true});
addFlashMessage({type: 'success', text: 'Kapacitor Created!'});
}).catch(() => {
addFlashMessage({type: 'error', text: 'There was a problem creating the Kapacitor record'});
});
}
},
handleResetToDefaults(e) {
e.preventDefault();
this.setState({
newURL: defaultKapacitorUrl,
newName: defaultKapacitorName,
});
const defaultState = {
url: this._parseKapacitorURL(),
name: defaultName,
username: '',
password: '',
};
this.setState({kapacitor: {...defaultState}});
},
render() {
const {kapacitor, newName, newURL, newUsername} = this.state;
// if the fields in state are defined, use them. otherwise use the defaults
const name = newName === undefined ? kapacitor && kapacitor.name || defaultKapacitorName : newName;
const url = newURL === undefined ? kapacitor && kapacitor.url || defaultKapacitorUrl : newURL;
const username = newUsername === undefined ? kapacitor && kapacitor.username || '' : newUsername;
_parseKapacitorURL() {
const parser = document.createElement('a');
parser.href = this.props.source.url;
return (
<div className="page">
<div className="page-header">
<div className="page-header__container">
<div className="page-header__left">
<h1>
Configure Kapacitor
</h1>
</div>
</div>
</div>
<div className="page-contents">
<div className="container-fluid">
<div className="row">
<div className="col-md-8 col-md-offset-2">
<div className="panel panel-minimal">
<div className="panel-body">
<p>
Kapacitor is used as the monitoring and alerting agent.
This page will let you configure which Kapacitor to use and
set up alert end points like email, Slack, and others.
</p>
<hr/>
<h4 className="text-center">Connection Details</h4>
<br/>
<form onSubmit={this.handleKapacitorUpdate}>
<div>
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-2">
<label htmlFor="connect-string">Connection String</label>
<input ref={(r) => this.kapacitorURL = r} className="form-control" id="connect-string" placeholder={defaultKapacitorUrl} value={url} onChange={this.updateURL}></input>
</div>
<div className="form-group col-xs-12 col-sm-8 col-sm-offset-2 col-md-4 col-md-offset-0">
<label htmlFor="name">Name</label>
<input ref={(r) => this.kapacitorName = r} className="form-control" id="name" placeholder={defaultKapacitorName} value={name} onChange={this.updateName}></input>
</div>
<div className="form-group col-xs-12 col-sm-4 col-sm-offset-2 col-md-4 col-md-offset-2">
<label htmlFor="username">Username</label>
<input ref={(r) => this.kapacitorUser = r} className="form-control" id="username" value={username} onChange={this.updateUsername}></input>
</div>
<div className="form-group col-xs-12 col-sm-4 col-md-4">
<label htmlFor="password">Password</label>
<input ref={(r) => this.kapacitorPassword = r} className="form-control" id="password" type="password"></input>
</div>
</div>
<div className="form-group form-group-submit col-xs-12 text-center">
<button className="btn btn-info" onClick={this.handleResetToDefaults}>Reset to Default</button>
<button className="btn btn-success" type="submit">Connect Kapacitor</button>
</div>
</form>
</div>
</div>
</div>
</div>
<div className="row">
<div className="col-md-8 col-md-offset-2">
{this.renderAlertOutputs()}
</div>
</div>
</div>
</div>
</div>
);
},
renderAlertOutputs() {
const {kapacitor} = this.state;
if (kapacitor) {
return <AlertOutputs source={this.props.source} kapacitor={kapacitor} addFlashMessage={this.props.addFlashMessage} />;
}
return (
<div className="panel panel-minimal">
<div className="panel-body">
<h4 className="text-center">Configure Alert Endpoints</h4>
<br/>
<p className="text-center">Set your Kapacitor connection info to configure alerting endpoints.</p>
</div>
</div>
);
return `${parser.protocol}//${parser.hostname}:${kapacitorPort}`;
},
});

View File

@ -78,9 +78,9 @@ export function createKapacitor(source, {url, name = 'My Kapacitor', username, p
});
}
export function updateKapacitor(kapacitor, {url, name = 'My Kapacitor', username, password}) {
export function updateKapacitor({links, url, name = 'My Kapacitor', username, password}) {
return AJAX({
url: kapacitor.links.self,
url: links.self,
method: 'PATCH',
data: {
name,