From e46ee78beda9d6836d6e3ce7c401ac5cf62b9762 Mon Sep 17 00:00:00 2001 From: Konrad Lalik Date: Tue, 12 Jul 2022 11:50:58 +0200 Subject: [PATCH] Alerting: Add default input parameters for expressions (#51810) --- .../unified/PanelAlertTabContent.test.tsx | 1 + .../components/rule-editor/QueryEditor.tsx | 7 +- .../alerting/unified/utils/rule-form.ts | 1 + .../expressions/ExpressionDatasource.ts | 4 +- .../expressions/ExpressionQueryEditor.tsx | 85 ++++++++++++++----- .../components/ClassicConditions.tsx | 5 +- .../expressions/utils/expressionTypes.ts | 7 +- public/app/types/unified-alerting-dto.ts | 1 + 8 files changed, 85 insertions(+), 26 deletions(-) diff --git a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx index af80c1289a6..a02dc3f4f0f 100644 --- a/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx +++ b/public/app/features/alerting/unified/PanelAlertTabContent.test.tsx @@ -328,6 +328,7 @@ describe('PanelAlertTabContent', () => { model: { refId: 'B', hide: false, + expression: 'A', type: 'classic_conditions', datasource: { type: ExpressionDatasourceRef.type, diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryEditor.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryEditor.tsx index ee4979f7445..71a38b2ac18 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryEditor.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryEditor.tsx @@ -35,6 +35,7 @@ interface Props { interface State { panelDataByRefId: Record; } + export class QueryEditor extends PureComponent { private runner: AlertingQueryRunner; private queries: AlertQuery[]; @@ -100,12 +101,16 @@ export class QueryEditor extends PureComponent { onNewExpressionQuery = () => { const { queries } = this; + const lastQuery = queries.at(-1); + const defaultParams = lastQuery ? [lastQuery.refId] : []; + this.onChangeQueries( addQuery(queries, { datasourceUid: ExpressionDatasourceUID, model: expressionDatasource.newQuery({ type: ExpressionQueryType.classic, - conditions: [defaultCondition], + conditions: [{ ...defaultCondition, query: { params: defaultParams } }], + expression: lastQuery?.refId, }), }) ); diff --git a/public/app/features/alerting/unified/utils/rule-form.ts b/public/app/features/alerting/unified/utils/rule-form.ts index 7f7a1aeb77c..bd03002c23c 100644 --- a/public/app/features/alerting/unified/utils/rule-form.ts +++ b/public/app/features/alerting/unified/utils/rule-form.ts @@ -220,6 +220,7 @@ const getDefaultExpression = (refId: string): AlertQuery => { }, }, ], + expression: 'A', }; return { diff --git a/public/app/features/expressions/ExpressionDatasource.ts b/public/app/features/expressions/ExpressionDatasource.ts index 5376e396377..7c8de0874d9 100644 --- a/public/app/features/expressions/ExpressionDatasource.ts +++ b/public/app/features/expressions/ExpressionDatasource.ts @@ -53,9 +53,9 @@ export class ExpressionDatasourceApi extends DataSourceWithBackend): ExpressionQuery { return { refId: '--', // Replaced with query - type: query?.type ?? ExpressionQueryType.math, datasource: ExpressionDatasourceRef, - conditions: query?.conditions ?? undefined, + type: query?.type ?? ExpressionQueryType.math, + ...query, }; } } diff --git a/public/app/features/expressions/ExpressionQueryEditor.tsx b/public/app/features/expressions/ExpressionQueryEditor.tsx index 618bdf93787..66c21d60ccd 100644 --- a/public/app/features/expressions/ExpressionQueryEditor.tsx +++ b/public/app/features/expressions/ExpressionQueryEditor.tsx @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { useCallback, useEffect, useRef } from 'react'; import { DataSourceApi, QueryEditorProps, SelectableValue } from '@grafana/data'; import { InlineField, Select } from '@grafana/ui'; @@ -13,15 +13,61 @@ import { getDefaults } from './utils/expressionTypes'; type Props = QueryEditorProps, ExpressionQuery>; const labelWidth = 14; -export class ExpressionQueryEditor extends PureComponent { - onSelectExpressionType = (item: SelectableValue) => { - const { query, onChange } = this.props; - onChange(getDefaults({ ...query, type: item.value! })); - }; +type NonClassicExpressionType = Exclude; +type ExpressionTypeConfigStorage = Partial>; - renderExpressionType() { - const { onChange, onRunQuery, query, queries } = this.props; +function useExpressionsCache() { + const expressionCache = useRef({}); + + const getCachedExpression = useCallback((queryType: ExpressionQueryType) => { + switch (queryType) { + case ExpressionQueryType.math: + case ExpressionQueryType.reduce: + case ExpressionQueryType.resample: + return expressionCache.current[queryType]; + case ExpressionQueryType.classic: + return undefined; + } + }, []); + + const setCachedExpression = useCallback((queryType: ExpressionQueryType, value: string | undefined) => { + switch (queryType) { + case ExpressionQueryType.math: + expressionCache.current.math = value; + break; + + // We want to use the same value for Reduce and Resample + case ExpressionQueryType.reduce: + case ExpressionQueryType.resample: + expressionCache.current.reduce = value; + expressionCache.current.resample = value; + break; + } + }, []); + + return { getCachedExpression, setCachedExpression }; +} + +export function ExpressionQueryEditor(props: Props) { + const { query, queries, onRunQuery, onChange } = props; + const { getCachedExpression, setCachedExpression } = useExpressionsCache(); + + useEffect(() => { + setCachedExpression(query.type, query.expression); + }, [query.expression, query.type, setCachedExpression]); + + const onSelectExpressionType = useCallback( + (item: SelectableValue) => { + const cachedExpression = getCachedExpression(item.value!); + const defaults = getDefaults({ ...query, type: item.value! }); + + onChange({ ...defaults, expression: cachedExpression ?? defaults.expression }); + }, + [query, onChange, getCachedExpression] + ); + + const renderExpressionType = () => { const refIds = queries!.filter((q) => query.refId !== q.refId).map((q) => ({ value: q.refId, label: q.refId })); switch (query.type) { @@ -37,19 +83,16 @@ export class ExpressionQueryEditor extends PureComponent { case ExpressionQueryType.classic: return ; } - } + }; - render() { - const { query } = this.props; - const selected = gelTypes.find((o) => o.value === query.type); + const selected = gelTypes.find((o) => o.value === query.type); - return ( -
- - + + {renderExpressionType()} +
+ ); } diff --git a/public/app/features/expressions/components/ClassicConditions.tsx b/public/app/features/expressions/components/ClassicConditions.tsx index 6496702d88f..3b7c8805ef4 100644 --- a/public/app/features/expressions/components/ClassicConditions.tsx +++ b/public/app/features/expressions/components/ClassicConditions.tsx @@ -26,9 +26,12 @@ export const ClassicConditions: FC = ({ onChange, query, refIds }) => { const onAddCondition = () => { if (query.conditions) { + const lastParams = query.conditions.at(-1)?.query?.params ?? []; + const newCondition: ClassicCondition = { ...defaultCondition, query: { params: lastParams } }; + onChange({ ...query, - conditions: query.conditions.length > 0 ? [...query.conditions, defaultCondition] : [defaultCondition], + conditions: query.conditions.length > 0 ? [...query.conditions, newCondition] : [newCondition], }); } }; diff --git a/public/app/features/expressions/utils/expressionTypes.ts b/public/app/features/expressions/utils/expressionTypes.ts index d3e3665963b..27c8b2c5941 100644 --- a/public/app/features/expressions/utils/expressionTypes.ts +++ b/public/app/features/expressions/utils/expressionTypes.ts @@ -9,7 +9,7 @@ export const getDefaults = (query: ExpressionQuery) => { if (!query.reducer) { query.reducer = ReducerID.mean; } - query.expression = undefined; + break; case ExpressionQueryType.resample: @@ -24,10 +24,15 @@ export const getDefaults = (query: ExpressionQuery) => { query.reducer = undefined; break; + case ExpressionQueryType.math: + query.expression = undefined; + break; + case ExpressionQueryType.classic: if (!query.conditions) { query.conditions = [defaultCondition]; } + break; default: diff --git a/public/app/types/unified-alerting-dto.ts b/public/app/types/unified-alerting-dto.ts index 617a10da100..8a447a9c1bb 100644 --- a/public/app/types/unified-alerting-dto.ts +++ b/public/app/types/unified-alerting-dto.ts @@ -159,6 +159,7 @@ export enum GrafanaAlertStateDecision { export interface AlertDataQuery extends DataQuery { maxDataPoints?: number; intervalMs?: number; + expression?: string; } export interface AlertQuery {