Merge pull request #3006 from influxdata/bugfix/task_enabled_correct_check
Fix Kapacitor Rules task enabled checkboxes to only toggle exactly as clickedpull/10616/head
commit
0bce2598eb
|
@ -6,6 +6,8 @@
|
|||
|
||||
### UI Improvements
|
||||
|
||||
1. [#2910](https://github.com/influxdata/chronograf/pull/2910): Redesign system notifications
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
1. [#2911](https://github.com/influxdata/chronograf/pull/2911): Fix Heroku OAuth
|
||||
|
@ -14,11 +16,11 @@
|
|||
1. [#2866](https://github.com/influxdata/chronograf/pull/2866): Change hover text on delete mappings confirmation button to 'Delete'
|
||||
1. [#2919](https://github.com/influxdata/chronograf/pull/2919): Automatically add graph type 'line' to any graph missing a type
|
||||
1. [#2970](https://github.com/influxdata/chronograf/pull/2970): Fix hanging browser on docker host dashboard
|
||||
1. [#3006](https://github.com/influxdata/chronograf/pull/3006): Fix Kapacitor Rules task enabled checkboxes to only toggle exactly as clicked
|
||||
|
||||
## v1.4.2.3 [2018-03-08]
|
||||
|
||||
## v1.4.2.2 [2018-03-07]
|
||||
1. [#2910](https://github.com/influxdata/chronograf/pull/2910): Redesign system notifications
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@ import {
|
|||
KapacitorPage,
|
||||
KapacitorRulePage,
|
||||
KapacitorRulesPage,
|
||||
KapacitorTasksPage,
|
||||
TickscriptPage,
|
||||
} from 'src/kapacitor'
|
||||
import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
|
||||
|
@ -154,7 +153,6 @@ const Root = React.createClass({
|
|||
path="kapacitors/:id/edit:hash"
|
||||
component={KapacitorPage}
|
||||
/>
|
||||
<Route path="kapacitor-tasks" component={KapacitorTasksPage} />
|
||||
<Route path="admin-chronograf" component={AdminChronografPage} />
|
||||
<Route path="admin-influxdb" component={AdminInfluxDBPage} />
|
||||
<Route path="manage-sources" component={ManageSources} />
|
||||
|
|
|
@ -3,11 +3,8 @@ import PropTypes from 'prop-types'
|
|||
import {Link} from 'react-router'
|
||||
|
||||
import NoKapacitorError from 'shared/components/NoKapacitorError'
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable'
|
||||
import TasksTable from 'src/kapacitor/components/TasksTable'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
|
||||
|
||||
const KapacitorRules = ({
|
||||
source,
|
||||
|
@ -19,7 +16,7 @@ const KapacitorRules = ({
|
|||
}) => {
|
||||
if (loading) {
|
||||
return (
|
||||
<PageContents>
|
||||
<div>
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">Alert Rules</h2>
|
||||
<button className="btn btn-primary btn-sm disabled" disabled={true}>
|
||||
|
@ -31,16 +28,12 @@ const KapacitorRules = ({
|
|||
<p>Loading Rules...</p>
|
||||
</div>
|
||||
</div>
|
||||
</PageContents>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!hasKapacitor) {
|
||||
return (
|
||||
<PageContents>
|
||||
<NoKapacitorError source={source} />
|
||||
</PageContents>
|
||||
)
|
||||
return <NoKapacitorError source={source} />
|
||||
}
|
||||
|
||||
const builderRules = rules.filter(r => r.query)
|
||||
|
@ -53,7 +46,7 @@ const KapacitorRules = ({
|
|||
}`
|
||||
|
||||
return (
|
||||
<PageContents source={source}>
|
||||
<div>
|
||||
<div className="panel">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">{builderHeader}</h2>
|
||||
|
@ -94,37 +87,11 @@ const KapacitorRules = ({
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
</PageContents>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const PageContents = ({children}) => (
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Manage Tasks</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<QuestionMarkTooltip
|
||||
tipID="manage-tasks--tooltip"
|
||||
tipContent="<b>Alert Rules</b> generate a TICKscript for<br/>you using our Builder UI.<br/><br/>Not all TICKscripts can be edited<br/>using the Builder."
|
||||
/>
|
||||
<SourceIndicator />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents fancy-scroll--kapacitor">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {arrayOf, bool, func, node, shape} = PropTypes
|
||||
const {arrayOf, bool, func, shape} = PropTypes
|
||||
|
||||
KapacitorRules.propTypes = {
|
||||
source: shape(),
|
||||
|
@ -135,9 +102,4 @@ KapacitorRules.propTypes = {
|
|||
onDelete: func,
|
||||
}
|
||||
|
||||
PageContents.propTypes = {
|
||||
children: node,
|
||||
onCloseTickscript: func,
|
||||
}
|
||||
|
||||
export default KapacitorRules
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
import {parseAlertNodeList} from 'src/shared/parsing/parseHandlersFromRule'
|
||||
import {TASKS_TABLE} from 'src/kapacitor/constants/tableSizing'
|
||||
const {
|
||||
colName,
|
||||
colTrigger,
|
||||
colMessage,
|
||||
colAlerts,
|
||||
colEnabled,
|
||||
colActions,
|
||||
} = TASKS_TABLE
|
||||
|
||||
const KapacitorRulesTable = ({rules, source, onDelete, onChangeRuleStatus}) => (
|
||||
<table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{minWidth: colName}}>Name</th>
|
||||
<th style={{width: colTrigger}}>Rule Type</th>
|
||||
<th style={{width: colMessage}}>Message</th>
|
||||
<th style={{width: colAlerts}}>Alert Handlers</th>
|
||||
<th style={{width: colEnabled}} className="text-center">
|
||||
Task Enabled
|
||||
</th>
|
||||
<th style={{width: colActions}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(rules, r => r.name.toLowerCase()).map(rule => {
|
||||
return (
|
||||
<RuleRow
|
||||
key={rule.id}
|
||||
rule={rule}
|
||||
source={source}
|
||||
onDelete={onDelete}
|
||||
onChangeRuleStatus={onChangeRuleStatus}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
const handleDelete = (rule, onDelete) => onDelete(rule)
|
||||
|
||||
const RuleRow = ({rule, source, onDelete, onChangeRuleStatus}) => (
|
||||
<tr key={rule.id}>
|
||||
<td style={{minWidth: colName}}>
|
||||
<Link to={`/sources/${source.id}/alert-rules/${rule.id}`}>
|
||||
{rule.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{width: colTrigger, textTransform: 'capitalize'}}>
|
||||
{rule.trigger}
|
||||
</td>
|
||||
<td style={{width: colMessage}}>{rule.message}</td>
|
||||
<td style={{width: colAlerts}}>{parseAlertNodeList(rule)}</td>
|
||||
<td style={{width: colEnabled}} className="text-center">
|
||||
<div className="dark-checkbox">
|
||||
<input
|
||||
id={`kapacitor-enabled ${rule.id}`}
|
||||
className="form-control-static"
|
||||
type="checkbox"
|
||||
defaultChecked={rule.status === 'enabled'}
|
||||
onClick={onChangeRuleStatus(rule)}
|
||||
/>
|
||||
<label htmlFor={`kapacitor-enabled ${rule.id}`} />
|
||||
</div>
|
||||
</td>
|
||||
<td style={{width: colActions}} className="text-right">
|
||||
<ConfirmButton
|
||||
text="Delete"
|
||||
type="btn-danger"
|
||||
size="btn-xs"
|
||||
customClass="table--show-on-row-hover"
|
||||
confirmAction={handleDelete(rule, onDelete)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
KapacitorRulesTable.propTypes = {
|
||||
rules: arrayOf(shape()),
|
||||
onChangeRuleStatus: func,
|
||||
onDelete: func,
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
RuleRow.propTypes = {
|
||||
rule: shape(),
|
||||
source: shape(),
|
||||
onChangeRuleStatus: func,
|
||||
onDelete: func,
|
||||
}
|
||||
|
||||
export default KapacitorRulesTable
|
|
@ -0,0 +1,131 @@
|
|||
import React, {PureComponent, SFC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {AlertRule, Source} from 'src/types'
|
||||
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
import {parseAlertNodeList} from 'src/shared/parsing/parseHandlersFromRule'
|
||||
import {TASKS_TABLE} from 'src/kapacitor/constants/tableSizing'
|
||||
const {
|
||||
colName,
|
||||
colTrigger,
|
||||
colMessage,
|
||||
colAlerts,
|
||||
colEnabled,
|
||||
colActions,
|
||||
} = TASKS_TABLE
|
||||
|
||||
interface KapacitorRulesTableProps {
|
||||
rules: AlertRule[]
|
||||
source: Source
|
||||
onChangeRuleStatus: (rule: AlertRule) => void
|
||||
onDelete: (rule: AlertRule) => void
|
||||
}
|
||||
|
||||
interface RuleRowProps {
|
||||
rule: AlertRule
|
||||
source: Source
|
||||
onChangeRuleStatus: (rule: AlertRule) => void
|
||||
onDelete: (rule: AlertRule) => void
|
||||
}
|
||||
|
||||
const KapacitorRulesTable: SFC<KapacitorRulesTableProps> = ({
|
||||
rules,
|
||||
source,
|
||||
onChangeRuleStatus,
|
||||
onDelete,
|
||||
}) => (
|
||||
<table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{minWidth: colName}}>Name</th>
|
||||
<th style={{width: colTrigger}}>Rule Type</th>
|
||||
<th style={{width: colMessage}}>Message</th>
|
||||
<th style={{width: colAlerts}}>Alert Handlers</th>
|
||||
<th style={{width: colEnabled}} className="text-center">
|
||||
Task Enabled
|
||||
</th>
|
||||
<th style={{width: colActions}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(rules, r => r.name.toLowerCase()).map(rule => {
|
||||
return (
|
||||
<RuleRow
|
||||
key={rule.id}
|
||||
rule={rule}
|
||||
source={source}
|
||||
onDelete={onDelete}
|
||||
onChangeRuleStatus={onChangeRuleStatus}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
export class RuleRow extends PureComponent<RuleRowProps> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleClickRuleStatusEnabled = this.handleClickRuleStatusEnabled.bind(
|
||||
this
|
||||
)
|
||||
this.handleDelete = this.handleDelete.bind(this)
|
||||
}
|
||||
|
||||
public handleClickRuleStatusEnabled(rule: AlertRule) {
|
||||
return () => {
|
||||
this.props.onChangeRuleStatus(rule)
|
||||
}
|
||||
}
|
||||
|
||||
public handleDelete(rule: AlertRule) {
|
||||
return () => {
|
||||
this.props.onDelete(rule)
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {rule, source} = this.props
|
||||
|
||||
return (
|
||||
<tr key={rule.id}>
|
||||
<td style={{minWidth: colName}}>
|
||||
<Link to={`/sources/${source.id}/alert-rules/${rule.id}`}>
|
||||
{rule.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{width: colTrigger, textTransform: 'capitalize'}}>
|
||||
{rule.trigger}
|
||||
</td>
|
||||
<td style={{width: colMessage}}>{rule.message}</td>
|
||||
<td style={{width: colAlerts}}>{parseAlertNodeList(rule)}</td>
|
||||
<td style={{width: colEnabled}} className="text-center">
|
||||
<div className="dark-checkbox">
|
||||
<input
|
||||
id={`kapacitor-rule-row-task-enabled ${rule.id}`}
|
||||
className="form-control-static"
|
||||
type="checkbox"
|
||||
checked={rule.status === 'enabled'}
|
||||
onChange={this.handleClickRuleStatusEnabled(rule)}
|
||||
/>
|
||||
<label htmlFor={`kapacitor-rule-row-task-enabled ${rule.id}`} />
|
||||
</div>
|
||||
</td>
|
||||
<td style={{width: colActions}} className="text-right">
|
||||
<ConfirmButton
|
||||
text="Delete"
|
||||
type="btn-danger"
|
||||
size="btn-xs"
|
||||
customClass="table--show-on-row-hover"
|
||||
confirmAction={this.handleDelete(rule)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default KapacitorRulesTable
|
|
@ -1,94 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
import {TASKS_TABLE} from 'src/kapacitor/constants/tableSizing'
|
||||
|
||||
const {colName, colType, colEnabled, colActions} = TASKS_TABLE
|
||||
|
||||
const TasksTable = ({tasks, source, onDelete, onChangeRuleStatus}) => (
|
||||
<table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{minWidth: colName}}>Name</th>
|
||||
<th style={{width: colType}}>Type</th>
|
||||
<th style={{width: colEnabled}} className="text-center">
|
||||
Task Enabled
|
||||
</th>
|
||||
<th style={{width: colActions}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(tasks, t => t.id.toLowerCase()).map(task => {
|
||||
return (
|
||||
<TaskRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
source={source}
|
||||
onDelete={onDelete}
|
||||
onChangeRuleStatus={onChangeRuleStatus}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
const handleDelete = (task, onDelete) => onDelete(task)
|
||||
|
||||
const TaskRow = ({task, source, onDelete, onChangeRuleStatus}) => (
|
||||
<tr key={task.id}>
|
||||
<td style={{minWidth: colName}}>
|
||||
<Link
|
||||
className="link-success"
|
||||
to={`/sources/${source.id}/tickscript/${task.id}`}
|
||||
>
|
||||
{task.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{width: colType, textTransform: 'capitalize'}}>{task.type}</td>
|
||||
<td style={{width: colEnabled}} className="text-center">
|
||||
<div className="dark-checkbox">
|
||||
<input
|
||||
id={`kapacitor-enabled ${task.id}`}
|
||||
className="form-control-static"
|
||||
type="checkbox"
|
||||
defaultChecked={task.status === 'enabled'}
|
||||
onClick={onChangeRuleStatus(task)}
|
||||
/>
|
||||
<label htmlFor={`kapacitor-enabled ${task.id}`} />
|
||||
</div>
|
||||
</td>
|
||||
<td style={{width: colActions}} className="text-right">
|
||||
<ConfirmButton
|
||||
text="Delete"
|
||||
type="btn-danger"
|
||||
size="btn-xs"
|
||||
customClass="table--show-on-row-hover"
|
||||
confirmAction={handleDelete(task, onDelete)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
TasksTable.propTypes = {
|
||||
tasks: arrayOf(shape()),
|
||||
onChangeRuleStatus: func,
|
||||
onDelete: func,
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
TaskRow.propTypes = {
|
||||
task: shape(),
|
||||
source: shape(),
|
||||
onChangeRuleStatus: func,
|
||||
onDelete: func,
|
||||
}
|
||||
|
||||
export default TasksTable
|
|
@ -0,0 +1,113 @@
|
|||
import React, {PureComponent, SFC} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {AlertRule, Source} from 'src/types'
|
||||
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
import {TASKS_TABLE} from 'src/kapacitor/constants/tableSizing'
|
||||
const {colName, colType, colEnabled, colActions} = TASKS_TABLE
|
||||
|
||||
interface TasksTableProps {
|
||||
tasks: AlertRule[]
|
||||
source: Source
|
||||
onChangeRuleStatus: (rule: AlertRule) => void
|
||||
onDelete: (rule: AlertRule) => void
|
||||
}
|
||||
|
||||
interface TaskRowProps {
|
||||
task: AlertRule
|
||||
source: Source
|
||||
onChangeRuleStatus: (rule: AlertRule) => void
|
||||
onDelete: (rule: AlertRule) => void
|
||||
}
|
||||
|
||||
const TasksTable: SFC<TasksTableProps> = ({
|
||||
tasks,
|
||||
source,
|
||||
onDelete,
|
||||
onChangeRuleStatus,
|
||||
}) => (
|
||||
<table className="table v-center table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{minWidth: colName}}>Name</th>
|
||||
<th style={{width: colType}}>Type</th>
|
||||
<th style={{width: colEnabled}} className="text-center">
|
||||
Task Enabled
|
||||
</th>
|
||||
<th style={{width: colActions}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(tasks, t => t.id.toLowerCase()).map(task => {
|
||||
return (
|
||||
<TaskRow
|
||||
key={task.id}
|
||||
task={task}
|
||||
source={source}
|
||||
onDelete={onDelete}
|
||||
onChangeRuleStatus={onChangeRuleStatus}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
|
||||
export class TaskRow extends PureComponent<TaskRowProps> {
|
||||
public handleClickRuleStatusEnabled(task: AlertRule) {
|
||||
return () => {
|
||||
this.props.onChangeRuleStatus(task)
|
||||
}
|
||||
}
|
||||
|
||||
public handleDelete(task: AlertRule) {
|
||||
return () => {
|
||||
this.props.onDelete(task)
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {task, source} = this.props
|
||||
|
||||
return (
|
||||
<tr key={task.id}>
|
||||
<td style={{minWidth: colName}}>
|
||||
<Link
|
||||
className="link-success"
|
||||
to={`/sources/${source.id}/tickscript/${task.id}`}
|
||||
>
|
||||
{task.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td style={{width: colType, textTransform: 'capitalize'}}>
|
||||
{task.type}
|
||||
</td>
|
||||
<td style={{width: colEnabled}} className="text-center">
|
||||
<div className="dark-checkbox">
|
||||
<input
|
||||
id={`kapacitor-task-row-task-enabled ${task.id}`}
|
||||
className="form-control-static"
|
||||
type="checkbox"
|
||||
checked={task.status === 'enabled'}
|
||||
onChange={this.handleClickRuleStatusEnabled(task)}
|
||||
/>
|
||||
<label htmlFor={`kapacitor-task-row-task-enabled ${task.id}`} />
|
||||
</div>
|
||||
</td>
|
||||
<td style={{width: colActions}} className="text-right">
|
||||
<ConfirmButton
|
||||
text="Delete"
|
||||
type="btn-danger"
|
||||
size="btn-xs"
|
||||
customClass="table--show-on-row-hover"
|
||||
confirmAction={this.handleDelete(task)}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default TasksTable
|
|
@ -2,9 +2,14 @@ import React, {Component} from 'react'
|
|||
import PropTypes from 'prop-types'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import {getActiveKapacitor} from 'shared/apis'
|
||||
import * as kapacitorActionCreators from '../actions/view'
|
||||
|
||||
import KapacitorRules from 'src/kapacitor/components/KapacitorRules'
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import QuestionMarkTooltip from 'shared/components/QuestionMarkTooltip'
|
||||
|
||||
class KapacitorRulesPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -25,12 +30,13 @@ class KapacitorRulesPage extends Component {
|
|||
this.setState({loading: false, hasKapacitor: !!kapacitor})
|
||||
}
|
||||
|
||||
handleDeleteRule = rule => () => {
|
||||
handleDeleteRule = rule => {
|
||||
const {actions} = this.props
|
||||
|
||||
actions.deleteRule(rule)
|
||||
}
|
||||
|
||||
handleRuleStatus = rule => () => {
|
||||
handleRuleStatus = rule => {
|
||||
const {actions} = this.props
|
||||
const status = rule.status === 'enabled' ? 'disabled' : 'enabled'
|
||||
|
||||
|
@ -43,19 +49,47 @@ class KapacitorRulesPage extends Component {
|
|||
const {hasKapacitor, loading} = this.state
|
||||
|
||||
return (
|
||||
<KapacitorRules
|
||||
source={source}
|
||||
rules={rules}
|
||||
hasKapacitor={hasKapacitor}
|
||||
loading={loading}
|
||||
onDelete={this.handleDeleteRule}
|
||||
onChangeRuleStatus={this.handleRuleStatus}
|
||||
/>
|
||||
<PageContents source={source}>
|
||||
<KapacitorRules
|
||||
source={source}
|
||||
rules={rules}
|
||||
hasKapacitor={hasKapacitor}
|
||||
loading={loading}
|
||||
onDelete={this.handleDeleteRule}
|
||||
onChangeRuleStatus={this.handleRuleStatus}
|
||||
/>
|
||||
</PageContents>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
const PageContents = ({children}) => (
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Manage Tasks</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<QuestionMarkTooltip
|
||||
tipID="manage-tasks--tooltip"
|
||||
tipContent="<b>Alert Rules</b> generate a TICKscript for<br/>you using our Builder UI.<br/><br/>Not all TICKscripts can be edited<br/>using the Builder."
|
||||
/>
|
||||
<SourceIndicator />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents fancy-scroll--kapacitor">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {arrayOf, func, node, shape, string} = PropTypes
|
||||
|
||||
KapacitorRulesPage.propTypes = {
|
||||
source: shape({
|
||||
|
@ -80,6 +114,10 @@ KapacitorRulesPage.propTypes = {
|
|||
}).isRequired,
|
||||
}
|
||||
|
||||
PageContents.propTypes = {
|
||||
children: node,
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
rules: Object.values(state.rules),
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export const KapacitorTasksPage = React.createClass({
|
||||
propTypes: {
|
||||
source: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired, // 'influx-enterprise'
|
||||
username: PropTypes.string.isRequired,
|
||||
links: PropTypes.shape({
|
||||
kapacitors: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {}
|
||||
},
|
||||
|
||||
render() {
|
||||
return <div className="kapacitorTasks">tasks</div>
|
||||
},
|
||||
})
|
||||
|
||||
export default KapacitorTasksPage
|
|
@ -1,12 +1,6 @@
|
|||
import KapacitorPage from './containers/KapacitorPage'
|
||||
import KapacitorRulePage from './containers/KapacitorRulePage'
|
||||
import KapacitorRulesPage from './containers/KapacitorRulesPage'
|
||||
import KapacitorTasksPage from './containers/KapacitorTasksPage'
|
||||
import TickscriptPage from './containers/TickscriptPage'
|
||||
export {
|
||||
KapacitorPage,
|
||||
KapacitorRulePage,
|
||||
KapacitorRulesPage,
|
||||
KapacitorTasksPage,
|
||||
TickscriptPage,
|
||||
}
|
||||
|
||||
export {KapacitorPage, KapacitorRulePage, KapacitorRulesPage, TickscriptPage}
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
import {AuthLinks, Organization, Role, User} from './auth'
|
||||
import {Query} from './query'
|
||||
import {Kapacitor, Source} from './sources'
|
||||
import {AlertRule, Kapacitor} from './kapacitor'
|
||||
import {Query, QueryConfig} from './query'
|
||||
import {Source} from './sources'
|
||||
|
||||
export {Query, Source, Kapacitor, AuthLinks, User, Organization, Role}
|
||||
export {
|
||||
AuthLinks,
|
||||
Role,
|
||||
User,
|
||||
Organization,
|
||||
AlertRule,
|
||||
Kapacitor,
|
||||
Query,
|
||||
QueryConfig,
|
||||
Source,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
import {QueryConfig} from './'
|
||||
|
||||
export interface Kapacitor {
|
||||
id?: string
|
||||
url: string
|
||||
name: string
|
||||
username?: string
|
||||
password?: string
|
||||
active: boolean
|
||||
insecureSkipVerify: boolean
|
||||
links: {
|
||||
self: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface AlertRule {
|
||||
id?: string
|
||||
tickscript: TICKScript
|
||||
query?: QueryConfig
|
||||
every: string
|
||||
alertNodes: AlertNodes
|
||||
message: string
|
||||
details: string
|
||||
trigger: string
|
||||
values: TriggerValues
|
||||
name: string
|
||||
type: string
|
||||
dbrps: DBRP[]
|
||||
status: string
|
||||
executing: boolean
|
||||
error: string
|
||||
created: string
|
||||
modified: string
|
||||
'last-enabled'?: string
|
||||
}
|
||||
|
||||
type TICKScript = string
|
||||
|
||||
// AlertNodes defines all possible kapacitor interactions with an alert.
|
||||
interface AlertNodes {
|
||||
stateChangesOnly: boolean
|
||||
useFlapping: boolean
|
||||
post: Post[]
|
||||
tcp: TCP[]
|
||||
email: Email[]
|
||||
exec: Exec[]
|
||||
log: Log[]
|
||||
victorOps: VictorOps[]
|
||||
pagerDuty: PagerDuty[]
|
||||
pushover: Pushover[]
|
||||
sensu: Sensu[]
|
||||
slack: Slack[]
|
||||
telegram: Telegram[]
|
||||
hipChat: HipChat[]
|
||||
alerta: Alerta[]
|
||||
opsGenie: OpsGenie[]
|
||||
talk: Talk[]
|
||||
}
|
||||
|
||||
interface Headers {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
// Post will POST alerts to a destination URL
|
||||
interface Post {
|
||||
url: string
|
||||
headers: Headers
|
||||
}
|
||||
|
||||
// Log sends the output of the alert to a file
|
||||
interface Log {
|
||||
filePath: string
|
||||
}
|
||||
|
||||
// Alerta sends the output of the alert to an alerta service
|
||||
interface Alerta {
|
||||
token: string
|
||||
resource: string
|
||||
event: string
|
||||
environment: string
|
||||
group: string
|
||||
value: string
|
||||
origin: string
|
||||
service: string[]
|
||||
}
|
||||
|
||||
// Exec executes a shell command on an alert
|
||||
interface Exec {
|
||||
command: string[]
|
||||
}
|
||||
|
||||
// TCP sends the alert to the address
|
||||
interface TCP {
|
||||
address: string
|
||||
}
|
||||
|
||||
// Email sends the alert to a list of email addresses
|
||||
interface Email {
|
||||
to: string[]
|
||||
}
|
||||
|
||||
// VictorOps sends alerts to the victorops.com service
|
||||
interface VictorOps {
|
||||
routingKey: string
|
||||
}
|
||||
|
||||
// PagerDuty sends alerts to the pagerduty.com service
|
||||
interface PagerDuty {
|
||||
serviceKey: string
|
||||
}
|
||||
|
||||
// HipChat sends alerts to stride.com
|
||||
interface HipChat {
|
||||
room: string
|
||||
token: string
|
||||
}
|
||||
|
||||
// Sensu sends alerts to sensu or sensuapp.org
|
||||
interface Sensu {
|
||||
source: string
|
||||
handlers: string[]
|
||||
}
|
||||
|
||||
// Pushover sends alerts to pushover.net
|
||||
interface Pushover {
|
||||
// UserKey is the User/Group key of your user (or you), viewable when logged
|
||||
// into the Pushover dashboard. Often referred to as USER_KEY
|
||||
// in the Pushover documentation.
|
||||
userKey: string
|
||||
|
||||
// Device is the users device name to send message directly to that device,
|
||||
// rather than all of a user's devices (multiple device names may
|
||||
// be separated by a comma)
|
||||
device: string
|
||||
|
||||
// Title is your message's title, otherwise your apps name is used
|
||||
title: string
|
||||
|
||||
// URL is a supplementary URL to show with your message
|
||||
url: string
|
||||
|
||||
// URLTitle is a title for your supplementary URL, otherwise just URL is shown
|
||||
urlTitle: string
|
||||
|
||||
// Sound is the name of one of the sounds supported by the device clients to override
|
||||
// the user's default sound choice
|
||||
sound: string
|
||||
}
|
||||
|
||||
// Slack sends alerts to a slack.com channel
|
||||
interface Slack {
|
||||
channel: string
|
||||
username: string
|
||||
iconEmoji: string
|
||||
}
|
||||
|
||||
// Telegram sends alerts to telegram.org
|
||||
interface Telegram {
|
||||
chatId: string
|
||||
parseMode: string
|
||||
disableWebPagePreview: boolean
|
||||
disableNotification: boolean
|
||||
}
|
||||
|
||||
// OpsGenie sends alerts to opsgenie.com
|
||||
interface OpsGenie {
|
||||
teams: string[]
|
||||
recipients: string[]
|
||||
}
|
||||
|
||||
// Talk sends alerts to Jane Talk (https://jianliao.com/site)
|
||||
interface Talk {}
|
||||
|
||||
// TriggerValues specifies the alerting logic for a specific trigger type
|
||||
interface TriggerValues {
|
||||
change?: string
|
||||
period?: string
|
||||
shift?: string
|
||||
operator?: string
|
||||
value?: string
|
||||
rangeValue: string
|
||||
}
|
||||
|
||||
// DBRP represents a database and retention policy for a time series source
|
||||
interface DBRP {
|
||||
db: string
|
||||
rp: string
|
||||
}
|
|
@ -8,7 +8,7 @@ export interface Query {
|
|||
groupBy: GroupBy
|
||||
areTagsAccepted: boolean
|
||||
rawText: string | null
|
||||
range?: TimeRange | null
|
||||
range?: DurationRange | null
|
||||
source?: string
|
||||
fill: string
|
||||
status?: Status
|
||||
|
@ -53,7 +53,28 @@ export interface Status {
|
|||
success?: string
|
||||
}
|
||||
|
||||
export interface TimeRange {
|
||||
export interface DurationRange {
|
||||
lower: string
|
||||
upper?: string
|
||||
}
|
||||
|
||||
interface TimeShift {
|
||||
label: string
|
||||
unit: string
|
||||
quantity: string
|
||||
}
|
||||
|
||||
export interface QueryConfig {
|
||||
id?: string
|
||||
database: string
|
||||
measurement: string
|
||||
retentionPolicy: string
|
||||
fields: Field[]
|
||||
tags: Tags
|
||||
groupBy: GroupBy
|
||||
areTagsAccepted: boolean
|
||||
fill?: string
|
||||
rawText: string
|
||||
range: DurationRange
|
||||
shifts: TimeShift[]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import {Kapacitor} from './'
|
||||
|
||||
export interface Source {
|
||||
id: string
|
||||
name: string
|
||||
|
@ -13,7 +15,7 @@ export interface Source {
|
|||
metaUrl?: string
|
||||
}
|
||||
|
||||
export interface SourceLinks {
|
||||
interface SourceLinks {
|
||||
self: string
|
||||
kapacitors: string
|
||||
proxy: string
|
||||
|
@ -24,16 +26,3 @@ export interface SourceLinks {
|
|||
databases: string
|
||||
roles?: string
|
||||
}
|
||||
|
||||
export interface Kapacitor {
|
||||
id?: string
|
||||
url: string
|
||||
name: string
|
||||
username?: string
|
||||
password?: string
|
||||
insecureSkipVerify: boolean
|
||||
active: boolean
|
||||
links: {
|
||||
self: string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
import React from 'react'
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
import _ from 'lodash'
|
||||
|
||||
import KapacitorRules from 'src/kapacitor/components/KapacitorRules'
|
||||
import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable'
|
||||
import TasksTable from 'src/kapacitor/components/TasksTable'
|
||||
|
||||
import {source, kapacitorRules} from 'test/resources'
|
||||
|
||||
describe('Kapacitor.Containers.KapacitorRules', () => {
|
||||
const props = {
|
||||
source,
|
||||
rules: kapacitorRules,
|
||||
hasKapacitor: true,
|
||||
loading: false,
|
||||
onDelete: () => {},
|
||||
onChangeRuleStatus: () => {},
|
||||
}
|
||||
|
||||
describe('rendering', () => {
|
||||
it('renders KapacitorRules', () => {
|
||||
const wrapper = shallow(<KapacitorRules {...props} />)
|
||||
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('renders KapacitorRulesTable', () => {
|
||||
const wrapper = shallow(<KapacitorRules {...props} />)
|
||||
|
||||
const kapacitorRulesTable = wrapper.find(KapacitorRulesTable)
|
||||
expect(kapacitorRulesTable.length).toEqual(1)
|
||||
|
||||
const tasksTable = wrapper.find(TasksTable)
|
||||
expect(tasksTable.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('renders TasksTable', () => {
|
||||
const wrapper = shallow(<KapacitorRules {...props} />)
|
||||
|
||||
const tasksTable = wrapper.find(TasksTable)
|
||||
expect(tasksTable.length).toEqual(1)
|
||||
})
|
||||
|
||||
describe('rows in KapacitorRulesTable and TasksTable', () => {
|
||||
const findRows = (root, reactTable) =>
|
||||
root
|
||||
.find(reactTable)
|
||||
.dive()
|
||||
.find('tbody')
|
||||
.children()
|
||||
.map(child => {
|
||||
const ruleID = child.key()
|
||||
const elRow = child.dive()
|
||||
const elLabel = elRow.find('label')
|
||||
const {htmlFor} = elLabel.props()
|
||||
const elCheckbox = elRow.find({type: 'checkbox'})
|
||||
const {checked, id} = elCheckbox.props()
|
||||
|
||||
return {
|
||||
row: {
|
||||
el: elRow,
|
||||
label: {
|
||||
el: elLabel,
|
||||
htmlFor,
|
||||
},
|
||||
checkbox: {
|
||||
el: elCheckbox,
|
||||
checked,
|
||||
id,
|
||||
},
|
||||
},
|
||||
rule: {
|
||||
id: ruleID, // rule.id
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const containsAnyDuplicate = arr => _.uniq(arr).length !== arr.length
|
||||
|
||||
let wrapper
|
||||
let rulesRows
|
||||
let tasksRows
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<KapacitorRules {...props} />)
|
||||
rulesRows = findRows(wrapper, KapacitorRulesTable)
|
||||
tasksRows = findRows(wrapper, TasksTable)
|
||||
})
|
||||
|
||||
it('renders every rule/task checkbox with unique html id', () => {
|
||||
const allCheckboxIDs = rulesRows
|
||||
.map(r => r.row.checkbox.id)
|
||||
.concat(tasksRows.map(r => r.row.checkbox.id))
|
||||
|
||||
expect(containsAnyDuplicate(allCheckboxIDs)).toEqual(false)
|
||||
})
|
||||
|
||||
it('renders each rule/task table row label with unique "for" attribute', () => {
|
||||
const allCheckboxLabelFors = rulesRows
|
||||
.map(r => r.row.label.htmlFor)
|
||||
.concat(tasksRows.map(r => r.row.label.htmlFor))
|
||||
|
||||
expect(containsAnyDuplicate(allCheckboxLabelFors)).toEqual(false)
|
||||
})
|
||||
|
||||
it('renders one corresponding task row for each rule row', () => {
|
||||
expect(rulesRows.length).toBeLessThanOrEqual(tasksRows.length)
|
||||
|
||||
rulesRows.forEach(ruleRow => {
|
||||
expect(
|
||||
tasksRows.filter(taskRow => taskRow.rule.id === ruleRow.rule.id)
|
||||
.length
|
||||
).toEqual(1)
|
||||
})
|
||||
})
|
||||
|
||||
it('renders corresponding rule/task rows with the same enabled status', () => {
|
||||
const correspondingRows = []
|
||||
|
||||
rulesRows.forEach(ruleRow => {
|
||||
const taskRow = tasksRows
|
||||
.filter(t => t.rule.id === ruleRow.rule.id)
|
||||
.pop()
|
||||
if (taskRow) {
|
||||
correspondingRows.push({ruleRow, taskRow})
|
||||
}
|
||||
})
|
||||
|
||||
correspondingRows.forEach(({ruleRow, taskRow}) => {
|
||||
expect(ruleRow.row.checkbox.checked).toEqual(
|
||||
taskRow.row.checkbox.checked
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,57 @@
|
|||
import React from 'react'
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
import KapacitorRulesTable from 'src/kapacitor/components/KapacitorRulesTable'
|
||||
import {RuleRow} from 'src/kapacitor/components/KapacitorRulesTable'
|
||||
|
||||
import {source, kapacitorRules} from 'test/resources'
|
||||
|
||||
describe('Kapacitor.Components.KapacitorRulesTable', () => {
|
||||
describe('rendering', () => {
|
||||
const props = {
|
||||
source,
|
||||
rules: kapacitorRules,
|
||||
onDelete: () => {},
|
||||
onChangeRuleStatus: () => {},
|
||||
}
|
||||
|
||||
it('renders KapacitorRulesTable', () => {
|
||||
const wrapper = shallow(<KapacitorRulesTable {...props} />)
|
||||
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Kapacitor.Containers.KapacitorRulesTable.RuleRow', () => {
|
||||
const props = {
|
||||
source,
|
||||
rule: kapacitorRules[0],
|
||||
onDelete: () => {},
|
||||
onChangeRuleStatus: jest.fn(),
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('rendering', () => {
|
||||
it('renders RuleRow', () => {
|
||||
const wrapper = shallow(<RuleRow {...props} />)
|
||||
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('user interaction', () => {
|
||||
it('calls onChangeRuleStatus when checkbox is effectively clicked', () => {
|
||||
const wrapper = shallow(<RuleRow {...props} />)
|
||||
|
||||
const checkbox = wrapper.find({type: 'checkbox'})
|
||||
checkbox.simulate('change')
|
||||
|
||||
expect(props.onChangeRuleStatus).toHaveBeenCalledTimes(1)
|
||||
expect(props.onChangeRuleStatus).toHaveBeenCalledWith(kapacitorRules[0])
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react'
|
||||
import {shallow} from 'enzyme'
|
||||
|
||||
import TasksTable from 'src/kapacitor/components/TasksTable'
|
||||
import {TaskRow} from 'src/kapacitor/components/TasksTable'
|
||||
|
||||
import {source, kapacitorRules} from 'test/resources'
|
||||
|
||||
describe('Kapacitor.Components.TasksTable', () => {
|
||||
describe('rendering', () => {
|
||||
const props = {
|
||||
source,
|
||||
tasks: kapacitorRules,
|
||||
onDelete: () => {},
|
||||
onChangeRuleStatus: () => {},
|
||||
}
|
||||
|
||||
it('renders TasksTable', () => {
|
||||
const wrapper = shallow(<TasksTable {...props} />)
|
||||
|
||||
expect(wrapper.exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('user interaction', () => {
|
||||
const props = {
|
||||
source,
|
||||
task: kapacitorRules[3],
|
||||
onDelete: () => {},
|
||||
onChangeRuleStatus: jest.fn(),
|
||||
}
|
||||
|
||||
it('calls onChangeRuleStatus when checkbox is effectively clicked', () => {
|
||||
const wrapper = shallow(<TaskRow {...props} />)
|
||||
|
||||
const checkbox = wrapper.find({type: 'checkbox'})
|
||||
checkbox.simulate('change')
|
||||
|
||||
expect(props.onChangeRuleStatus).toHaveBeenCalledTimes(1)
|
||||
expect(props.onChangeRuleStatus).toHaveBeenCalledWith(kapacitorRules[3])
|
||||
})
|
||||
})
|
||||
})
|
|
@ -63,6 +63,340 @@ export const kapacitor = {
|
|||
},
|
||||
}
|
||||
|
||||
export const kapacitorRules = [
|
||||
{
|
||||
id: 'chronograf-v1-1bb60c5d-9c46-4601-8fdd-930ac5d2ae3d',
|
||||
tickscript:
|
||||
"var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'cpu'\n\nvar groupBy = ['cpu']\n\nvar whereFilter = lambda: (\"cpu\" != 'cpu-total' OR \"cpu\" != 'cpu1')\n\nvar period = 1h\n\nvar name = 'asdfasdfasdfasdfbob'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = ''\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'deadman'\n\nvar threshold = 0.0\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n\nvar trigger = data\n |deadman(threshold, period)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .stateChangesOnly()\n .pushover()\n .pushover()\n .sensu()\n .source('Kapacitorsdfasdf')\n .handlers()\n\ntrigger\n |eval(lambda: \"emitted\")\n .as('value')\n .keep('value', messageField, durationField)\n |eval(lambda: float(\"value\"))\n .as('value')\n .keep()\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n",
|
||||
query: {
|
||||
id: 'chronograf-v1-1bb60c5d-9c46-4601-8fdd-930ac5d2ae3d',
|
||||
database: 'telegraf',
|
||||
measurement: 'cpu',
|
||||
retentionPolicy: 'autogen',
|
||||
fields: [],
|
||||
tags: {
|
||||
cpu: ['cpu-total', 'cpu1'],
|
||||
},
|
||||
groupBy: {
|
||||
time: '',
|
||||
tags: ['cpu'],
|
||||
},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
shifts: null,
|
||||
},
|
||||
every: '',
|
||||
alertNodes: {
|
||||
typeOf: 'alert',
|
||||
stateChangesOnly: true,
|
||||
useFlapping: false,
|
||||
post: [],
|
||||
tcp: [],
|
||||
email: [],
|
||||
exec: [],
|
||||
log: [],
|
||||
victorOps: [],
|
||||
pagerDuty: [],
|
||||
pushover: [
|
||||
{
|
||||
userKey: '',
|
||||
device: '',
|
||||
title: '',
|
||||
url: '',
|
||||
urlTitle: '',
|
||||
sound: '',
|
||||
},
|
||||
{
|
||||
userKey: '',
|
||||
device: '',
|
||||
title: '',
|
||||
url: '',
|
||||
urlTitle: '',
|
||||
sound: '',
|
||||
},
|
||||
],
|
||||
sensu: [
|
||||
{
|
||||
source: 'Kapacitorsdfasdf',
|
||||
handlers: [],
|
||||
},
|
||||
],
|
||||
slack: [],
|
||||
telegram: [],
|
||||
hipChat: [],
|
||||
alerta: [],
|
||||
opsGenie: [],
|
||||
talk: [],
|
||||
},
|
||||
message: '',
|
||||
details: '',
|
||||
trigger: 'deadman',
|
||||
values: {
|
||||
period: '1h0m0s',
|
||||
rangeValue: '',
|
||||
},
|
||||
name: 'asdfasdfasdfasdfbob',
|
||||
type: 'stream',
|
||||
dbrps: [
|
||||
{
|
||||
db: 'telegraf',
|
||||
rp: 'autogen',
|
||||
},
|
||||
],
|
||||
status: 'enabled',
|
||||
executing: true,
|
||||
error: '',
|
||||
created: '2018-01-05T15:40:48.195743458-08:00',
|
||||
modified: '2018-03-13T17:17:23.991640555-07:00',
|
||||
'last-enabled': '2018-03-13T17:17:23.991640555-07:00',
|
||||
links: {
|
||||
self:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/rules/chronograf-v1-1bb60c5d-9c46-4601-8fdd-930ac5d2ae3d',
|
||||
kapacitor:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-1bb60c5d-9c46-4601-8fdd-930ac5d2ae3d',
|
||||
output:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-1bb60c5d-9c46-4601-8fdd-930ac5d2ae3d%2Foutput',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'chronograf-v1-75b638b0-1530-4163-adab-c9631386e0a2',
|
||||
tickscript:
|
||||
"var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'disk'\n\nvar groupBy = []\n\nvar whereFilter = lambda: TRUE\n\nvar name = 'Untitled bob'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = ''\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'threshold'\n\nvar crit = 0\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n |eval(lambda: \"inodes_free\")\n .as('value')\n\nvar trigger = data\n |alert()\n .crit(lambda: \"value\" == crit)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .stateChangesOnly()\n .email()\n .pagerDuty()\n .alerta()\n .environment('bob')\n .origin('kapacitoadfr')\n .services()\n\ntrigger\n |eval(lambda: float(\"value\"))\n .as('value')\n .keep()\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n",
|
||||
query: {
|
||||
id: 'chronograf-v1-75b638b0-1530-4163-adab-c9631386e0a2',
|
||||
database: 'telegraf',
|
||||
measurement: 'disk',
|
||||
retentionPolicy: 'autogen',
|
||||
fields: [
|
||||
{
|
||||
value: 'inodes_free',
|
||||
type: 'field',
|
||||
alias: '',
|
||||
},
|
||||
],
|
||||
tags: {},
|
||||
groupBy: {
|
||||
time: '',
|
||||
tags: [],
|
||||
},
|
||||
areTagsAccepted: false,
|
||||
rawText: null,
|
||||
range: null,
|
||||
shifts: null,
|
||||
},
|
||||
every: '',
|
||||
alertNodes: {
|
||||
typeOf: 'alert',
|
||||
stateChangesOnly: true,
|
||||
useFlapping: false,
|
||||
post: [],
|
||||
tcp: [],
|
||||
email: [
|
||||
{
|
||||
to: [],
|
||||
},
|
||||
],
|
||||
exec: [],
|
||||
log: [],
|
||||
victorOps: [],
|
||||
pagerDuty: [
|
||||
{
|
||||
serviceKey: '',
|
||||
},
|
||||
],
|
||||
pushover: [],
|
||||
sensu: [],
|
||||
slack: [],
|
||||
telegram: [],
|
||||
hipChat: [],
|
||||
alerta: [
|
||||
{
|
||||
token: '',
|
||||
resource: '',
|
||||
event: '',
|
||||
environment: 'bob',
|
||||
group: '',
|
||||
value: '',
|
||||
origin: 'kapacitoadfr',
|
||||
service: [],
|
||||
},
|
||||
],
|
||||
opsGenie: [],
|
||||
talk: [],
|
||||
},
|
||||
message: '',
|
||||
details: '',
|
||||
trigger: 'threshold',
|
||||
values: {
|
||||
operator: 'equal to',
|
||||
value: '0',
|
||||
rangeValue: '',
|
||||
},
|
||||
name: 'Untitled bob',
|
||||
type: 'stream',
|
||||
dbrps: [
|
||||
{
|
||||
db: 'telegraf',
|
||||
rp: 'autogen',
|
||||
},
|
||||
],
|
||||
status: 'disabled',
|
||||
executing: false,
|
||||
error: '',
|
||||
created: '2018-01-05T15:41:22.759905067-08:00',
|
||||
modified: '2018-03-14T18:46:37.940091231-07:00',
|
||||
'last-enabled': '2018-03-14T18:46:32.409262103-07:00',
|
||||
links: {
|
||||
self:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/rules/chronograf-v1-75b638b0-1530-4163-adab-c9631386e0a2',
|
||||
kapacitor:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-75b638b0-1530-4163-adab-c9631386e0a2',
|
||||
output:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-75b638b0-1530-4163-adab-c9631386e0a2%2Foutput',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'chronograf-v1-7734918d-b8b6-460d-a416-34767ba76faa',
|
||||
tickscript:
|
||||
"var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'cpu'\n\nvar groupBy = []\n\nvar whereFilter = lambda: (\"host\" == 'Bobs-MacBook-Pro.local')\n\nvar period = 24h\n\nvar name = 'xena'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = ''\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'deadman'\n\nvar threshold = 0.0\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n\nvar trigger = data\n |deadman(threshold, period)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .hipChat()\n .room('asdf')\n\ntrigger\n |eval(lambda: \"emitted\")\n .as('value')\n .keep('value', messageField, durationField)\n |eval(lambda: float(\"value\"))\n .as('value')\n .keep()\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n",
|
||||
query: {
|
||||
id: 'chronograf-v1-7734918d-b8b6-460d-a416-34767ba76faa',
|
||||
database: 'telegraf',
|
||||
measurement: 'cpu',
|
||||
retentionPolicy: 'autogen',
|
||||
fields: [],
|
||||
tags: {
|
||||
host: ['Bobs-MacBook-Pro.local'],
|
||||
},
|
||||
groupBy: {
|
||||
time: '',
|
||||
tags: [],
|
||||
},
|
||||
areTagsAccepted: true,
|
||||
rawText: null,
|
||||
range: null,
|
||||
shifts: null,
|
||||
},
|
||||
every: '',
|
||||
alertNodes: {
|
||||
typeOf: 'alert',
|
||||
stateChangesOnly: true,
|
||||
useFlapping: false,
|
||||
post: [],
|
||||
tcp: [],
|
||||
email: [],
|
||||
exec: [],
|
||||
log: [],
|
||||
victorOps: [],
|
||||
pagerDuty: [],
|
||||
pushover: [],
|
||||
sensu: [],
|
||||
slack: [],
|
||||
telegram: [],
|
||||
hipChat: [
|
||||
{
|
||||
room: 'asdf',
|
||||
token: '',
|
||||
},
|
||||
],
|
||||
alerta: [],
|
||||
opsGenie: [],
|
||||
talk: [],
|
||||
},
|
||||
message: '',
|
||||
details: '',
|
||||
trigger: 'deadman',
|
||||
values: {
|
||||
period: '24h0m0s',
|
||||
rangeValue: '',
|
||||
},
|
||||
name: 'xena',
|
||||
type: 'stream',
|
||||
dbrps: [
|
||||
{
|
||||
db: 'telegraf',
|
||||
rp: 'autogen',
|
||||
},
|
||||
],
|
||||
status: 'disabled',
|
||||
executing: false,
|
||||
error: '',
|
||||
created: '2018-01-05T15:44:54.657212781-08:00',
|
||||
modified: '2018-03-13T17:17:19.099800735-07:00',
|
||||
'last-enabled': '2018-03-13T17:17:15.964357573-07:00',
|
||||
links: {
|
||||
self:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/rules/chronograf-v1-7734918d-b8b6-460d-a416-34767ba76faa',
|
||||
kapacitor:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-7734918d-b8b6-460d-a416-34767ba76faa',
|
||||
output:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-7734918d-b8b6-460d-a416-34767ba76faa%2Foutput',
|
||||
},
|
||||
},
|
||||
{
|
||||
// if rule has no `query` key, it will display as a tickscript task only
|
||||
id: 'chronograf-v1-7734918d-b8b6-460d-a416-34767ba76aac',
|
||||
tickscript:
|
||||
"var db = 'telegraf'\n\nvar rp = 'autogen'\n\nvar measurement = 'cpu'\n\nvar groupBy = []\n\nvar whereFilter = lambda: (\"host\" == 'Bobs-MacBook-Pro.local')\n\nvar period = 24h\n\nvar name = 'xena'\n\nvar idVar = name + ':{{.Group}}'\n\nvar message = ''\n\nvar idTag = 'alertID'\n\nvar levelTag = 'level'\n\nvar messageField = 'message'\n\nvar durationField = 'duration'\n\nvar outputDB = 'chronograf'\n\nvar outputRP = 'autogen'\n\nvar outputMeasurement = 'alerts'\n\nvar triggerType = 'deadman'\n\nvar threshold = 0.0\n\nvar data = stream\n |from()\n .database(db)\n .retentionPolicy(rp)\n .measurement(measurement)\n .groupBy(groupBy)\n .where(whereFilter)\n\nvar trigger = data\n |deadman(threshold, period)\n .stateChangesOnly()\n .message(message)\n .id(idVar)\n .idTag(idTag)\n .levelTag(levelTag)\n .messageField(messageField)\n .durationField(durationField)\n .hipChat()\n .room('asdf')\n\ntrigger\n |eval(lambda: \"emitted\")\n .as('value')\n .keep('value', messageField, durationField)\n |eval(lambda: float(\"value\"))\n .as('value')\n .keep()\n |influxDBOut()\n .create()\n .database(outputDB)\n .retentionPolicy(outputRP)\n .measurement(outputMeasurement)\n .tag('alertName', name)\n .tag('triggerType', triggerType)\n\ntrigger\n |httpOut('output')\n",
|
||||
every: '',
|
||||
alertNodes: {
|
||||
typeOf: 'alert',
|
||||
stateChangesOnly: true,
|
||||
useFlapping: false,
|
||||
post: [],
|
||||
tcp: [],
|
||||
email: [],
|
||||
exec: [],
|
||||
log: [],
|
||||
victorOps: [],
|
||||
pagerDuty: [],
|
||||
pushover: [],
|
||||
sensu: [],
|
||||
slack: [],
|
||||
telegram: [],
|
||||
hipChat: [
|
||||
{
|
||||
room: 'asdf',
|
||||
token: '',
|
||||
},
|
||||
],
|
||||
alerta: [],
|
||||
opsGenie: [],
|
||||
talk: [],
|
||||
},
|
||||
message: '',
|
||||
details: '',
|
||||
trigger: 'deadman',
|
||||
values: {
|
||||
period: '24h0m0s',
|
||||
rangeValue: '',
|
||||
},
|
||||
name: 'pineapples',
|
||||
type: 'stream',
|
||||
dbrps: [
|
||||
{
|
||||
db: 'telegraf',
|
||||
rp: 'autogen',
|
||||
},
|
||||
],
|
||||
status: 'enabled',
|
||||
executing: false,
|
||||
error: '',
|
||||
created: '2018-01-05T15:44:54.657212781-08:00',
|
||||
modified: '2018-03-13T17:17:19.099800735-07:00',
|
||||
'last-enabled': '2018-03-13T17:17:15.964357573-07:00',
|
||||
links: {
|
||||
self:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/rules/chronograf-v1-7734918d-b8b6-460d-a416-34767ba76aac',
|
||||
kapacitor:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-7734918d-b8b6-460d-a416-34767ba76aac',
|
||||
output:
|
||||
'/chronograf/v1/sources/1/kapacitors/1/proxy?path=%2Fkapacitor%2Fv1%2Ftasks%2Fchronograf-v1-7734918d-b8b6-460d-a416-34767ba76aac%2Foutput',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export const authLinks = {
|
||||
allUsers: '/chronograf/v1/users',
|
||||
auth: [
|
||||
|
|
Loading…
Reference in New Issue