Merge pull request #2098 from influxdata/bugfix/not-equal-to
BUGFIX: Fix display of not-equal-to on rule graphpull/10616/head
commit
0f23b3774e
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'}}
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue