diff --git a/.betterer.results b/.betterer.results index eb828ff66ac..f9f719bfc49 100644 --- a/.betterer.results +++ b/.betterer.results @@ -7416,8 +7416,7 @@ exports[`better eslint`] = { ], "public/app/plugins/datasource/prometheus/querybuilder/state.test.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], - [0, 0, 0, "Unexpected any. Specify a different type.", "2"] + [0, 0, 0, "Unexpected any. Specify a different type.", "1"] ], "public/app/plugins/datasource/prometheus/querybuilder/state.ts:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] diff --git a/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap b/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap new file mode 100644 index 00000000000..91d24e856b6 --- /dev/null +++ b/public/app/features/alerting/unified/utils/__snapshots__/rule-form.test.ts.snap @@ -0,0 +1,59 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`formValuesToRulerGrafanaRuleDTO should correctly convert rule form values 1`] = ` +Object { + "annotations": Object { + "description": "", + "runbook_url": "", + "summary": "", + }, + "for": "5m", + "grafana_alert": Object { + "condition": "A", + "data": Array [], + "exec_err_state": "Error", + "no_data_state": "NoData", + "title": "", + }, + "labels": Object { + "": "", + }, +} +`; + +exports[`formValuesToRulerGrafanaRuleDTO should not save both instant and range type queries 1`] = ` +Object { + "annotations": Object { + "description": "", + "runbook_url": "", + "summary": "", + }, + "for": "5m", + "grafana_alert": Object { + "condition": "A", + "data": Array [ + Object { + "datasourceUid": "dsuid", + "model": Object { + "expr": "", + "instant": false, + "range": true, + "refId": "A", + }, + "queryType": "query", + "refId": "A", + "relativeTimeRange": Object { + "from": 900, + "to": 1000, + }, + }, + ], + "exec_err_state": "Error", + "no_data_state": "NoData", + "title": "", + }, + "labels": Object { + "": "", + }, +} +`; diff --git a/public/app/features/alerting/unified/utils/rule-form.test.ts b/public/app/features/alerting/unified/utils/rule-form.test.ts new file mode 100644 index 00000000000..adeae48e409 --- /dev/null +++ b/public/app/features/alerting/unified/utils/rule-form.test.ts @@ -0,0 +1,36 @@ +import { PromQuery } from 'app/plugins/datasource/prometheus/types'; + +import { RuleFormValues } from '../types/rule-form'; + +import { formValuesToRulerGrafanaRuleDTO, getDefaultFormValues } from './rule-form'; + +describe('formValuesToRulerGrafanaRuleDTO', () => { + it('should correctly convert rule form values', () => { + const formValues: RuleFormValues = { + ...getDefaultFormValues(), + condition: 'A', + }; + + expect(formValuesToRulerGrafanaRuleDTO(formValues)).toMatchSnapshot(); + }); + + it('should not save both instant and range type queries', () => { + const defaultValues = getDefaultFormValues(); + + const values: RuleFormValues = { + ...defaultValues, + queries: [ + { + refId: 'A', + relativeTimeRange: { from: 900, to: 1000 }, + datasourceUid: 'dsuid', + model: { refId: 'A', expr: '', instant: true, range: true } as PromQuery, + queryType: 'query', + }, + ], + condition: 'A', + }; + + expect(formValuesToRulerGrafanaRuleDTO(values)).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/alerting/unified/utils/rule-form.ts b/public/app/features/alerting/unified/utils/rule-form.ts index cdd336af723..ca53a5299d8 100644 --- a/public/app/features/alerting/unified/utils/rule-form.ts +++ b/public/app/features/alerting/unified/utils/rule-form.ts @@ -14,8 +14,10 @@ import { getNextRefIdChar } from 'app/core/utils/query'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { ExpressionDatasourceUID } from 'app/features/expressions/ExpressionDatasource'; import { ExpressionQuery, ExpressionQueryType } from 'app/features/expressions/types'; +import { PromQuery } from 'app/plugins/datasource/prometheus/types'; import { RuleWithLocation } from 'app/types/unified-alerting'; import { + AlertDataQuery, AlertQuery, Annotations, GrafanaAlertStateDecision, @@ -101,7 +103,7 @@ export function formValuesToRulerGrafanaRuleDTO(values: RuleFormValues): Postabl condition, no_data_state: noDataState, exec_err_state: execErrState, - data: queries, + data: queries.map(fixBothInstantAndRangeQuery), }, for: evaluateFor, annotations: arrayToRecord(values.annotations || []), @@ -382,3 +384,22 @@ export function getIntervals(range: TimeRange, lowLimit?: string, resolution?: n return rangeUtil.calculateInterval(range, resolution, lowLimit); } + +export function fixBothInstantAndRangeQuery(query: AlertQuery) { + const model = query.model; + + if (!isPromQuery(model)) { + return query; + } + + const isBothInstantAndRange = model.instant && model.range; + if (isBothInstantAndRange) { + return { ...query, model: { ...model, range: true, instant: false } }; + } + + return query; +} + +function isPromQuery(model: AlertDataQuery): model is PromQuery { + return 'expr' in model && 'instant' in model && 'range' in model; +} diff --git a/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts b/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts index b0caa8c9f01..e8bdc3762e9 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/state.test.ts @@ -1,11 +1,13 @@ import { CoreApp } from '@grafana/data'; +import { PromQuery } from '../types'; + import { QueryEditorMode } from './shared/types'; import { changeEditorMode, getQueryWithDefaults } from './state'; describe('getQueryWithDefaults(', () => { it('should set defaults', () => { - expect(getQueryWithDefaults({ refId: 'A' } as any, CoreApp.Dashboard)).toEqual({ + expect(getQueryWithDefaults({ refId: 'A' } as PromQuery, CoreApp.Dashboard)).toEqual({ editorMode: 'builder', expr: '', legendFormat: '__auto', @@ -25,6 +27,19 @@ describe('getQueryWithDefaults(', () => { }); }); + it('should not set both instant and range for Prometheus queries in Alert Creation', () => { + expect( + getQueryWithDefaults({ refId: 'A', range: true, instant: true } as PromQuery, CoreApp.UnifiedAlerting) + ).toEqual({ + editorMode: 'builder', + expr: '', + legendFormat: '__auto', + range: true, + instant: false, + refId: 'A', + }); + }); + it('changing editor mode with blank query should change default', () => { changeEditorMode({ refId: 'A', expr: '' }, QueryEditorMode.Code, (query) => { expect(query.editorMode).toBe(QueryEditorMode.Code); diff --git a/public/app/plugins/datasource/prometheus/querybuilder/state.ts b/public/app/plugins/datasource/prometheus/querybuilder/state.ts index 17d2d8e5931..0215bec27ea 100644 --- a/public/app/plugins/datasource/prometheus/querybuilder/state.ts +++ b/public/app/plugins/datasource/prometheus/querybuilder/state.ts @@ -56,5 +56,11 @@ export function getQueryWithDefaults(query: PromQuery, app: CoreApp | undefined) } } + // Unified Alerting does not support "both" for query type – fall back to "range". + const isBothInstantAndRange = query.instant && query.range; + if (app === CoreApp.UnifiedAlerting && isBothInstantAndRange) { + result = { ...result, instant: false, range: true }; + } + return result; }