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 casespull/14605/head
parent
cb9d8eafe7
commit
17c40a75de
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 /> ')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -37,6 +37,9 @@ export {
|
|||
LevelRule,
|
||||
TagRule,
|
||||
CheckStatusLevel,
|
||||
GreaterThreshold,
|
||||
LesserThreshold,
|
||||
RangeThreshold,
|
||||
ThresholdCheck,
|
||||
DeadmanCheck,
|
||||
NotificationEndpoint,
|
||||
|
|
Loading…
Reference in New Issue