2021-05-18 16:16:26 +02:00
import { css } from '@emotion/css' ;
2022-12-22 16:28:17 +01:00
import { cloneDeep } from 'lodash' ;
2023-03-14 07:38:21 -07:00
import React , { ChangeEvent , useState } from 'react' ;
2022-04-22 14:33:13 +01:00
2021-05-18 16:16:26 +02:00
import {
2021-11-12 12:49:06 +01:00
CoreApp ,
2023-04-27 16:38:22 +02:00
DataSourceApi ,
2021-05-18 16:16:26 +02:00
DataSourceInstanceSettings ,
GrafanaTheme2 ,
2021-10-12 13:49:30 +03:00
LoadingState ,
2021-05-18 16:16:26 +02:00
PanelData ,
RelativeTimeRange ,
2021-09-15 17:35:12 +02:00
ThresholdsConfig ,
2021-05-18 16:16:26 +02:00
} from '@grafana/data' ;
2022-10-24 17:12:36 +02:00
import { Stack } from '@grafana/experimental' ;
2023-06-16 13:14:46 +02:00
import { DataQuery } from '@grafana/schema' ;
2023-09-14 15:05:28 +02:00
import { GraphTresholdsStyleMode , Icon , InlineField , Input , Tooltip , useStyles2 } from '@grafana/ui' ;
2022-04-22 14:33:13 +01:00
import { QueryEditorRow } from 'app/features/query/components/QueryEditorRow' ;
import { AlertQuery } from 'app/types/unified-alerting-dto' ;
2023-07-24 11:20:55 -04:00
import { msToSingleUnitDuration } from '../../utils/time' ;
2023-09-14 15:05:28 +02:00
import { ExpressionStatusIndicator } from '../expressions/ExpressionStatusIndicator' ;
2022-04-22 14:33:13 +01:00
2023-07-04 11:17:14 -03:00
import { QueryOptions } from './QueryOptions' ;
2022-04-22 14:33:13 +01:00
import { VizWrapper } from './VizWrapper' ;
2021-05-18 16:16:26 +02:00
2023-01-24 09:16:21 +01:00
export const DEFAULT_MAX_DATA_POINTS = 43200 ;
2023-07-24 11:20:55 -04:00
export const DEFAULT_MIN_INTERVAL = '1s' ;
2023-01-24 09:16:21 +01:00
export interface AlertQueryOptions {
maxDataPoints? : number | undefined ;
2023-07-24 11:20:55 -04:00
minInterval? : string | undefined ;
2023-01-24 09:16:21 +01:00
}
2021-05-18 16:16:26 +02:00
interface Props {
data : PanelData ;
2022-10-05 14:35:15 +02:00
error? : Error ;
2021-05-27 12:29:10 +02:00
query : AlertQuery ;
queries : AlertQuery [ ] ;
2021-05-18 16:16:26 +02:00
dsSettings : DataSourceInstanceSettings ;
onChangeDataSource : ( settings : DataSourceInstanceSettings , index : number ) = > void ;
onChangeQuery : ( query : DataQuery , index : number ) = > void ;
onChangeTimeRange ? : ( timeRange : RelativeTimeRange , index : number ) = > void ;
onRemoveQuery : ( query : DataQuery ) = > void ;
2021-05-27 12:29:10 +02:00
onDuplicateQuery : ( query : AlertQuery ) = > void ;
2021-05-18 16:16:26 +02:00
onRunQueries : ( ) = > void ;
index : number ;
2021-09-15 17:35:12 +02:00
thresholds : ThresholdsConfig ;
2022-12-22 16:28:17 +01:00
thresholdsType? : GraphTresholdsStyleMode ;
2022-12-21 16:18:35 +01:00
onChangeThreshold ? : ( thresholds : ThresholdsConfig , index : number ) = > void ;
2022-10-05 14:35:15 +02:00
condition : string | null ;
onSetCondition : ( refId : string ) = > void ;
2023-01-24 09:16:21 +01:00
onChangeQueryOptions : ( options : AlertQueryOptions , index : number ) = > void ;
2021-05-18 16:16:26 +02:00
}
2023-03-14 07:38:21 -07:00
export const QueryWrapper = ( {
2021-05-18 16:16:26 +02:00
data ,
2022-10-05 14:35:15 +02:00
error ,
2021-05-18 16:16:26 +02:00
dsSettings ,
index ,
onChangeDataSource ,
onChangeQuery ,
onChangeTimeRange ,
onRunQueries ,
onRemoveQuery ,
onDuplicateQuery ,
query ,
queries ,
2021-09-15 17:35:12 +02:00
thresholds ,
2022-12-22 16:28:17 +01:00
thresholdsType ,
2021-09-15 17:35:12 +02:00
onChangeThreshold ,
2022-10-05 14:35:15 +02:00
condition ,
onSetCondition ,
2023-01-24 09:16:21 +01:00
onChangeQueryOptions ,
2023-03-14 07:38:21 -07:00
} : Props ) = > {
2021-05-18 16:16:26 +02:00
const styles = useStyles2 ( getStyles ) ;
2023-04-27 16:38:22 +02:00
const [ dsInstance , setDsInstance ] = useState < DataSourceApi > ( ) ;
const defaults = dsInstance ? . getDefaultQuery ? dsInstance . getDefaultQuery ( CoreApp . UnifiedAlerting ) : { } ;
2021-05-18 16:16:26 +02:00
2023-06-16 13:14:46 +02:00
const queryWithDefaults = {
. . . defaults ,
. . . cloneDeep ( query . model ) ,
} ;
2022-08-12 15:32:07 +02:00
function SelectingDataSourceTooltip() {
const styles = useStyles2 ( getStyles ) ;
2021-05-18 16:16:26 +02:00
return (
2022-08-12 15:32:07 +02:00
< div className = { styles . dsTooltip } >
< Tooltip
content = {
< >
Not finding the data source you want ? Some data sources are not supported for alerting . Click on the icon
for more information .
< / >
}
>
< Icon
name = "info-circle"
onClick = { ( ) = >
window . open (
' https://grafana.com/docs/grafana/latest/alerting/fundamentals/data-source-alerting/' ,
'_blank'
)
}
/ >
< / Tooltip >
< / div >
2021-05-18 16:16:26 +02:00
) ;
2022-08-12 15:32:07 +02:00
}
2022-10-05 14:35:15 +02:00
// TODO add a warning label here too when the data looks like time series data and is used as an alert condition
function HeaderExtras ( { query , error , index } : { query : AlertQuery ; error? : Error ; index : number } ) {
2023-07-24 11:20:55 -04:00
const queryOptions : AlertQueryOptions = {
maxDataPoints : query.model.maxDataPoints ,
minInterval : query.model.intervalMs ? msToSingleUnitDuration ( query . model . intervalMs ) : undefined ,
} ;
2023-01-24 09:16:21 +01:00
const alertQueryOptions : AlertQueryOptions = {
maxDataPoints : queryOptions.maxDataPoints ,
2023-07-24 11:20:55 -04:00
minInterval : queryOptions.minInterval ,
2023-01-24 09:16:21 +01:00
} ;
2023-09-13 13:58:16 -04:00
const isAlertCondition = condition === query . refId ;
2023-04-27 16:38:22 +02:00
return (
2023-09-14 15:05:28 +02:00
< Stack direction = "row" alignItems = "center" gap = { 1 } >
2023-04-27 16:38:22 +02:00
< SelectingDataSourceTooltip / >
2023-07-04 11:17:14 -03:00
< QueryOptions
onChangeTimeRange = { onChangeTimeRange }
query = { query }
queryOptions = { alertQueryOptions }
onChangeQueryOptions = { onChangeQueryOptions }
index = { index }
/ >
2023-09-14 15:05:28 +02:00
< ExpressionStatusIndicator
2023-04-27 16:38:22 +02:00
error = { error }
2023-09-14 15:05:28 +02:00
onSetCondition = { ( ) = > onSetCondition ( query . refId ) }
isCondition = { isAlertCondition }
2023-04-27 16:38:22 +02:00
/ >
< / Stack >
) ;
2022-08-12 15:32:07 +02:00
}
2021-05-18 16:16:26 +02:00
2023-06-16 13:14:46 +02:00
const showVizualisation = data . state !== LoadingState . NotStarted ;
2021-05-18 16:16:26 +02:00
return (
2023-04-27 16:38:22 +02:00
< Stack direction = "column" gap = { 0.5 } >
< div className = { styles . wrapper } >
< QueryEditorRow < DataQuery >
alerting
2023-06-16 15:14:00 -03:00
collapsable = { false }
2023-04-27 16:38:22 +02:00
dataSource = { dsSettings }
onDataSourceLoaded = { setDsInstance }
onChangeDataSource = { ( settings ) = > onChangeDataSource ( settings , index ) }
id = { query . refId }
index = { index }
key = { query . refId }
data = { data }
2023-06-16 13:14:46 +02:00
query = { queryWithDefaults }
2023-04-27 16:38:22 +02:00
onChange = { ( query ) = > onChangeQuery ( query , index ) }
onRemoveQuery = { onRemoveQuery }
onAddQuery = { ( ) = > onDuplicateQuery ( cloneDeep ( query ) ) }
onRunQuery = { onRunQueries }
queries = { queries }
renderHeaderExtras = { ( ) = > < HeaderExtras query = { query } index = { index } error = { error } / > }
app = { CoreApp . UnifiedAlerting }
hideDisableQuery = { true }
/ >
< / div >
2023-06-16 13:14:46 +02:00
{ showVizualisation && (
2023-04-27 16:38:22 +02:00
< VizWrapper
data = { data }
thresholds = { thresholds }
thresholdsType = { thresholdsType }
onThresholdsChange = { onChangeThreshold ? ( thresholds ) = > onChangeThreshold ( thresholds , index ) : undefined }
/ >
) }
< / Stack >
2021-05-18 16:16:26 +02:00
) ;
} ;
2022-10-14 10:24:32 +01:00
export const EmptyQueryWrapper = ( { children } : React . PropsWithChildren < { } > ) = > {
2022-01-17 14:30:00 +01:00
const styles = useStyles2 ( getStyles ) ;
return < div className = { styles . wrapper } > { children } < / div > ;
} ;
2023-07-04 11:17:14 -03:00
export function MaxDataPointsOption ( {
2023-01-24 09:16:21 +01:00
options ,
onChange ,
} : {
options : AlertQueryOptions ;
onChange : ( options : AlertQueryOptions ) = > void ;
} ) {
const value = options . maxDataPoints ? ? '' ;
const onMaxDataPointsBlur = ( event : ChangeEvent < HTMLInputElement > ) = > {
const maxDataPointsNumber = parseInt ( event . target . value , 10 ) ;
const maxDataPoints = isNaN ( maxDataPointsNumber ) || maxDataPointsNumber === 0 ? undefined : maxDataPointsNumber ;
if ( maxDataPoints !== options . maxDataPoints ) {
onChange ( {
. . . options ,
maxDataPoints ,
} ) ;
}
} ;
return (
2023-09-11 14:45:46 +02:00
< InlineField
labelWidth = { 24 }
label = "Max data points"
tooltip = "The maximum data points per series. Used directly by some data sources and used in calculation of auto interval. With streaming data this value is used for the rolling buffer."
>
2023-01-24 09:16:21 +01:00
< Input
type = "number"
2023-09-11 14:45:46 +02:00
width = { 10 }
2023-01-24 09:16:21 +01:00
placeholder = { DEFAULT_MAX_DATA_POINTS . toLocaleString ( ) }
spellCheck = { false }
onBlur = { onMaxDataPointsBlur }
defaultValue = { value }
/ >
2023-09-11 14:45:46 +02:00
< / InlineField >
2023-01-24 09:16:21 +01:00
) ;
}
2023-07-24 11:20:55 -04:00
export function MinIntervalOption ( {
options ,
onChange ,
} : {
options : AlertQueryOptions ;
onChange : ( options : AlertQueryOptions ) = > void ;
} ) {
const value = options . minInterval ? ? '' ;
const onMinIntervalBlur = ( event : ChangeEvent < HTMLInputElement > ) = > {
const minInterval = event . target . value ;
if ( minInterval !== value ) {
onChange ( {
. . . options ,
minInterval ,
} ) ;
}
} ;
return (
2023-09-11 14:45:46 +02:00
< InlineField
label = "Min interval"
labelWidth = { 24 }
tooltip = {
< >
A lower limit for the interval . Recommended to be set to write frequency , for example < code > 1 m < / code > if your
data is written every minute .
< / >
}
>
2023-07-24 11:20:55 -04:00
< Input
type = "text"
2023-09-11 14:45:46 +02:00
width = { 10 }
2023-07-24 11:20:55 -04:00
placeholder = { DEFAULT_MIN_INTERVAL }
spellCheck = { false }
onBlur = { onMinIntervalBlur }
defaultValue = { value }
/ >
2023-09-11 14:45:46 +02:00
< / InlineField >
2023-07-24 11:20:55 -04:00
) ;
}
2021-05-18 16:16:26 +02:00
const getStyles = ( theme : GrafanaTheme2 ) = > ( {
wrapper : css `
2021-05-27 10:46:06 +02:00
label : AlertingQueryWrapper ;
2021-05-18 16:16:26 +02:00
margin - bottom : $ { theme . spacing ( 1 ) } ;
2023-06-16 15:14:00 -03:00
border : 1px solid $ { theme . colors . border . weak } ;
2023-08-01 14:46:07 +01:00
border - radius : $ { theme . shape . radius . default } ;
2023-07-04 11:17:14 -03:00
button {
overflow : visible ;
}
2021-05-18 16:16:26 +02:00
` ,
2022-08-12 15:32:07 +02:00
dsTooltip : css `
display : flex ;
align - items : center ;
& : hover {
opacity : 0.85 ;
cursor : pointer ;
}
` ,
2021-05-18 16:16:26 +02:00
} ) ;