Merge pull request #1335 from influxdata/fix/sanitized
improve UX for sanitized kapacitor configpull/10616/head
commit
29cd2df8cb
|
@ -14,6 +14,7 @@
|
|||
1. [#1269](https://github.com/influxdata/chronograf/issues/1269): Add more functionality to the explorer's query generation process
|
||||
1. [#1318](https://github.com/influxdata/chronograf/issues/1318): Fix JWT refresh for auth-durations of zero and less than five minutes
|
||||
1. [#1332](https://github.com/influxdata/chronograf/pull/1332): Remove table toggle from dashboard visualization
|
||||
1. [#1335](https://github.com/influxdata/chronograf/pull/1335): Improve UX for sanitized kapacitor settings
|
||||
|
||||
### Features
|
||||
1. [#1232](https://github.com/influxdata/chronograf/pull/1232): Fuse the query builder and raw query editor
|
||||
|
|
|
@ -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, testAlertOutput} from 'shared/apis'
|
||||
import {
|
||||
getKapacitorConfig,
|
||||
updateKapacitorConfigSection,
|
||||
testAlertOutput,
|
||||
} from 'shared/apis'
|
||||
|
||||
import {
|
||||
AlertaConfig,
|
||||
|
@ -42,12 +46,17 @@ class AlertTabs extends Component {
|
|||
}
|
||||
|
||||
refreshKapacitorConfig(kapacitor) {
|
||||
getKapacitorConfig(kapacitor).then(({data: {sections}}) => {
|
||||
this.setState({configSections: sections})
|
||||
}).catch(() => {
|
||||
this.setState({configSections: null})
|
||||
this.props.addFlashMessage({type: 'error', text: 'There was an error getting the Kapacitor config'})
|
||||
})
|
||||
getKapacitorConfig(kapacitor)
|
||||
.then(({data: {sections}}) => {
|
||||
this.setState({configSections: sections})
|
||||
})
|
||||
.catch(() => {
|
||||
this.setState({configSections: null})
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was an error getting the Kapacitor config',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
getSection(sections, section) {
|
||||
|
@ -57,29 +66,45 @@ class AlertTabs extends Component {
|
|||
handleSaveConfig(section, properties) {
|
||||
if (section !== '') {
|
||||
const propsToSend = this.sanitizeProperties(section, properties)
|
||||
updateKapacitorConfigSection(this.props.kapacitor, section, propsToSend).then(() => {
|
||||
this.refreshKapacitorConfig(this.props.kapacitor)
|
||||
this.props.addFlashMessage({type: 'success', text: `Alert for ${section} successfully saved`})
|
||||
}).catch(() => {
|
||||
this.props.addFlashMessage({type: 'error', text: 'There was an error saving the kapacitor config'})
|
||||
})
|
||||
updateKapacitorConfigSection(this.props.kapacitor, section, propsToSend)
|
||||
.then(() => {
|
||||
this.refreshKapacitorConfig(this.props.kapacitor)
|
||||
this.props.addFlashMessage({
|
||||
type: 'success',
|
||||
text: `Alert for ${section} successfully saved`,
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was an error saving the kapacitor config',
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleTest(section, properties) {
|
||||
const propsToSend = this.sanitizeProperties(section, properties)
|
||||
testAlertOutput(this.props.kapacitor, section, propsToSend).then(() => {
|
||||
this.props.addFlashMessage({type: 'success', text: 'Slack test message sent'})
|
||||
}).catch(() => {
|
||||
this.props.addFlashMessage({type: 'error', text: 'There was an error testing the slack alert'})
|
||||
})
|
||||
testAlertOutput(this.props.kapacitor, section, propsToSend)
|
||||
.then(() => {
|
||||
this.props.addFlashMessage({
|
||||
type: 'success',
|
||||
text: 'Slack test message sent',
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
this.props.addFlashMessage({
|
||||
type: 'error',
|
||||
text: 'There was an error testing the slack alert',
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
sanitizeProperties(section, properties) {
|
||||
const cleanProps = Object.assign({}, properties, {enabled: true})
|
||||
const {redacted} = this.getSection(this.state.configSections, section)
|
||||
if (redacted && redacted.length) {
|
||||
redacted.forEach((badProp) => {
|
||||
redacted.forEach(badProp => {
|
||||
if (properties[badProp] === 'true') {
|
||||
delete cleanProps[badProp]
|
||||
}
|
||||
|
@ -95,50 +120,101 @@ class AlertTabs extends Component {
|
|||
return null
|
||||
}
|
||||
|
||||
const test = (properties) => {
|
||||
const test = properties => {
|
||||
this.handleTest('slack', properties)
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
type: 'Alerta',
|
||||
component: (<AlertaConfig onSave={(p) => this.handleSaveConfig('alerta', p)} config={this.getSection(configSections, 'alerta')} />),
|
||||
component: (
|
||||
<AlertaConfig
|
||||
onSave={p => this.handleSaveConfig('alerta', p)}
|
||||
config={this.getSection(configSections, 'alerta')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'SMTP',
|
||||
component: (<SMTPConfig onSave={(p) => this.handleSaveConfig('smtp', p)} config={this.getSection(configSections, 'smtp')} />),
|
||||
component: (
|
||||
<SMTPConfig
|
||||
onSave={p => this.handleSaveConfig('smtp', p)}
|
||||
config={this.getSection(configSections, 'smtp')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'Slack',
|
||||
component: (<SlackConfig onSave={(p) => this.handleSaveConfig('slack', p)} onTest={test} config={this.getSection(configSections, 'slack')} />),
|
||||
component: (
|
||||
<SlackConfig
|
||||
onSave={p => this.handleSaveConfig('slack', p)}
|
||||
onTest={test}
|
||||
config={this.getSection(configSections, 'slack')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'VictorOps',
|
||||
component: (<VictorOpsConfig onSave={(p) => this.handleSaveConfig('victorops', p)} config={this.getSection(configSections, 'victorops')} />),
|
||||
component: (
|
||||
<VictorOpsConfig
|
||||
onSave={p => this.handleSaveConfig('victorops', p)}
|
||||
config={this.getSection(configSections, 'victorops')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'Telegram',
|
||||
component: (<TelegramConfig onSave={(p) => this.handleSaveConfig('telegram', p)} config={this.getSection(configSections, 'telegram')} />),
|
||||
component: (
|
||||
<TelegramConfig
|
||||
onSave={p => this.handleSaveConfig('telegram', p)}
|
||||
config={this.getSection(configSections, 'telegram')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'OpsGenie',
|
||||
component: (<OpsGenieConfig onSave={(p) => this.handleSaveConfig('opsgenie', p)} config={this.getSection(configSections, 'opsgenie')} />),
|
||||
component: (
|
||||
<OpsGenieConfig
|
||||
onSave={p => this.handleSaveConfig('opsgenie', p)}
|
||||
config={this.getSection(configSections, 'opsgenie')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'PagerDuty',
|
||||
component: (<PagerDutyConfig onSave={(p) => this.handleSaveConfig('pagerduty', p)} config={this.getSection(configSections, 'pagerduty')} />),
|
||||
component: (
|
||||
<PagerDutyConfig
|
||||
onSave={p => this.handleSaveConfig('pagerduty', p)}
|
||||
config={this.getSection(configSections, 'pagerduty')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'HipChat',
|
||||
component: (<HipChatConfig onSave={(p) => this.handleSaveConfig('hipchat', p)} config={this.getSection(configSections, 'hipchat')} />),
|
||||
component: (
|
||||
<HipChatConfig
|
||||
onSave={p => this.handleSaveConfig('hipchat', p)}
|
||||
config={this.getSection(configSections, 'hipchat')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'Sensu',
|
||||
component: (<SensuConfig onSave={(p) => this.handleSaveConfig('sensu', p)} config={this.getSection(configSections, 'sensu')} />),
|
||||
component: (
|
||||
<SensuConfig
|
||||
onSave={p => this.handleSaveConfig('sensu', p)}
|
||||
config={this.getSection(configSections, 'sensu')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
type: 'Talk',
|
||||
component: (<TalkConfig onSave={(p) => this.handleSaveConfig('talk', p)} config={this.getSection(configSections, 'talk')} />),
|
||||
component: (
|
||||
<TalkConfig
|
||||
onSave={p => this.handleSaveConfig('talk', p)}
|
||||
config={this.getSection(configSections, 'talk')}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -152,14 +228,12 @@ class AlertTabs extends Component {
|
|||
|
||||
<Tabs tabContentsClass="config-endpoint">
|
||||
<TabList customClass="config-endpoint--tabs">
|
||||
{
|
||||
tabs.map((t, i) => (<Tab key={tabs[i].type}>{tabs[i].type}</Tab>))
|
||||
}
|
||||
{tabs.map((t, i) => <Tab key={tabs[i].type}>{tabs[i].type}</Tab>)}
|
||||
</TabList>
|
||||
<TabPanels customClass="config-endpoint--tab-contents">
|
||||
{
|
||||
tabs.map((t, i) => (<TabPanel key={tabs[i].type}>{t.component}</TabPanel>))
|
||||
}
|
||||
{tabs.map((t, i) => (
|
||||
<TabPanel key={tabs[i].type}>{t.component}</TabPanel>
|
||||
))}
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
@ -167,11 +241,7 @@ class AlertTabs extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {
|
||||
func,
|
||||
shape,
|
||||
string,
|
||||
} = PropTypes
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
AlertTabs.propTypes = {
|
||||
source: shape({
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
const AlertaConfig = React.createClass({
|
||||
propTypes: {
|
||||
config: PropTypes.shape({
|
||||
options: PropTypes.shape({
|
||||
environment: PropTypes.string,
|
||||
origin: PropTypes.string,
|
||||
token: PropTypes.bool,
|
||||
url: PropTypes.string,
|
||||
config: shape({
|
||||
options: shape({
|
||||
environment: string,
|
||||
origin: string,
|
||||
token: bool,
|
||||
url: string,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
onSave: PropTypes.func.isRequired,
|
||||
onSave: func.isRequired,
|
||||
},
|
||||
|
||||
handleSaveAlert(e) {
|
||||
|
@ -33,27 +37,50 @@ const AlertaConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="environment">Environment</label>
|
||||
<input className="form-control" id="environment" type="text" ref={(r) => this.environment = r} defaultValue={environment || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="environment"
|
||||
type="text"
|
||||
ref={r => this.environment = r}
|
||||
defaultValue={environment || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="origin">Origin</label>
|
||||
<input className="form-control" id="origin" type="text" ref={(r) => this.origin = r} defaultValue={origin || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="origin"
|
||||
type="text"
|
||||
ref={r => this.origin = r}
|
||||
defaultValue={origin || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">Token</label>
|
||||
<input className="form-control" id="token" type="text" ref={(r) => this.token = r} defaultValue={token || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the Alerta Token has been set</label>
|
||||
<RedactedInput
|
||||
defaultValue={token}
|
||||
id="token"
|
||||
refFunc={r => this.token = r}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">User</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="url"
|
||||
type="text"
|
||||
ref={r => this.url = r}
|
||||
defaultValue={url || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
|
||||
import {HIPCHAT_TOKEN_TIP} from 'src/kapacitor/copy'
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const {
|
||||
bool,
|
||||
func,
|
||||
shape,
|
||||
string,
|
||||
} = PropTypes
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
const HipchatConfig = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -37,7 +34,9 @@ const HipchatConfig = React.createClass({
|
|||
const {options} = this.props.config
|
||||
const {url, room, token} = options
|
||||
|
||||
const subdomain = url.replace('https://', '').replace('.hipchat.com/v2/room', '')
|
||||
const subdomain = url
|
||||
.replace('https://', '')
|
||||
.replace('.hipchat.com/v2/room', '')
|
||||
|
||||
return (
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
|
@ -48,7 +47,7 @@ const HipchatConfig = React.createClass({
|
|||
id="url"
|
||||
type="text"
|
||||
placeholder="your-subdomain"
|
||||
ref={(r) => this.url = r}
|
||||
ref={r => this.url = r}
|
||||
defaultValue={subdomain && subdomain.length ? subdomain : ''}
|
||||
/>
|
||||
</div>
|
||||
|
@ -60,7 +59,7 @@ const HipchatConfig = React.createClass({
|
|||
id="room"
|
||||
type="text"
|
||||
placeholder="your-hipchat-room"
|
||||
ref={(r) => this.room = r}
|
||||
ref={r => this.room = r}
|
||||
defaultValue={room || ''}
|
||||
/>
|
||||
</div>
|
||||
|
@ -68,24 +67,19 @@ const HipchatConfig = React.createClass({
|
|||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">
|
||||
Token
|
||||
<QuestionMarkTooltip
|
||||
tipID="token"
|
||||
tipContent={HIPCHAT_TOKEN_TIP}
|
||||
/>
|
||||
<QuestionMarkTooltip tipID="token" tipContent={HIPCHAT_TOKEN_TIP} />
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
<RedactedInput
|
||||
defaultValue={token}
|
||||
id="token"
|
||||
type="text"
|
||||
placeholder="your-hipchat-token"
|
||||
ref={(r) => this.token = r}
|
||||
defaultValue={token || ''}
|
||||
refFunc={r => this.token = r}
|
||||
/>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the HipChat token has been set</label>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
const {
|
||||
array,
|
||||
arrayOf,
|
||||
bool,
|
||||
func,
|
||||
shape,
|
||||
string,
|
||||
} = PropTypes
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const {array, arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
const OpsGenieConfig = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -47,15 +42,23 @@ const OpsGenieConfig = React.createClass({
|
|||
},
|
||||
|
||||
handleAddRecipient(recipient) {
|
||||
this.setState({currentRecipients: this.state.currentRecipients.concat(recipient)})
|
||||
this.setState({
|
||||
currentRecipients: this.state.currentRecipients.concat(recipient),
|
||||
})
|
||||
},
|
||||
|
||||
handleDeleteTeam(team) {
|
||||
this.setState({currentTeams: this.state.currentTeams.filter(t => t !== team)})
|
||||
this.setState({
|
||||
currentTeams: this.state.currentTeams.filter(t => t !== team),
|
||||
})
|
||||
},
|
||||
|
||||
handleDeleteRecipient(recipient) {
|
||||
this.setState({currentRecipients: this.state.currentRecipients.filter(r => r !== recipient)})
|
||||
this.setState({
|
||||
currentRecipients: this.state.currentRecipients.filter(
|
||||
r => r !== recipient
|
||||
),
|
||||
})
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -67,15 +70,37 @@ const OpsGenieConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="api-key">API Key</label>
|
||||
<input className="form-control" id="api-key" type="text" ref={(r) => this.apiKey = r} defaultValue={apiKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the OpsGenie API key has been set</label>
|
||||
<RedactedInput
|
||||
defaultValue={apiKey}
|
||||
id="api-key"
|
||||
refFunc={r => this.apiKey = r}
|
||||
/>
|
||||
<label className="form-helper">
|
||||
Note: a value of
|
||||
{' '}
|
||||
<code>true</code>
|
||||
{' '}
|
||||
indicates the OpsGenie API key has been set
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<TagInput title="Teams" onAddTag={this.handleAddTeam} onDeleteTag={this.handleDeleteTeam} tags={currentTeams} />
|
||||
<TagInput title="Recipients" onAddTag={this.handleAddRecipient} onDeleteTag={this.handleDeleteRecipient} tags={currentRecipients} />
|
||||
<TagInput
|
||||
title="Teams"
|
||||
onAddTag={this.handleAddTeam}
|
||||
onDeleteTag={this.handleDeleteTeam}
|
||||
tags={currentTeams}
|
||||
/>
|
||||
<TagInput
|
||||
title="Recipients"
|
||||
onAddTag={this.handleAddRecipient}
|
||||
onDeleteTag={this.handleDeleteRecipient}
|
||||
tags={currentRecipients}
|
||||
/>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
@ -105,7 +130,7 @@ const TagInput = React.createClass({
|
|||
},
|
||||
|
||||
shouldAddToList(item, tags) {
|
||||
return (!_.isEmpty(item) && !tags.find(l => l === item))
|
||||
return !_.isEmpty(item) && !tags.find(l => l === item)
|
||||
},
|
||||
|
||||
render() {
|
||||
|
@ -118,11 +143,12 @@ const TagInput = React.createClass({
|
|||
placeholder={`Type and hit 'Enter' to add to list of ${title}`}
|
||||
autoComplete="off"
|
||||
className="form-control"
|
||||
id={title} type="text"
|
||||
ref={(r) => this.input = r}
|
||||
onKeyDown={this.handleAddTag}>
|
||||
</input>
|
||||
<Tags tags={tags} onDeleteTag={onDeleteTag}/>
|
||||
id={title}
|
||||
type="text"
|
||||
ref={r => this.input = r}
|
||||
onKeyDown={this.handleAddTag}
|
||||
/>
|
||||
<Tags tags={tags} onDeleteTag={onDeleteTag} />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
@ -138,13 +164,9 @@ const Tags = React.createClass({
|
|||
const {tags, onDeleteTag} = this.props
|
||||
return (
|
||||
<div className="input-tag-list">
|
||||
{
|
||||
tags.map((item) => {
|
||||
return (
|
||||
<Tag key={item} item={item} onDelete={onDeleteTag} />
|
||||
)
|
||||
})
|
||||
}
|
||||
{tags.map(item => {
|
||||
return <Tag key={item} item={item} onDelete={onDeleteTag} />
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
@ -162,11 +184,10 @@ const Tag = React.createClass({
|
|||
return (
|
||||
<span key={item} className="input-tag-item">
|
||||
<span>{item}</span>
|
||||
<span className="icon remove" onClick={() => onDelete(item)}></span>
|
||||
<span className="icon remove" onClick={() => onDelete(item)} />
|
||||
</span>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
export default OpsGenieConfig
|
||||
|
|
|
@ -31,17 +31,37 @@ const PagerDutyConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="service-key">Service Key</label>
|
||||
<input className="form-control" id="service-key" type="text" ref={(r) => this.serviceKey = r} defaultValue={serviceKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the PagerDuty service key has been set</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="service-key"
|
||||
type="text"
|
||||
ref={r => this.serviceKey = r}
|
||||
defaultValue={serviceKey || ''}
|
||||
/>
|
||||
<label className="form-helper">
|
||||
Note: a value of
|
||||
{' '}
|
||||
<code>true</code>
|
||||
{' '}
|
||||
indicates the PagerDuty service key has been set
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">PagerDuty URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="url"
|
||||
type="text"
|
||||
ref={r => this.url = r}
|
||||
defaultValue={url || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
class RedactedInput extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
editing: false,
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {defaultValue, id, refFunc} = this.props
|
||||
const {editing} = this.state
|
||||
|
||||
if (defaultValue === true && !editing) {
|
||||
return (
|
||||
<div className="alert-value-set">
|
||||
<span>
|
||||
value set
|
||||
<a
|
||||
href="#"
|
||||
onClick={() => {
|
||||
this.setState({editing: true})
|
||||
}}
|
||||
>
|
||||
(change it)
|
||||
</a>
|
||||
</span>
|
||||
<input
|
||||
className="form-control"
|
||||
id={id}
|
||||
type="hidden"
|
||||
ref={refFunc}
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
className="form-control"
|
||||
id={id}
|
||||
type="text"
|
||||
ref={refFunc}
|
||||
defaultValue={''}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {bool, func, string} = PropTypes
|
||||
|
||||
RedactedInput.propTypes = {
|
||||
id: string.isRequired,
|
||||
defaultValue: bool,
|
||||
refFunc: func.isRequired,
|
||||
}
|
||||
|
||||
export default RedactedInput
|
|
@ -35,31 +35,64 @@ const SMTPConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-host">SMTP Host</label>
|
||||
<input className="form-control" id="smtp-host" type="text" ref={(r) => this.host = r} defaultValue={host || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="smtp-host"
|
||||
type="text"
|
||||
ref={r => this.host = r}
|
||||
defaultValue={host || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-port">SMTP Port</label>
|
||||
<input className="form-control" id="smtp-port" type="text" ref={(r) => this.port = r} defaultValue={port || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="smtp-port"
|
||||
type="text"
|
||||
ref={r => this.port = r}
|
||||
defaultValue={port || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="smtp-from">From Email</label>
|
||||
<input className="form-control" id="smtp-from" placeholder="email@domain.com" type="text" ref={(r) => this.from = r} defaultValue={from || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="smtp-from"
|
||||
placeholder="email@domain.com"
|
||||
type="text"
|
||||
ref={r => this.from = r}
|
||||
defaultValue={from || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-user">User</label>
|
||||
<input className="form-control" id="smtp-user" type="text" ref={(r) => this.username = r} defaultValue={username || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="smtp-user"
|
||||
type="text"
|
||||
ref={r => this.username = r}
|
||||
defaultValue={username || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="smtp-password">Password</label>
|
||||
<input className="form-control" id="smtp-password" type="password" ref={(r) => this.password = r} defaultValue={`${password}`}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="smtp-password"
|
||||
type="password"
|
||||
ref={r => this.password = r}
|
||||
defaultValue={`${password}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -29,16 +29,30 @@ const SensuConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="source">Source</label>
|
||||
<input className="form-control" id="source" type="text" ref={(r) => this.source = r} defaultValue={source || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="source"
|
||||
type="text"
|
||||
ref={r => this.source = r}
|
||||
defaultValue={source || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12 col-md-6">
|
||||
<label htmlFor="address">Address</label>
|
||||
<input className="form-control" id="address" type="text" ref={(r) => this.addr = r} defaultValue={addr || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="address"
|
||||
type="text"
|
||||
ref={r => this.addr = r}
|
||||
defaultValue={addr || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const SlackConfig = React.createClass({
|
||||
propTypes: {
|
||||
config: PropTypes.shape({
|
||||
|
@ -49,18 +51,40 @@ const SlackConfig = React.createClass({
|
|||
return (
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="slack-url">Slack Webhook URL (<a href="https://api.slack.com/incoming-webhooks" target="_">see more on Slack webhooks</a>)</label>
|
||||
<input className="form-control" id="slack-url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates that the Slack channel has been set</label>
|
||||
<label htmlFor="slack-url">
|
||||
Slack Webhook URL (
|
||||
<a href="https://api.slack.com/incoming-webhooks" target="_">
|
||||
see more on Slack webhooks
|
||||
</a>
|
||||
)
|
||||
</label>
|
||||
<RedactedInput
|
||||
defaultValue={url}
|
||||
id="url"
|
||||
refFunc={r => this.url = r}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="slack-channel">Slack Channel (optional)</label>
|
||||
<input className="form-control" id="slack-channel" type="text" placeholder="#alerts" ref={(r) => this.channel = r} defaultValue={channel || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="slack-channel"
|
||||
type="text"
|
||||
placeholder="#alerts"
|
||||
ref={r => this.channel = r}
|
||||
defaultValue={channel || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 text-center">
|
||||
<a className="btn btn-warning" onClick={this.handleTest} disabled={!this.state.testEnabled}>Send Test Message</a>
|
||||
<a
|
||||
className="btn btn-warning"
|
||||
onClick={this.handleTest}
|
||||
disabled={!this.state.testEnabled}
|
||||
>
|
||||
Send Test Message
|
||||
</a>
|
||||
<button className="btn btn-primary" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
const {
|
||||
bool,
|
||||
string,
|
||||
shape,
|
||||
func,
|
||||
} = PropTypes
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const {bool, string, shape, func} = PropTypes
|
||||
|
||||
const TalkConfig = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -36,17 +33,28 @@ const TalkConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates that the Talk URL has been set</label>
|
||||
<RedactedInput
|
||||
defaultValue={url}
|
||||
id="url"
|
||||
refFunc={r => this.url = r}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="author">Author Name</label>
|
||||
<input className="form-control" id="author" type="text" ref={(r) => this.author = r} defaultValue={author || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="author"
|
||||
type="text"
|
||||
ref={r => this.author = r}
|
||||
defaultValue={author || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -2,12 +2,9 @@ import React, {PropTypes} from 'react'
|
|||
import QuestionMarkTooltip from 'src/shared/components/QuestionMarkTooltip'
|
||||
import {TELEGRAM_CHAT_ID_TIP, TELEGRAM_TOKEN_TIP} from 'src/kapacitor/copy'
|
||||
|
||||
const {
|
||||
bool,
|
||||
func,
|
||||
shape,
|
||||
string,
|
||||
} = PropTypes
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
const TelegramConfig = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -56,7 +53,16 @@ const TelegramConfig = React.createClass({
|
|||
return (
|
||||
<form onSubmit={this.handleSaveAlert}>
|
||||
<p className="no-user-select">
|
||||
You need a <a href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot" target="_blank">Telegram Bot</a> to use this endpoint
|
||||
You need a
|
||||
{' '}
|
||||
<a
|
||||
href="https://docs.influxdata.com/kapacitor/v1.2/guides/event-handler-setup/#telegram-bot"
|
||||
target="_blank"
|
||||
>
|
||||
Telegram Bot
|
||||
</a>
|
||||
{' '}
|
||||
to use this endpoint
|
||||
</p>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="token">
|
||||
|
@ -66,15 +72,11 @@ const TelegramConfig = React.createClass({
|
|||
tipContent={TELEGRAM_TOKEN_TIP}
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
<RedactedInput
|
||||
defaultValue={token}
|
||||
id="token"
|
||||
type="text"
|
||||
placeholder="your-telegram-token"
|
||||
ref={(r) => this.token = r}
|
||||
defaultValue={token || ''}>
|
||||
</input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the Telegram token has been set</label>
|
||||
refFunc={r => this.token = r}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
|
@ -90,20 +92,34 @@ const TelegramConfig = React.createClass({
|
|||
id="chat-id"
|
||||
type="text"
|
||||
placeholder="your-telegram-chat-id"
|
||||
ref={(r) => this.chatID = r}
|
||||
defaultValue={chatID || ''}>
|
||||
</input>
|
||||
ref={r => this.chatID = r}
|
||||
defaultValue={chatID || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="parseMode">Select the alert message format</label>
|
||||
<div className="form-control-static">
|
||||
<div className="radio">
|
||||
<input id="parseModeMarkdown" type="radio" name="parseMode" value="markdown" defaultChecked={parseMode !== 'HTML'} ref={(r) => this.parseModeMarkdown = r} />
|
||||
<input
|
||||
id="parseModeMarkdown"
|
||||
type="radio"
|
||||
name="parseMode"
|
||||
value="markdown"
|
||||
defaultChecked={parseMode !== 'HTML'}
|
||||
ref={r => this.parseModeMarkdown = r}
|
||||
/>
|
||||
<label htmlFor="parseModeMarkdown">Markdown</label>
|
||||
</div>
|
||||
<div className="radio">
|
||||
<input id="parseModeHTML" type="radio" name="parseMode" value="html" defaultChecked={parseMode === 'HTML'} ref={(r) => this.parseModeHTML = r} />
|
||||
<input
|
||||
id="parseModeHTML"
|
||||
type="radio"
|
||||
name="parseMode"
|
||||
value="html"
|
||||
defaultChecked={parseMode === 'HTML'}
|
||||
ref={r => this.parseModeHTML = r}
|
||||
/>
|
||||
<label htmlFor="parseModeHTML">HTML</label>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -111,16 +127,32 @@ const TelegramConfig = React.createClass({
|
|||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="disableWebPagePreview" type="checkbox" defaultChecked={disableWebPagePreview} ref={(r) => this.disableWebPagePreview = r} />
|
||||
<input
|
||||
id="disableWebPagePreview"
|
||||
type="checkbox"
|
||||
defaultChecked={disableWebPagePreview}
|
||||
ref={r => this.disableWebPagePreview = r}
|
||||
/>
|
||||
<label htmlFor="disableWebPagePreview">
|
||||
Disable <a href="https://telegram.org/blog/link-preview" target="_blank">link previews</a> in alert messages.
|
||||
Disable
|
||||
{' '}
|
||||
<a href="https://telegram.org/blog/link-preview" target="_blank">
|
||||
link previews
|
||||
</a>
|
||||
{' '}
|
||||
in alert messages.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input id="disableNotification" type="checkbox" defaultChecked={disableNotification} ref={(r) => this.disableNotification = r} />
|
||||
<input
|
||||
id="disableNotification"
|
||||
type="checkbox"
|
||||
defaultChecked={disableNotification}
|
||||
ref={r => this.disableNotification = r}
|
||||
/>
|
||||
<label htmlFor="disableNotification">
|
||||
Disable notifications on iOS devices and disable sounds on Android devices. Android users continue to receive notifications.
|
||||
</label>
|
||||
|
@ -128,7 +160,9 @@ const TelegramConfig = React.createClass({
|
|||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import RedactedInput from './RedactedInput'
|
||||
|
||||
const VictorOpsConfig = React.createClass({
|
||||
propTypes: {
|
||||
config: PropTypes.shape({
|
||||
|
@ -34,22 +36,39 @@ const VictorOpsConfig = React.createClass({
|
|||
<form onSubmit={this.handleSaveAlert}>
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="api-key">API Key</label>
|
||||
<input className="form-control" id="api-key" type="text" ref={(r) => this.apiKey = r} defaultValue={apiKey || ''}></input>
|
||||
<label className="form-helper">Note: a value of <code>true</code> indicates the VictorOps API key has been set</label>
|
||||
<RedactedInput
|
||||
defaultValue={apiKey}
|
||||
id="api-key"
|
||||
refFunc={r => this.apiKey = r}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="routing-key">Routing Key</label>
|
||||
<input className="form-control" id="routing-key" type="text" ref={(r) => this.routingKey = r} defaultValue={routingKey || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="routing-key"
|
||||
type="text"
|
||||
ref={r => this.routingKey = r}
|
||||
defaultValue={routingKey || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="url">VictorOps URL</label>
|
||||
<input className="form-control" id="url" type="text" ref={(r) => this.url = r} defaultValue={url || ''}></input>
|
||||
<input
|
||||
className="form-control"
|
||||
id="url"
|
||||
type="text"
|
||||
ref={r => this.url = r}
|
||||
defaultValue={url || ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className="btn btn-block btn-primary" type="submit">Save</button>
|
||||
<button className="btn btn-block btn-primary" type="submit">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
|
|
|
@ -24,25 +24,26 @@
|
|||
@import 'layout/sidebar';
|
||||
|
||||
// Components
|
||||
@import 'components/dropdown';
|
||||
@import 'components/input-tag-list';
|
||||
@import 'components/page-header-dropdown';
|
||||
@import 'components/page-header-editable';
|
||||
@import 'components/multi-select-dropdown';
|
||||
@import 'components/page-spinner';
|
||||
@import 'components/flash-messages';
|
||||
@import 'components/flip-toggle';
|
||||
@import 'components/dygraphs';
|
||||
@import 'components/react-tooltips';
|
||||
@import 'components/search-widget';
|
||||
@import 'components/tables';
|
||||
@import 'components/resizer';
|
||||
@import 'components/source-indicator';
|
||||
@import 'components/graph-tips';
|
||||
@import 'components/confirm-buttons';
|
||||
@import 'components/custom-time-range';
|
||||
@import 'components/dropdown';
|
||||
@import 'components/dygraphs';
|
||||
@import 'components/flash-messages';
|
||||
@import 'components/flip-toggle';
|
||||
@import 'components/graph-tips';
|
||||
@import 'components/graph';
|
||||
@import 'components/input-tag-list';
|
||||
@import 'components/multi-select-dropdown';
|
||||
@import 'components/page-header-dropdown';
|
||||
@import 'components/page-header-editable';
|
||||
@import 'components/page-spinner';
|
||||
@import 'components/query-maker';
|
||||
@import 'components/react-tooltips';
|
||||
@import 'components/redacted-input';
|
||||
@import 'components/resizer';
|
||||
@import 'components/search-widget';
|
||||
@import 'components/source-indicator';
|
||||
@import 'components/tables';
|
||||
|
||||
// Pages
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.alert-value-set {
|
||||
padding: 6px 13px 0;
|
||||
|
||||
span a {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue