Merge branch 'master' into master

pull/10616/head
Benjamin Schweizer 2018-01-17 20:43:58 +01:00 committed by GitHub
commit 9d931390ee
21 changed files with 436 additions and 106 deletions

View File

@ -1,7 +1,11 @@
## v1.4.1.0 [unreleased]
### Features
1. [#2409](https://github.com/influxdata/chronograf/pull/2409): Allow adding multiple event handlers to a rule
1. [#2526](https://github.com/influxdata/chronograf/pull/2526): Add support for RS256/JWKS verification, support for id_token parsing (as in ADFS)
1. [#2709](https://github.com/influxdata/chronograf/pull/2709): Add "send test alert" button to test kapacitor alert configurations"
### UI Improvements
1. [#2698](https://github.com/influxdata/chronograf/pull/2698): Improve clarity of terminology surrounding InfluxDB & Kapacitor connections
### Bug Fixes
1. [#2689](https://github.com/influxdata/chronograf/pull/2689): Allow insecure (self-signed) certificates for kapacitor and influxdb
@ -17,6 +21,7 @@
### Bug Fixes
1. [#2652](https://github.com/influxdata/chronograf/pull/2652): Make page render successfully when attempting to edit a source
1. [#2664](https://github.com/influxdata/chronograf/pull/2664): Fix CustomTimeIndicator positioning
1. [#2687](https://github.com/influxdata/chronograf/pull/2687): Remove series with "no value" from legend
## v1.4.0.0-rc2 [2017-12-21]
### UI Improvements
@ -103,7 +108,6 @@
1. [#2460](https://github.com/influxdata/chronograf/pull/2460): Update kapacitor alerts to cast to float before sending to influx
1. [#2479](https://github.com/influxdata/chronograf/pull/2479): Support authentication for Enterprise Meta Nodes
1. [#2477](https://github.com/influxdata/chronograf/pull/2477): Improve performance of hoverline rendering
1. [#2409](https://github.com/influxdata/chronograf/pull/2409): Add multiple event handlers to rules
### UI Improvements

View File

@ -2,7 +2,11 @@ import React, {Component, PropTypes} from 'react'
import _ from 'lodash'
import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs'
import {getKapacitorConfig, updateKapacitorConfigSection} from 'shared/apis'
import {
getKapacitorConfig,
updateKapacitorConfigSection,
testAlertOutput,
} from 'shared/apis'
import {
AlertaConfig,
@ -76,18 +80,35 @@ class AlertTabs extends Component {
this.refreshKapacitorConfig(this.props.kapacitor)
this.props.addFlashMessage({
type: 'success',
text: `Alert for ${section} successfully saved`,
text: `Alert configuration for ${section} successfully saved.`,
})
})
.catch(() => {
this.props.addFlashMessage({
type: 'error',
text: 'There was an error saving the kapacitor config',
text: `There was an error saving the alert configuration for ${section}.`,
})
})
}
}
handleTestConfig = section => async e => {
e.preventDefault()
try {
await testAlertOutput(this.props.kapacitor, section)
this.props.addFlashMessage({
type: 'success',
text: `Successfully triggered an alert to ${section}. If the alert does not reach its destination, please check your configuration settings.`,
})
} catch (error) {
this.props.addFlashMessage({
type: 'error',
text: `There was an error sending an alert to ${section}.`,
})
}
}
sanitizeProperties = (section, properties) => {
const cleanProps = {...properties, enabled: true}
const {redacted} = this.getSection(this.state.configSections, section)
@ -116,6 +137,8 @@ class AlertTabs extends Component {
<AlertaConfig
onSave={this.handleSaveConfig('alerta')}
config={this.getSection(configSections, 'alerta')}
onTest={this.handleTestConfig('alerta')}
enabled={this.getEnabled(configSections, 'alerta')}
/>,
},
hipchat: {
@ -125,6 +148,8 @@ class AlertTabs extends Component {
<HipChatConfig
onSave={this.handleSaveConfig('hipchat')}
config={this.getSection(configSections, 'hipchat')}
onTest={this.handleTestConfig('hipchat')}
enabled={this.getEnabled(configSections, 'hipchat')}
/>,
},
opsgenie: {
@ -134,6 +159,8 @@ class AlertTabs extends Component {
<OpsGenieConfig
onSave={this.handleSaveConfig('opsgenie')}
config={this.getSection(configSections, 'opsgenie')}
onTest={this.handleTestConfig('opsgenie')}
enabled={this.getEnabled(configSections, 'opsgenie')}
/>,
},
pagerduty: {
@ -143,6 +170,8 @@ class AlertTabs extends Component {
<PagerDutyConfig
onSave={this.handleSaveConfig('pagerduty')}
config={this.getSection(configSections, 'pagerduty')}
onTest={this.handleTestConfig('pagerduty')}
enabled={this.getEnabled(configSections, 'pagerduty')}
/>,
},
pushover: {
@ -152,6 +181,8 @@ class AlertTabs extends Component {
<PushoverConfig
onSave={this.handleSaveConfig('pushover')}
config={this.getSection(configSections, 'pushover')}
onTest={this.handleTestConfig('pushover')}
enabled={this.getEnabled(configSections, 'pushover')}
/>,
},
sensu: {
@ -161,6 +192,8 @@ class AlertTabs extends Component {
<SensuConfig
onSave={this.handleSaveConfig('sensu')}
config={this.getSection(configSections, 'sensu')}
onTest={this.handleTestConfig('sensu')}
enabled={this.getEnabled(configSections, 'sensu')}
/>,
},
slack: {
@ -170,6 +203,8 @@ class AlertTabs extends Component {
<SlackConfig
onSave={this.handleSaveConfig('slack')}
config={this.getSection(configSections, 'slack')}
onTest={this.handleTestConfig('slack')}
enabled={this.getEnabled(configSections, 'slack')}
/>,
},
smtp: {
@ -179,6 +214,8 @@ class AlertTabs extends Component {
<SMTPConfig
onSave={this.handleSaveConfig('smtp')}
config={this.getSection(configSections, 'smtp')}
onTest={this.handleTestConfig('smtp')}
enabled={this.getEnabled(configSections, 'smtp')}
/>,
},
talk: {
@ -188,6 +225,8 @@ class AlertTabs extends Component {
<TalkConfig
onSave={this.handleSaveConfig('talk')}
config={this.getSection(configSections, 'talk')}
onTest={this.handleTestConfig('talk')}
enabled={this.getEnabled(configSections, 'talk')}
/>,
},
telegram: {
@ -197,6 +236,8 @@ class AlertTabs extends Component {
<TelegramConfig
onSave={this.handleSaveConfig('telegram')}
config={this.getSection(configSections, 'telegram')}
onTest={this.handleTestConfig('telegram')}
enabled={this.getEnabled(configSections, 'telegram')}
/>,
},
victorops: {
@ -206,6 +247,8 @@ class AlertTabs extends Component {
<VictorOpsConfig
onSave={this.handleSaveConfig('victorops')}
config={this.getSection(configSections, 'victorops')}
onTest={this.handleTestConfig('victorops')}
enabled={this.getEnabled(configSections, 'victorops')}
/>,
},
}

View File

@ -6,14 +6,15 @@ import FancyScrollbar from 'shared/components/FancyScrollbar'
class KapacitorForm extends Component {
render() {
const {onInputChange, onReset, kapacitor, onSubmit, exists} = this.props
const {url, name, username, password} = kapacitor
const {url: kapaUrl, name, username, password} = kapacitor
return (
<div className="page">
<div className="page-header">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Configure Kapacitor</h1>
<h1 className="page-header__title">{`${exists
? 'Configure'
: 'Add a New'} Kapacitor Connection`}</h1>
</div>
</div>
</div>
@ -29,13 +30,13 @@ class KapacitorForm extends Component {
<form onSubmit={onSubmit}>
<div>
<div className="form-group">
<label htmlFor="url">Kapacitor URL</label>
<label htmlFor="kapaUrl">Kapacitor URL</label>
<input
className="form-control"
id="url"
name="url"
placeholder={url}
value={url}
id="kapaUrl"
name="kapaUrl"
placeholder={kapaUrl}
value={kapaUrl}
onChange={onInputChange}
spellCheck="false"
/>
@ -60,7 +61,7 @@ class KapacitorForm extends Component {
id="username"
name="username"
placeholder="username"
value={username}
value={username || ''}
onChange={onInputChange}
spellCheck="false"
/>
@ -73,7 +74,7 @@ class KapacitorForm extends Component {
type="password"
name="password"
placeholder="password"
value={password}
value={password || ''}
onChange={onInputChange}
spellCheck="false"
/>

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class AlertaConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -18,6 +21,11 @@ class AlertaConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -26,7 +34,7 @@ class AlertaConfig extends Component {
const {environment, origin, token, url} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="environment">Environment</label>
<input
@ -35,6 +43,7 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.environment = r)}
defaultValue={environment || ''}
onChange={this.disableTest}
/>
</div>
@ -46,6 +55,7 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.origin = r)}
defaultValue={origin || ''}
onChange={this.disableTest}
/>
</div>
@ -55,6 +65,7 @@ class AlertaConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -66,12 +77,26 @@ class AlertaConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Alerta Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -91,6 +116,8 @@ AlertaConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default AlertaConfig

View File

@ -7,9 +7,12 @@ import RedactedInput from './RedactedInput'
class HipchatConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -19,6 +22,11 @@ class HipchatConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -32,7 +40,7 @@ class HipchatConfig extends Component {
.replace('.hipchat.com/v2/room', '')
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="url">Subdomain</label>
<input
@ -42,6 +50,7 @@ class HipchatConfig extends Component {
placeholder="your-subdomain"
ref={r => (this.url = r)}
defaultValue={subdomain && subdomain.length ? subdomain : ''}
onChange={this.disableTest}
/>
</div>
@ -54,6 +63,7 @@ class HipchatConfig extends Component {
placeholder="your-hipchat-room"
ref={r => (this.room = r)}
defaultValue={room || ''}
onChange={this.disableTest}
/>
</div>
@ -66,12 +76,26 @@ class HipchatConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update HipChat Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -90,6 +114,8 @@ HipchatConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default HipchatConfig

View File

@ -12,10 +12,11 @@ class OpsGenieConfig extends Component {
this.state = {
currentTeams: teams || [],
currentRecipients: recipients || [],
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -25,6 +26,11 @@ class OpsGenieConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleAddTeam = team => {
@ -59,13 +65,14 @@ class OpsGenieConfig extends Component {
const {currentTeams, currentRecipients} = this.state
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="api-key">API Key</label>
<RedactedInput
defaultValue={apiKey}
id="api-key"
refFunc={this.handleApiKeyRef}
disableTest={this.disableTest}
/>
</div>
@ -74,17 +81,32 @@ class OpsGenieConfig extends Component {
onAddTag={this.handleAddTeam}
onDeleteTag={this.handleDeleteTeam}
tags={currentTeams}
disableTest={this.disableTest}
/>
<TagInput
title="Recipients"
onAddTag={this.handleAddRecipient}
onDeleteTag={this.handleDeleteRecipient}
tags={currentRecipients}
disableTest={this.disableTest}
/>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update OpsGenie Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -103,6 +125,8 @@ OpsGenieConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
class TagInput extends Component {
@ -121,6 +145,7 @@ class TagInput extends Component {
this.input.value = ''
onAddTag(newItem)
this.props.disableTest()
}
}
@ -156,6 +181,7 @@ TagInput.propTypes = {
onDeleteTag: func.isRequired,
tags: arrayOf(string).isRequired,
title: string.isRequired,
disableTest: func.isRequired,
}
const Tags = ({tags, onDeleteTag}) =>

View File

@ -4,9 +4,12 @@ import RedactedInput from './RedactedInput'
class PagerDutyConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -15,6 +18,11 @@ class PagerDutyConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
@ -23,13 +31,14 @@ class PagerDutyConfig extends Component {
const serviceKey = options['service-key']
const refFunc = r => (this.serviceKey = r)
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="service-key">Service Key</label>
<RedactedInput
defaultValue={serviceKey || ''}
id="service-key"
refFunc={refFunc}
disableTest={this.disableTest}
/>
</div>
@ -41,12 +50,26 @@ class PagerDutyConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update PagerDuty Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -64,6 +87,8 @@ PagerDutyConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default PagerDutyConfig

View File

@ -8,9 +8,12 @@ import {PUSHOVER_DOCS_LINK} from 'src/kapacitor/copy'
class PushoverConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -20,6 +23,11 @@ class PushoverConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUserKeyRef = r => (this.userKey = r)
@ -32,7 +40,7 @@ class PushoverConfig extends Component {
const userKey = options['user-key']
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="user-key">
User Key
@ -45,6 +53,7 @@ class PushoverConfig extends Component {
defaultValue={userKey}
id="user-key"
refFunc={this.handleUserKeyRef}
disableTest={this.disableTest}
/>
</div>
@ -60,6 +69,7 @@ class PushoverConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -71,12 +81,26 @@ class PushoverConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Pushover Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -95,6 +119,8 @@ PushoverConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default PushoverConfig

View File

@ -13,7 +13,7 @@ class RedactedInput extends Component {
}
render() {
const {defaultValue, id, refFunc} = this.props
const {defaultValue, id, refFunc, disableTest} = this.props
const {editing} = this.state
if (defaultValue === true && !editing) {
@ -43,6 +43,7 @@ class RedactedInput extends Component {
type="text"
ref={refFunc}
defaultValue={''}
onChange={disableTest}
/>
)
}
@ -54,6 +55,7 @@ RedactedInput.propTypes = {
id: string.isRequired,
defaultValue: bool,
refFunc: func.isRequired,
disableTest: func,
}
export default RedactedInput

View File

@ -3,9 +3,12 @@ import React, {PropTypes, Component} from 'react'
class SMTPConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -17,13 +20,18 @@ class SMTPConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
const {host, port, from, username, password} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12 col-md-6">
<label htmlFor="smtp-host">SMTP Host</label>
<input
@ -32,6 +40,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.host = r)}
defaultValue={host || ''}
onChange={this.disableTest}
/>
</div>
@ -43,6 +52,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.port = r)}
defaultValue={port || ''}
onChange={this.disableTest}
/>
</div>
@ -55,6 +65,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.from = r)}
defaultValue={from || ''}
onChange={this.disableTest}
/>
</div>
@ -66,6 +77,7 @@ class SMTPConfig extends Component {
type="text"
ref={r => (this.username = r)}
defaultValue={username || ''}
onChange={this.disableTest}
/>
</div>
@ -77,12 +89,26 @@ class SMTPConfig extends Component {
type="password"
ref={r => (this.password = r)}
defaultValue={`${password}`}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update SMTP Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -103,6 +129,8 @@ SMTPConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SMTPConfig

View File

@ -3,9 +3,12 @@ import React, {PropTypes, Component} from 'react'
class SensuConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -14,13 +17,18 @@ class SensuConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
render() {
const {source, addr} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12 col-md-6">
<label htmlFor="source">Source</label>
<input
@ -29,6 +37,7 @@ class SensuConfig extends Component {
type="text"
ref={r => (this.source = r)}
defaultValue={source || ''}
onChange={this.disableTest}
/>
</div>
@ -40,12 +49,26 @@ class SensuConfig extends Component {
type="text"
ref={r => (this.addr = r)}
defaultValue={addr || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Sensu Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -53,7 +76,7 @@ class SensuConfig extends Component {
}
}
const {func, shape, string} = PropTypes
const {bool, func, shape, string} = PropTypes
SensuConfig.propTypes = {
config: shape({
@ -63,6 +86,8 @@ SensuConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SensuConfig

View File

@ -6,25 +6,21 @@ class SlackConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: !!this.props.config.options.url,
testEnabled: this.props.enabled,
}
}
componentWillReceiveProps(nextProps) {
this.setState({
testEnabled: !!nextProps.config.options.url,
})
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
url: this.url.value,
channel: this.channel.value,
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUrlRef = r => (this.url = r)
@ -33,7 +29,7 @@ class SlackConfig extends Component {
const {url, channel} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="slack-url">
Slack Webhook URL (
@ -46,6 +42,7 @@ class SlackConfig extends Component {
defaultValue={url}
id="url"
refFunc={this.handleUrlRef}
disableTest={this.disableTest}
/>
</div>
@ -58,14 +55,30 @@ class SlackConfig extends Component {
placeholder="#alerts"
ref={r => (this.channel = r)}
defaultValue={channel || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Slack Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
<br />
<br />
</form>
)
}
@ -81,6 +94,8 @@ SlackConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default SlackConfig

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class TalkConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -16,6 +19,11 @@ class TalkConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleUrlRef = r => (this.url = r)
@ -24,13 +32,14 @@ class TalkConfig extends Component {
const {url, author_name: author} = this.props.config.options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="url">URL</label>
<RedactedInput
defaultValue={url}
id="url"
refFunc={this.handleUrlRef}
disableTest={this.disableTest}
/>
</div>
@ -42,12 +51,26 @@ class TalkConfig extends Component {
type="text"
ref={r => (this.author = r)}
defaultValue={author || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Talk Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -65,6 +88,8 @@ TalkConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default TalkConfig

View File

@ -7,9 +7,12 @@ import RedactedInput from './RedactedInput'
class TelegramConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
let parseMode
@ -29,6 +32,11 @@ class TelegramConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleTokenRef = r => (this.token = r)
@ -42,7 +50,7 @@ class TelegramConfig extends Component {
const parseMode = options['parse-mode']
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<div className="alert alert-warning alert-icon no-user-select">
<span className="icon triangle" />
@ -68,6 +76,7 @@ class TelegramConfig extends Component {
defaultValue={token}
id="token"
refFunc={this.handleTokenRef}
disableTest={this.disableTest}
/>
</div>
@ -86,6 +95,7 @@ class TelegramConfig extends Component {
placeholder="your-telegram-chat-id"
ref={r => (this.chatID = r)}
defaultValue={chatID || ''}
onChange={this.disableTest}
/>
</div>
@ -100,6 +110,7 @@ class TelegramConfig extends Component {
value="markdown"
defaultChecked={parseMode !== 'HTML'}
ref={r => (this.parseModeMarkdown = r)}
onChange={this.disableTest}
/>
<label htmlFor="parseModeMarkdown">Markdown</label>
</div>
@ -111,6 +122,7 @@ class TelegramConfig extends Component {
value="html"
defaultChecked={parseMode === 'HTML'}
ref={r => (this.parseModeHTML = r)}
onChange={this.disableTest}
/>
<label htmlFor="parseModeHTML">HTML</label>
</div>
@ -124,6 +136,7 @@ class TelegramConfig extends Component {
type="checkbox"
defaultChecked={disableWebPagePreview}
ref={r => (this.disableWebPagePreview = r)}
onChange={this.disableTest}
/>
<label htmlFor="disableWebPagePreview">
Disable{' '}
@ -142,6 +155,7 @@ class TelegramConfig extends Component {
type="checkbox"
defaultChecked={disableNotification}
ref={r => (this.disableNotification = r)}
onChange={this.disableTest}
/>
<label htmlFor="disableNotification">
Disable notifications on iOS devices and disable sounds on Android
@ -151,8 +165,21 @@ class TelegramConfig extends Component {
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update Telegram Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -173,6 +200,8 @@ TelegramConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default TelegramConfig

View File

@ -5,9 +5,12 @@ import RedactedInput from './RedactedInput'
class VictorOpsConfig extends Component {
constructor(props) {
super(props)
this.state = {
testEnabled: this.props.enabled,
}
}
handleSaveAlert = e => {
handleSubmit = e => {
e.preventDefault()
const properties = {
@ -17,6 +20,11 @@ class VictorOpsConfig extends Component {
}
this.props.onSave(properties)
this.setState({testEnabled: true})
}
disableTest = () => {
this.setState({testEnabled: false})
}
handleApiRef = r => (this.apiKey = r)
@ -28,13 +36,14 @@ class VictorOpsConfig extends Component {
const {url} = options
return (
<form onSubmit={this.handleSaveAlert}>
<form onSubmit={this.handleSubmit}>
<div className="form-group col-xs-12">
<label htmlFor="api-key">API Key</label>
<RedactedInput
defaultValue={apiKey}
id="api-key"
refFunc={this.handleApiRef}
disableTest={this.disableTest}
/>
</div>
@ -46,6 +55,7 @@ class VictorOpsConfig extends Component {
type="text"
ref={r => (this.routingKey = r)}
defaultValue={routingKey || ''}
onChange={this.disableTest}
/>
</div>
@ -57,12 +67,26 @@ class VictorOpsConfig extends Component {
type="text"
ref={r => (this.url = r)}
defaultValue={url || ''}
onChange={this.disableTest}
/>
</div>
<div className="form-group-submit col-xs-12 text-center">
<button className="btn btn-primary" type="submit">
Update VictorOps Config
<button
className="btn btn-primary"
type="submit"
disabled={this.state.testEnabled}
>
<span className="icon checkmark" />
Save Changes
</button>
<button
className="btn btn-primary"
disabled={!this.state.testEnabled}
onClick={this.props.onTest}
>
<span className="icon pulse-c" />
Send Test Alert
</button>
</div>
</form>
@ -81,6 +105,8 @@ VictorOpsConfig.propTypes = {
}).isRequired,
}).isRequired,
onSave: func.isRequired,
onTest: func.isRequired,
enabled: bool.isRequired,
}
export default VictorOpsConfig

View File

@ -152,20 +152,18 @@ export function updateKapacitorConfigSection(kapacitor, section, properties) {
})
}
export function testAlertOutput(kapacitor, outputName, properties) {
return kapacitorProxy(
kapacitor,
'GET',
'/kapacitor/v1/service-tests'
).then(({data: {services}}) => {
const service = services.find(s => s.name === outputName)
return kapacitorProxy(
export const testAlertOutput = async (kapacitor, outputName) => {
try {
const {data: {services}} = await kapacitorProxy(
kapacitor,
'POST',
service.link.href,
Object.assign({}, service.options, properties)
'GET',
'/kapacitor/v1/service-tests'
)
})
const service = services.find(s => s.name === outputName)
return kapacitorProxy(kapacitor, 'POST', service.link.href, {})
} catch (error) {
console.error(error)
}
}
export function createKapacitorTask(kapacitor, id, type, dbrps, script) {

View File

@ -29,8 +29,9 @@ const DygraphLegend = ({
isFilterVisible,
onToggleFilter,
}) => {
const withValues = series.filter(s => s.y)
const sorted = _.sortBy(
series,
withValues,
({y, label}) => (sortType === 'numeric' ? y : label)
)

View File

@ -21,7 +21,7 @@ const kapacitorDropdown = (
to={`/sources/${source.id}/kapacitors/new`}
className="btn btn-xs btn-default"
>
<span className="icon plus" /> Add Config
<span className="icon plus" /> Add Kapacitor Connection
</Link>
</Authorized>
)
@ -62,7 +62,7 @@ const kapacitorDropdown = (
onChoose={setActiveKapacitor}
addNew={{
url: `/sources/${source.id}/kapacitors/new`,
text: 'Add Kapacitor',
text: 'Add Kapacitor Connection',
}}
actions={[
{
@ -105,16 +105,16 @@ const InfluxTable = ({
<h2 className="panel-title">
{isUsingAuth
? <span>
InfluxDB Sources for <em>{me.currentOrganization.name}</em>
Connections for <em>{me.currentOrganization.name}</em>
</span>
: <span>InfluxDB Sources</span>}
: <span>Connections</span>}
</h2>
<Authorized requiredRole={EDITOR_ROLE}>
<Link
to={`/sources/${source.id}/manage-sources/new`}
className="btn btn-sm btn-primary"
>
<span className="icon plus" /> Add Source
<span className="icon plus" /> Add Connection
</Link>
</Authorized>
</div>
@ -123,14 +123,14 @@ const InfluxTable = ({
<thead>
<tr>
<th className="source-table--connect-col" />
<th>Source Name & Host</th>
<th>InfluxDB Connection</th>
<th className="text-right" />
<th>
Active Kapacitor{' '}
Kapacitor Connection{' '}
<QuestionMarkTooltip
tipID="kapacitor-node-helper"
tipContent={
'<p>Kapacitor Configurations are<br/>scoped per InfluxDB Source.<br/>Only one can be active at a time.</p>'
'<p>Kapacitor Connections are<br/>scoped per InfluxDB Connection.<br/>Only one can be active at a time.</p>'
}
/>
</th>
@ -189,7 +189,7 @@ const InfluxTable = ({
href="#"
onClick={handleDeleteSource(s)}
>
Delete Source
Delete Connection
</a>
</Authorized>
</td>

View File

@ -23,13 +23,14 @@ const SourceForm = ({
? <div className="text-center">
{me.role === SUPERADMIN_ROLE
? <h3>
<strong>{me.currentOrganization.name}</strong> has no sources
<strong>{me.currentOrganization.name}</strong> has no
connections
</h3>
: <h3>
<strong>{me.currentOrganization.name}</strong> has no sources
available to <em>{me.role}s</em>
<strong>{me.currentOrganization.name}</strong> has no
connections available to <em>{me.role}s</em>
</h3>}
<h6>Add a Source below:</h6>
<h6>Add a Connection below:</h6>
</div>
: null}
@ -112,13 +113,13 @@ const SourceForm = ({
<div className="form-control-static">
<input
type="checkbox"
id="defaultSourceCheckbox"
id="defaultConnectionCheckbox"
name="default"
checked={source.default}
onChange={onInputChange}
/>
<label htmlFor="defaultSourceCheckbox">
Make this the default source
<label htmlFor="defaultConnectionCheckbox">
Make this the default connection
</label>
</div>
</div>
@ -148,7 +149,7 @@ const SourceForm = ({
type="submit"
>
<span className={`icon ${editMode ? 'checkmark' : 'plus'}`} />
{editMode ? 'Save Changes' : 'Add Source'}
{editMode ? 'Save Changes' : 'Add Connection'}
</button>
<br />

View File

@ -131,7 +131,7 @@ class SourcePage extends Component {
.catch(err => {
// dont want to flash this until they submit
const error = this._parseError(err)
console.error('Error on source creation: ', error)
console.error('Error creating InfluxDB connection: ', error)
})
}
@ -142,10 +142,10 @@ class SourcePage extends Component {
.then(({data: sourceFromServer}) => {
this.props.addSourceAction(sourceFromServer)
this._redirect(sourceFromServer)
notify('success', `New source ${source.name} added`)
notify('success', `InfluxDB ${source.name} available as a connection`)
})
.catch(error => {
this.handleError('Unable to create source', error)
this.handleError('Unable to create InfluxDB connection', error)
})
}
@ -156,10 +156,10 @@ class SourcePage extends Component {
.then(({data: sourceFromServer}) => {
this.props.updateSourceAction(sourceFromServer)
this._redirect(sourceFromServer)
notify('success', `Source ${source.name} updated`)
notify('success', `InfluxDB connection ${source.name} updated`)
})
.catch(error => {
this.handleError('Unable to update source', error)
this.handleError('Unable to update InfluxDB connection', error)
})
}
@ -208,7 +208,9 @@ class SourcePage extends Component {
<div className="page-header__col-md-8">
<div className="page-header__left">
<h1 className="page-header__title">
{editMode ? 'Edit Source' : 'Add a New Source'}
{editMode
? 'Configure InfluxDB Connection'
: 'Add a New InfluxDB Connection'}
</h1>
</div>
{isInitialSource

View File

@ -6,7 +6,7 @@
margin: 0 0 0 4px;
}
.text-center .btn {
margin: 0 2px;
margin: 0 6px;
}
.default-source-label {
display: inline-block;