Merge pull request #2098 from influxdata/bugfix/not-equal-to

BUGFIX: Fix display of not-equal-to on rule graph
pull/10616/head
Andrew Watkins 2017-10-16 17:50:23 -07:00 committed by GitHub
commit 0f23b3774e
6 changed files with 179 additions and 128 deletions

View File

@ -2,6 +2,7 @@
### Bug Fixes
1. [#2095](https://github.com/influxdata/chronograf/pull/2095): Improve the copy in the retention policy edit page
1. [#2093](https://github.com/influxdata/chronograf/pull/2093): Fix when exporting `SHOW DATABASES` CSV has bad data
1. [#2098](https://github.com/influxdata/chronograf/pull/2098): Fix not-equal-to highlighting in Kapacitor Rule Builder
### Features
1. [#2083](https://github.com/influxdata/chronograf/pull/2083): Every dashboard can now have its own time range

View File

@ -3,132 +3,66 @@ import buildInfluxQLQuery from 'utils/influxql'
import AutoRefresh from 'shared/components/AutoRefresh'
import LineGraph from 'shared/components/LineGraph'
import TimeRangeDropdown from 'shared/components/TimeRangeDropdown'
import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay'
const RefreshingLineGraph = AutoRefresh(LineGraph)
const {shape, string, func} = PropTypes
const RuleGraph = ({
query,
source,
timeRange: {lower},
timeRange,
rule,
onChooseTimeRange,
}) => {
const autoRefreshMs = 30000
const queryText = buildInfluxQLQuery({lower}, query)
const queries = [{host: source.links.proxy, text: queryText}]
const kapacitorLineColors = ['#4ED8A0']
export const RuleGraph = React.createClass({
propTypes: {
source: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}).isRequired,
query: shape({}).isRequired,
rule: shape({}).isRequired,
timeRange: shape({}).isRequired,
onChooseTimeRange: func.isRequired,
},
render() {
const {
query,
source,
timeRange: {lower},
timeRange,
rule,
onChooseTimeRange,
} = this.props
const autoRefreshMs = 30000
const queryText = buildInfluxQLQuery({lower}, query)
const queries = [{host: source.links.proxy, text: queryText}]
const kapacitorLineColors = ['#4ED8A0']
if (!queryText) {
return (
<div className="rule-builder--graph-empty">
<p>
Select a <strong>Time-Series</strong> to preview on a graph
</p>
</div>
)
}
if (!queryText) {
return (
<div className="rule-builder--graph">
<div className="rule-builder--graph-options">
<p>Preview Data from</p>
<TimeRangeDropdown
onChooseTimeRange={onChooseTimeRange}
selected={timeRange}
preventCustomTimeRange={true}
/>
</div>
<RefreshingLineGraph
queries={queries}
autoRefresh={autoRefreshMs}
underlayCallback={this.createUnderlayCallback()}
isGraphFilled={false}
overrideLineColors={kapacitorLineColors}
ruleValues={rule.values}
/>
<div className="rule-builder--graph-empty">
<p>
Select a <strong>Time-Series</strong> to preview on a graph
</p>
</div>
)
},
}
createUnderlayCallback() {
const {rule} = this.props
return (canvas, area, dygraph) => {
if (rule.trigger !== 'threshold' || rule.values.value === '') {
return
}
return (
<div className="rule-builder--graph">
<div className="rule-builder--graph-options">
<p>Preview Data from</p>
<TimeRangeDropdown
onChooseTimeRange={onChooseTimeRange}
selected={timeRange}
preventCustomTimeRange={true}
/>
</div>
<RefreshingLineGraph
queries={queries}
isGraphFilled={false}
ruleValues={rule.values}
autoRefresh={autoRefreshMs}
overrideLineColors={kapacitorLineColors}
underlayCallback={underlayCallback(rule)}
/>
</div>
)
}
const theOnePercent = 0.01
let highlightStart = 0
let highlightEnd = 0
switch (rule.values.operator) {
case 'equal to or greater':
case 'greater than': {
highlightStart = rule.values.value
highlightEnd = dygraph.yAxisRange()[1]
break
}
case 'equal to or less than':
case 'less than': {
highlightStart = dygraph.yAxisRange()[0]
highlightEnd = rule.values.value
break
}
case 'not equal to':
case 'equal to': {
const width =
theOnePercent * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0])
highlightStart = +rule.values.value - width
highlightEnd = +rule.values.value + width
break
}
case 'outside range': {
const {rangeValue, value} = rule.values
highlightStart = Math.min(+value, +rangeValue)
highlightEnd = Math.max(+value, +rangeValue)
canvas.fillStyle = 'rgba(78, 216, 160, 0.3)'
canvas.fillRect(area.x, area.y, area.w, area.h)
break
}
case 'inside range': {
const {rangeValue, value} = rule.values
highlightStart = Math.min(+value, +rangeValue)
highlightEnd = Math.max(+value, +rangeValue)
break
}
}
const bottom = dygraph.toDomYCoord(highlightStart)
const top = dygraph.toDomYCoord(highlightEnd)
canvas.fillStyle =
rule.values.operator === 'outside range'
? 'rgba(41, 41, 51, 1)'
: 'rgba(78, 216, 160, 0.3)'
canvas.fillRect(area.x, top, area.w, bottom - top)
}
},
})
RuleGraph.propTypes = {
source: shape({
links: shape({
proxy: string.isRequired,
}).isRequired,
}).isRequired,
query: shape({}).isRequired,
rule: shape({}).isRequired,
timeRange: shape({}).isRequired,
onChooseTimeRange: func.isRequired,
}
export default RuleGraph

View File

