Visualize check view with check plot (#14599)

* Use CheckPlot to visualize check view

* Revert to add threshold types

* Disable view type dropdown in checkView

* Add proper equality

* Add errors in default cases
pull/14605/head
Deniz Kusefoglu 2019-08-08 12:25:58 -07:00 committed by GitHub
parent cb9d8eafe7
commit 17c40a75de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 203 additions and 85 deletions

View File

@ -8938,6 +8938,11 @@ components:
type: array
items:
$ref: "#/components/schemas/Threshold"
Threshold:
oneOf:
- $ref: "#/components/schemas/GreaterThreshold"
- $ref: "#/components/schemas/LesserThreshold"
- $ref: "#/components/schemas/RangeThreshold"
DeadmanCheck:
allOf:
- $ref: "#/components/schemas/CheckBase"
@ -8954,19 +8959,54 @@ components:
type: boolean
level:
$ref: "#/components/schemas/CheckStatusLevel"
Threshold:
ThresholdBase:
properties:
level:
$ref: "#/components/schemas/CheckStatusLevel"
allValues:
description: if true, only alert if all values meet threshold
type: boolean
lowerBound:
type: number
format: float
upperBound:
type: number
format: float
GreaterThreshold:
allOf:
- $ref: "#/components/schemas/ThresholdBase"
- type: object
required: [type, value]
properties:
type:
type: string
enum: [greater]
value:
type: number
format: float
LesserThreshold:
allOf:
- $ref: "#/components/schemas/ThresholdBase"
- type: object
required: [type, value]
properties:
type:
type: string
enum: [lesser]
value:
type: number
format: float
RangeThreshold:
allOf:
- $ref: "#/components/schemas/ThresholdBase"
- type: object
required: [type, min, max, within]
properties:
type:
type: string
enum: [range]
min:
type: number
format: float
max:
type: number
format: float
within:
type: boolean
CheckStatusLevel:
description: the state to record if check matches a criteria
type: string

View File

@ -23,7 +23,10 @@ export const DEFAULT_THRESHOLD_CHECK: Partial<ThresholdCheck> = {
name: DEFAULT_CHECK_NAME,
type: 'threshold',
status: 'active',
thresholds: [],
thresholds: [
{type: 'greater', value: 40, level: 'INFO'},
{type: 'lesser', value: 20, level: 'OK'},
],
every: DEFAULT_CHECK_EVERY,
offset: DEFAULT_CHECK_OFFSET,
}
@ -59,9 +62,9 @@ export const check1: Check = {
statusMessageTemplate: 'this is a great message template',
thresholds: [
{
level: 'UNKNOWN',
lowerBound: 20,
allValues: false,
level: 'CRIT',
type: 'lesser',
value: 10,
},
],
}
@ -81,9 +84,9 @@ export const check2: Check = {
statusMessageTemplate: 'this is a great message template',
thresholds: [
{
level: 'UNKNOWN',
lowerBound: 20,
allValues: false,
level: 'INFO',
type: 'greater',
value: 10,
},
],
}

View File

@ -1,5 +1,6 @@
// Libraries
import React, {useState, FunctionComponent} from 'react'
import React, {FunctionComponent} from 'react'
import {connect} from 'react-redux'
import {Config, Table} from '@influxdata/giraffe'
import {flatMap} from 'lodash'
@ -21,21 +22,25 @@ import {
RemoteDataState,
CheckViewProperties,
TimeZone,
AppState,
Check,
Threshold,
} from 'src/types'
import {updateCurrentCheck} from 'src/alerting/actions/checks'
const X_COLUMN = '_time'
const Y_COLUMN = '_value'
const THRESHOLDS: Threshold[] = [
{
level: 'UNKNOWN',
lowerBound: 20,
allValues: false,
},
]
interface DispatchProps {
updateCurrentCheck: typeof updateCurrentCheck
}
interface Props {
interface StateProps {
check: Partial<Check>
thresholds: Threshold[]
}
interface OwnProps {
table: Table
fluxGroupKeyUnion: string[]
loading: RemoteDataState
@ -44,14 +49,20 @@ interface Props {
children: (config: Config) => JSX.Element
}
type Props = OwnProps & DispatchProps & StateProps
const CheckPlot: FunctionComponent<Props> = ({
updateCurrentCheck,
table,
thresholds,
fluxGroupKeyUnion,
loading,
children,
timeZone,
}) => {
const [thresholds, setThresholds] = useState(THRESHOLDS)
const updateCheckThresholds = (thresholds: Threshold[]) => {
updateCurrentCheck({thresholds})
}
const [yDomain, onSetYDomain, onResetYDomain] = useVisDomainSettings(
[0, 100],
@ -115,7 +126,7 @@ const CheckPlot: FunctionComponent<Props> = ({
<ThresholdMarkers
key="custom"
thresholds={thresholds}
onSetThresholds={setThresholds}
onSetThresholds={updateCheckThresholds}
yScale={yScale}
yDomain={yDomain}
/>
@ -132,4 +143,26 @@ const CheckPlot: FunctionComponent<Props> = ({
)
}
export default CheckPlot
const mstp = (state: AppState): StateProps => {
const {
checks: {
current: {check},
},
} = state
let thresholds = []
if (check.type === 'threshold') {
thresholds = check.thresholds
}
return {check, thresholds}
}
const mdtp: DispatchProps = {
updateCurrentCheck: updateCurrentCheck,
}
export default connect<StateProps, DispatchProps, {}>(
mstp,
mdtp
)(CheckPlot)

View File

@ -11,29 +11,29 @@ import {isInDomain, clamp} from 'src/shared/utils/vis'
import {DragEvent} from 'src/shared/utils/useDragBehavior'
// Types
import {Threshold} from 'src/types'
import {GreaterThreshold} from 'src/types'
interface Props {
yScale: Scale<number, number>
yDomain: number[]
threshold: Threshold
threshold: GreaterThreshold
onChangePos: (e: DragEvent) => void
}
const GreaterThresholdMarker: FunctionComponent<Props> = ({
yDomain,
yScale,
threshold: {level, lowerBound},
threshold: {level, value},
onChangePos,
}) => {
const y = yScale(clamp(lowerBound, yDomain))
const y = yScale(clamp(value, yDomain))
return (
<>
{isInDomain(lowerBound, yDomain) && (
{isInDomain(value, yDomain) && (
<ThresholdMarker level={level} y={y} onDrag={onChangePos} />
)}
{lowerBound <= yDomain[1] && (
{value <= yDomain[1] && (
<ThresholdMarkerArea
level={level}
top={yScale(yDomain[1])}

View File

@ -11,29 +11,29 @@ import {clamp, isInDomain} from 'src/shared/utils/vis'
import {DragEvent} from 'src/shared/utils/useDragBehavior'
// Types
import {Threshold} from 'src/types'
import {LesserThreshold} from 'src/types'
interface Props {
yScale: Scale<number, number>
yDomain: number[]
threshold: Threshold
threshold: LesserThreshold
onChangePos: (e: DragEvent) => void
}
const LessThresholdMarker: FunctionComponent<Props> = ({
yScale,
yDomain,
threshold: {level, upperBound},
threshold: {level, value},
onChangePos,
}) => {
const y = yScale(clamp(upperBound, yDomain))
const y = yScale(clamp(value, yDomain))
return (
<>
{isInDomain(upperBound, yDomain) && (
{isInDomain(value, yDomain) && (
<ThresholdMarker level={level} y={y} onDrag={onChangePos} />
)}
{upperBound >= yDomain[0] && (
{value >= yDomain[0] && (
<ThresholdMarkerArea
level={level}
top={y}

View File

@ -11,12 +11,12 @@ import {isInDomain, clamp} from 'src/shared/utils/vis'
import {DragEvent} from 'src/shared/utils/useDragBehavior'
// Types
import {Threshold} from 'src/types'
import {RangeThreshold} from 'src/types'
interface Props {
yScale: Scale<number, number>
yDomain: number[]
threshold: Threshold
threshold: RangeThreshold
onChangeMaxPos: (e: DragEvent) => void
onChangeMinPos: (e: DragEvent) => void
}
@ -24,33 +24,33 @@ interface Props {
const RangeThresholdMarkers: FunctionComponent<Props> = ({
yScale,
yDomain,
threshold: {level, lowerBound, upperBound},
threshold: {level, within, min, max},
onChangeMinPos,
onChangeMaxPos,
}) => {
const minY = yScale(clamp(lowerBound, yDomain))
const maxY = yScale(clamp(upperBound, yDomain))
const minY = yScale(clamp(min, yDomain))
const maxY = yScale(clamp(max, yDomain))
return (
<>
{isInDomain(lowerBound, yDomain) && (
{isInDomain(min, yDomain) && (
<ThresholdMarker level={level} y={minY} onDrag={onChangeMinPos} />
)}
{isInDomain(upperBound, yDomain) && (
{isInDomain(max, yDomain) && (
<ThresholdMarker level={level} y={maxY} onDrag={onChangeMaxPos} />
)}
{lowerBound > upperBound ? (
{within ? (
<ThresholdMarkerArea level={level} top={maxY} height={minY - maxY} />
) : (
<>
{upperBound <= yDomain[1] && (
{max <= yDomain[1] && (
<ThresholdMarkerArea
level={level}
top={yScale(yDomain[1])}
height={maxY - yScale(yDomain[1])}
/>
)}
{lowerBound >= yDomain[0] && (
{min >= yDomain[0] && (
<ThresholdMarkerArea
level={level}
top={minY}

View File

@ -36,6 +36,18 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
[field]: yValue,
}
if (
nextThreshold.type === 'range' &&
nextThreshold.min > nextThreshold.max
) {
// If the user drags the min past the max or vice versa, we swap the
// values that are set so that the min is always at most the max
const maxValue = nextThreshold.min
nextThreshold.min = nextThreshold.max
nextThreshold.max = maxValue
}
const nextThresholds = thresholds.map((t, i) =>
i === index ? nextThreshold : t
)
@ -50,41 +62,40 @@ const ThresholdMarkers: FunctionComponent<Props> = ({
const onChangeMaxPos = ({y}) => handleDrag(index, 'maxValue', y)
const onChangeMinPos = ({y}) => handleDrag(index, 'minValue', y)
if (threshold.lowerBound && threshold.upperBound) {
return (
<RangeThresholdMarkers
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangeMinPos={onChangeMinPos}
onChangeMaxPos={onChangeMaxPos}
/>
)
}
if (threshold.lowerBound) {
return (
<GreaterThresholdMarker
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangePos={onChangePos}
/>
)
}
if (threshold.upperBound) {
return (
<LessThresholdMarker
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangePos={onChangePos}
/>
)
switch (threshold.type) {
case 'greater':
return (
<GreaterThresholdMarker
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangePos={onChangePos}
/>
)
case 'lesser':
return (
<LessThresholdMarker
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangePos={onChangePos}
/>
)
case 'range':
return (
<RangeThresholdMarkers
key={index}
yScale={yScale}
yDomain={yDomain}
threshold={threshold}
onChangeMinPos={onChangeMinPos}
onChangeMaxPos={onChangeMaxPos}
/>
)
default:
throw new Error('Unknown threshold type in <ThresholdMarkers /> ')
}
})}
</div>

View File

@ -12,6 +12,7 @@ import FluxTablesTransform from 'src/shared/components/FluxTablesTransform'
import XYPlot from 'src/shared/components/XYPlot'
import ScatterPlot from 'src/shared/components/ScatterPlot'
import LatestValueTransform from 'src/shared/components/LatestValueTransform'
import CheckPlot from 'src/shared/components/CheckPlot'
// Types
import {
@ -155,8 +156,21 @@ const ViewSwitcher: FunctionComponent<Props> = ({
</ScatterPlot>
)
case 'check':
return (
<CheckPlot
table={table}
fluxGroupKeyUnion={fluxGroupKeyUnion}
loading={loading}
timeZone={timeZone}
viewProperties={properties}
>
{config => <Plot config={config} />}
</CheckPlot>
)
default:
return <div />
throw new Error('Unknown view type in <ViewSwitcher /> ')
}
}

View File

@ -16,6 +16,7 @@ import {VIS_GRAPHICS} from 'src/timeMachine/constants/visGraphics'
// Types
import {View, NewView, AppState, ViewType} from 'src/types'
import {ComponentStatus} from 'src/clockface'
interface DispatchProps {
onUpdateType: typeof setType
@ -34,7 +35,11 @@ class ViewTypeDropdown extends PureComponent<Props> {
widthPixels={215}
className="view-type-dropdown"
button={(active, onClick) => (
<Dropdown.Button active={active} onClick={onClick}>
<Dropdown.Button
active={active}
onClick={onClick}
status={this.dropdownStatus}
>
{this.getVewTypeGraphic(this.selectedView)}
</Dropdown.Button>
)}
@ -67,6 +72,15 @@ class ViewTypeDropdown extends PureComponent<Props> {
))
}
private get dropdownStatus(): ComponentStatus {
const {view} = this.props
if (view.properties.type === 'check') {
return ComponentStatus.Disabled
}
return ComponentStatus.Valid
}
private get selectedView(): ViewType {
const {view} = this.props

View File

@ -37,6 +37,9 @@ export {
LevelRule,
TagRule,
CheckStatusLevel,
GreaterThreshold,
LesserThreshold,
RangeThreshold,
ThresholdCheck,
DeadmanCheck,
NotificationEndpoint,