2019-11-14 10:59:41 +01:00
import React , { PureComponent , ChangeEvent } from 'react' ;
2020-04-25 21:48:20 +01:00
2021-09-15 16:26:23 +01:00
import { QueryEditorProps , PanelData } from '@grafana/data' ;
2020-04-12 22:20:02 +02:00
import { LegacyForms , ValidationEvents , EventsWithValidation , Icon } from '@grafana/ui' ;
2020-04-07 08:50:54 +02:00
const { Input , Switch } = LegacyForms ;
2021-09-08 16:06:43 +02:00
import { CloudWatchQuery , CloudWatchMetricsQuery , CloudWatchJsonData , ExecutedQueryPreview } from '../types' ;
2020-04-25 21:48:20 +01:00
import { CloudWatchDatasource } from '../datasource' ;
import { QueryField , Alias , MetricsQueryFieldsEditor } from './' ;
2019-11-14 10:59:41 +01:00
2021-09-15 16:26:23 +01:00
export type Props = QueryEditorProps < CloudWatchDatasource , CloudWatchQuery , CloudWatchJsonData > ;
2019-11-14 10:59:41 +01:00
interface State {
showMeta : boolean ;
}
const idValidationEvents : ValidationEvents = {
[ EventsWithValidation . onBlur ] : [
{
2021-01-20 07:59:48 +01:00
rule : ( value ) = > new RegExp ( /^$|^[a-z][a-zA-Z0-9_]*$/ ) . test ( value ) ,
2019-11-14 10:59:41 +01:00
errorMessage : 'Invalid format. Only alphanumeric characters and underscores are allowed' ,
} ,
] ,
} ;
2020-03-20 14:00:49 +02:00
export const normalizeQuery = ( {
namespace ,
metricName ,
expression ,
dimensions ,
region ,
id ,
alias ,
2021-09-08 16:06:43 +02:00
statistic ,
2020-03-20 14:00:49 +02:00
period ,
. . . rest
2020-04-25 21:48:20 +01:00
} : CloudWatchMetricsQuery ) : CloudWatchMetricsQuery = > {
2020-03-20 14:00:49 +02:00
const normalizedQuery = {
namespace : namespace || '' ,
metricName : metricName || '' ,
expression : expression || '' ,
dimensions : dimensions || { } ,
region : region || 'default' ,
id : id || '' ,
alias : alias || '' ,
2021-09-08 16:06:43 +02:00
statistic : statistic ? ? 'Average' ,
2020-03-20 14:00:49 +02:00
period : period || '' ,
. . . rest ,
} ;
return ! rest . hasOwnProperty ( 'matchExact' ) ? { . . . normalizedQuery , matchExact : true } : normalizedQuery ;
} ;
2020-04-25 21:48:20 +01:00
export class MetricsQueryEditor extends PureComponent < Props , State > {
2020-01-15 16:38:15 +01:00
state : State = { showMeta : false } ;
2019-11-14 10:59:41 +01:00
2020-05-07 13:22:45 +02:00
componentDidMount ( ) : void {
const metricsQuery = this . props . query as CloudWatchMetricsQuery ;
const query = normalizeQuery ( metricsQuery ) ;
this . props . onChange ( query ) ;
}
2020-04-25 21:48:20 +01:00
onChange ( query : CloudWatchMetricsQuery ) {
2019-11-14 10:59:41 +01:00
const { onChange , onRunQuery } = this . props ;
onChange ( query ) ;
onRunQuery ( ) ;
}
2021-09-08 16:06:43 +02:00
getExecutedQueryPreview ( data? : PanelData ) : ExecutedQueryPreview {
if ( ! ( data ? . series . length && data ? . series [ 0 ] . meta ? . custom ) ) {
return {
executedQuery : '' ,
period : '' ,
id : '' ,
} ;
}
return {
executedQuery : data?.series [ 0 ] . meta . executedQueryString ? ? '' ,
period : data.series [ 0 ] . meta . custom [ 'period' ] ,
id : data.series [ 0 ] . meta . custom [ 'id' ] ,
} ;
}
2019-11-14 10:59:41 +01:00
render() {
2020-03-20 14:00:49 +02:00
const { data , onRunQuery } = this . props ;
2020-04-25 21:48:20 +01:00
const metricsQuery = this . props . query as CloudWatchMetricsQuery ;
2020-01-15 16:38:15 +01:00
const { showMeta } = this . state ;
2020-04-25 21:48:20 +01:00
const query = normalizeQuery ( metricsQuery ) ;
2021-09-08 16:06:43 +02:00
const executedQueryPreview = this . getExecutedQueryPreview ( data ) ;
2019-11-14 10:59:41 +01:00
return (
< >
2020-04-25 21:48:20 +01:00
< MetricsQueryFieldsEditor { ... { ...this.props , query }} > < / MetricsQueryFieldsEditor >
2021-09-08 16:06:43 +02:00
< div className = "gf-form-inline" >
< div className = "gf-form" >
< QueryField
label = "Id"
tooltip = "Id can include numbers, letters, and underscore, and must start with a lowercase letter."
>
< Input
className = "gf-form-input width-8"
onBlur = { onRunQuery }
onChange = { ( event : ChangeEvent < HTMLInputElement > ) = >
this . onChange ( { . . . metricsQuery , id : event.target.value } )
}
validationEvents = { idValidationEvents }
value = { query . id }
/ >
< / QueryField >
2019-11-14 10:59:41 +01:00
< / div >
2021-09-08 16:06:43 +02:00
< div className = "gf-form gf-form--grow" >
< QueryField
className = "gf-form--grow"
label = "Expression"
tooltip = "Optionally you can add an expression here. Please note that if a math expression that is referencing other queries is being used, it will not be possible to create an alert rule based on this query"
>
< Input
className = "gf-form-input"
onBlur = { onRunQuery }
value = { query . expression || '' }
onChange = { ( event : ChangeEvent < HTMLInputElement > ) = >
this . onChange ( { . . . metricsQuery , expression : event.target.value } )
}
/ >
< / QueryField >
< / div >
< / div >
2019-11-14 10:59:41 +01:00
< div className = "gf-form-inline" >
< div className = "gf-form" >
2020-01-15 16:38:15 +01:00
< QueryField label = "Period" tooltip = "Minimum interval between points in seconds" >
2019-11-14 10:59:41 +01:00
< Input
className = "gf-form-input width-8"
2020-04-25 21:48:20 +01:00
value = { query . period || '' }
2019-11-14 10:59:41 +01:00
placeholder = "auto"
onBlur = { onRunQuery }
2020-01-15 16:38:15 +01:00
onChange = { ( event : ChangeEvent < HTMLInputElement > ) = >
2020-04-25 21:48:20 +01:00
this . onChange ( { . . . metricsQuery , period : event.target.value } )
2020-01-15 16:38:15 +01:00
}
2019-11-14 10:59:41 +01:00
/ >
< / QueryField >
< / div >
< div className = "gf-form" >
< QueryField
label = "Alias"
tooltip = "Alias replacement variables: {{metric}}, {{stat}}, {{namespace}}, {{region}}, {{period}}, {{label}}, {{YOUR_DIMENSION_NAME}}"
>
2020-04-25 21:48:20 +01:00
< Alias
value = { metricsQuery . alias }
onChange = { ( value : string ) = > this . onChange ( { . . . metricsQuery , alias : value } ) }
/ >
2019-11-14 10:59:41 +01:00
< / QueryField >
< Switch
label = "Match Exact"
labelClass = "query-keyword"
tooltip = "Only show metrics that exactly match all defined dimension names."
2020-04-25 21:48:20 +01:00
checked = { metricsQuery . matchExact }
onChange = { ( ) = >
this . onChange ( {
. . . metricsQuery ,
matchExact : ! metricsQuery . matchExact ,
} )
}
2019-11-14 10:59:41 +01:00
/ >
< label className = "gf-form-label" >
< a
onClick = { ( ) = >
2021-09-08 16:06:43 +02:00
executedQueryPreview &&
2019-11-14 10:59:41 +01:00
this . setState ( {
showMeta : ! showMeta ,
} )
}
>
2021-09-08 16:06:43 +02:00
< Icon name = { showMeta ? 'angle-down' : 'angle-right' } / > { showMeta ? 'Hide' : 'Show' } Query Preview
2019-11-14 10:59:41 +01:00
< / a >
< / label >
< / div >
< div className = "gf-form gf-form--grow" >
< div className = "gf-form-label gf-form-label--grow" / >
< / div >
2021-09-08 16:06:43 +02:00
{ showMeta && (
2019-11-14 10:59:41 +01:00
< table className = "filter-table form-inline" >
< thead >
< tr >
< th > Metric Data Query ID < / th >
< th > Metric Data Query Expression < / th >
2020-01-17 13:22:43 +01:00
< th > Period < / th >
2019-11-14 10:59:41 +01:00
< th / >
< / tr >
< / thead >
< tbody >
2021-09-08 16:06:43 +02:00
< tr >
< td > { executedQueryPreview . id } < / td >
< td > { executedQueryPreview . executedQuery } < / td >
< td > { executedQueryPreview . period } < / td >
< / tr >
2019-11-14 10:59:41 +01:00
< / tbody >
< / table >
) }
< / div >
< / >
) ;
}
}