@ -4,6 +4,7 @@ import Dropdown from 'shared/components/Dropdown'
const mapToItems = (arr, type) => arr.map(text => ({text, type}))
const operators = mapToItems(OPERATORS, 'operator')
const noopSubmit = e => e.preventDefault()
const Threshold = ({
rule: {values: {operator, value, rangeValue}},
@ -24,7 +25,7 @@ const Threshold = ({
selected={operator}
onChoose={onDropdownChange}
/>
<form style={{display: 'flex'}}>
<form style={{display: 'flex'}} onSubmit={noopSubmit}>
<input
className="form-control input-sm form-malachite monotype"
style={{width: '160px', marginLeft: '6px'}}

View File

@ -21,16 +21,27 @@ export const defaultRuleConfigs = {
export const defaultEveryFrequency = '30s'
// constants taken from https://github.com/influxdata/chronograf/blob/870dbc72d1a8b784eaacad5eeea79fc54968b656/kapacitor/operators.go#L13
export const EQUAL_TO = 'equal to'
export const LESS_THAN = 'less than'
export const GREATER_THAN = 'greater than'
export const NOT_EQUAL_TO = 'not equal to'
export const INSIDE_RANGE = 'inside range'
export const OUTSIDE_RANGE = 'outside range'
export const EQUAL_TO_OR_GREATER_THAN = 'equal to or greater'
export const EQUAL_TO_OR_LESS_THAN = 'equal to or less than'
export const OPERATORS = [
'greater than',
'equal to or greater',
'equal to or less than',
'less than',
'equal to',
'not equal to',
'inside range',
'outside range',
GREATER_THAN,
EQUAL_TO_OR_GREATER_THAN,
EQUAL_TO_OR_LESS_THAN,
LESS_THAN,
EQUAL_TO,
NOT_EQUAL_TO,
INSIDE_RANGE,
OUTSIDE_RANGE,
]
// export const RELATIONS = ['once', 'more than ', 'less than'];
export const PERIODS = ['1m', '5m', '10m', '30m', '1h', '2h', '24h']
export const CHANGES = ['change', '% change']

View File

@ -0,0 +1,100 @@
import {
EQUAL_TO,
LESS_THAN,
NOT_EQUAL_TO,
GREATER_THAN,
INSIDE_RANGE,
OUTSIDE_RANGE,
EQUAL_TO_OR_LESS_THAN,
EQUAL_TO_OR_GREATER_THAN,
} from 'src/kapacitor/constants'
const HIGHLIGHT = 'rgba(78, 216, 160, 0.3)'
const BACKGROUND = 'rgba(41, 41, 51, 1)'
const getFillColor = operator => {
const backgroundColor = BACKGROUND
const highlightColor = HIGHLIGHT
if (operator === OUTSIDE_RANGE) {
return backgroundColor
}
if (operator === NOT_EQUAL_TO) {
return backgroundColor
}
return highlightColor
}
const underlayCallback = rule => (canvas, area, dygraph) => {
const {values} = rule
const {operator, value} = values
if (rule.trigger !== 'threshold' || value === '' || !isFinite(value)) {
return
}
const theOnePercent = 0.01
let highlightStart = 0
let highlightEnd = 0
switch (operator) {
case `${EQUAL_TO_OR_GREATER_THAN}`:
case `${GREATER_THAN}`: {
highlightStart = value
highlightEnd = dygraph.yAxisRange()[1]
break
}
case `${EQUAL_TO_OR_LESS_THAN}`:
case `${LESS_THAN}`: {
highlightStart = dygraph.yAxisRange()[0]
highlightEnd = value
break
}
case `${EQUAL_TO}`: {
const width =
theOnePercent * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0])
highlightStart = +value - width
highlightEnd = +value + width
break
}
case `${NOT_EQUAL_TO}`: {
const width =
theOnePercent * (dygraph.yAxisRange()[1] - dygraph.yAxisRange()[0])
highlightStart = +value - width
highlightEnd = +value + width
canvas.fillStyle = HIGHLIGHT
canvas.fillRect(area.x, area.y, area.w, area.h)
break
}
case `${OUTSIDE_RANGE}`: {
highlightStart = Math.min(+value, +values.rangeValue)
highlightEnd = Math.max(+value, +values.rangeValue)
canvas.fillStyle = HIGHLIGHT
canvas.fillRect(area.x, area.y, area.w, area.h)
break
}
case `${INSIDE_RANGE}`: {
highlightStart = Math.min(+value, +values.rangeValue)
highlightEnd = Math.max(+value, +values.rangeValue)
break
}
}
const bottom = dygraph.toDomYCoord(highlightStart)
const top = dygraph.toDomYCoord(highlightEnd)
const fillColor = getFillColor(operator)
canvas.fillStyle = fillColor
canvas.fillRect(area.x, top, area.w, bottom - top)
}
export default underlayCallback

View File

@ -3,6 +3,8 @@ import BigNumber from 'bignumber.js'
const ADD_FACTOR = 1.1
const SUB_FACTOR = 0.9
const checkNumeric = num => (isFinite(num) ? num : null)
const considerEmpty = (userNumber, number) => {
if (userNumber) {
return +userNumber
@ -17,13 +19,15 @@ const getRange = (
ruleValues = {value: null, rangeValue: null, operator: ''}
) => {
const {value, rangeValue, operator} = ruleValues
const [userMin, userMax] = userSelectedRange
const [uMin, uMax] = userSelectedRange
const userMin = checkNumeric(uMin)
const userMax = checkNumeric(uMax)
const addPad = bigNum => bigNum.times(ADD_FACTOR).toNumber()
const subPad = bigNum => bigNum.times(SUB_FACTOR).toNumber()
const pad = v => {
if (v === null || v === '' || v === undefined) {
if (v === null || v === '' || !isFinite(v)) {
return null
}