diff --git a/.betterer.results b/.betterer.results index a92ec3d7c3a..e3ff6cb8ab5 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1,5 +1,5 @@ // BETTERER RESULTS V2. -// +// // If this file contains merge conflicts, use `betterer merge` to automatically resolve them: // https://phenomnomnominal.github.io/betterer/docs/results-file/#merge // @@ -2685,6 +2685,9 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/components/rule-editor/RuleInspector.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], + "public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx:5381": [ + [0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"] + ], "public/app/features/alerting/unified/components/rules/RuleDetailsDataSources.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"] ], diff --git a/packages/grafana-runtime/src/services/__mocks__/dataSourceSrv.ts b/packages/grafana-runtime/src/services/__mocks__/dataSourceSrv.ts index 13b565b9ef2..61995de2b0a 100644 --- a/packages/grafana-runtime/src/services/__mocks__/dataSourceSrv.ts +++ b/packages/grafana-runtime/src/services/__mocks__/dataSourceSrv.ts @@ -19,12 +19,5 @@ export function getDataSourceSrv() { return { getList: () => [ds1], getInstanceSettings: () => ds1, - get: () => - Promise.resolve({ - filterQuery: () => true, - getDefaultQuery: () => ({ - expr: 'vector(1)', - }), - }), }; } diff --git a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx index 3d900bbd148..01c66b8cf27 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudOnlyAllowed.test.tsx @@ -2,7 +2,6 @@ import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; -import { MockDataSourceApi } from 'test/mocks/datasource_srv'; import { byRole, byText } from 'testing-library-selector'; import { setDataSourceSrv } from '@grafana/runtime'; @@ -160,10 +159,7 @@ describe('RuleEditor cloud: checking editable data sources', () => { return null; }); - const dsServer = new MockDataSourceSrv(dataSources); - jest.spyOn(dsServer, 'get').mockResolvedValue(new MockDataSourceApi('ds')); - - setDataSourceSrv(dsServer); + setDataSourceSrv(new MockDataSourceSrv(dataSources)); mocks.getAllDataSources.mockReturnValue(Object.values(dataSources)); mocks.searchFolders.mockResolvedValue([]); diff --git a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx index 35128cbbc8f..b1429b3a32d 100644 --- a/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorCloudRules.test.tsx @@ -1,9 +1,8 @@ -import { screen, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react'; +import { waitFor, screen, within, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import React from 'react'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; -import { MockDataSourceApi } from 'test/mocks/datasource_srv'; import { byRole } from 'testing-library-selector'; import { setDataSourceSrv } from '@grafana/runtime'; @@ -80,10 +79,8 @@ describe('RuleEditor cloud', () => { { alerting: true } ), }; - const dsServer = new MockDataSourceSrv(dataSources); - jest.spyOn(dsServer, 'get').mockResolvedValue(new MockDataSourceApi('ds')); - setDataSourceSrv(dsServer); + setDataSourceSrv(new MockDataSourceSrv(dataSources)); mocks.getAllDataSources.mockReturnValue(Object.values(dataSources)); mocks.api.setRulerRuleGroup.mockResolvedValue(); mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]); diff --git a/public/app/features/alerting/unified/RuleEditorExisting.test.tsx b/public/app/features/alerting/unified/RuleEditorExisting.test.tsx index c9677548952..ebb6b894b44 100644 --- a/public/app/features/alerting/unified/RuleEditorExisting.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorExisting.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, waitFor, within } from '@testing-library/react'; +import { render, waitFor, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { Route } from 'react-router-dom'; @@ -25,7 +25,7 @@ import { disableRBAC, mockDataSource, MockDataSourceSrv, mockFolder } from './mo import { fetchRulerRulesIfNotFetchedYet } from './state/actions'; import * as config from './utils/config'; import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; -import * as ruleForm from './utils/rule-form'; +import { getDefaultQueries } from './utils/rule-form'; jest.mock('./components/rule-editor/ExpressionEditor', () => ({ // eslint-disable-next-line react/display-name @@ -46,14 +46,12 @@ jest.mock('app/features/query/components/QueryEditorRow', () => ({ })); jest.spyOn(config, 'getAllDataSources'); -jest.spyOn(ruleForm, 'getDefaultQueriesAsync'); jest.setTimeout(60 * 1000); const mocks = { getAllDataSources: jest.mocked(config.getAllDataSources), searchFolders: jest.mocked(searchFolders), - getDefaultQueriesAsync: jest.mocked(ruleForm.getDefaultQueriesAsync), api: { discoverFeatures: jest.mocked(discoverFeatures), fetchRulerRulesGroup: jest.mocked(fetchRulerRulesGroup), @@ -121,17 +119,6 @@ describe('RuleEditor grafana managed rules', () => { mocks.getAllDataSources.mockReturnValue(Object.values(dataSources)); mocks.api.setRulerRuleGroup.mockResolvedValue(); mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]); - mocks.getDefaultQueriesAsync.mockResolvedValue({ - queries: [ - { - refId: 'A', - relativeTimeRange: { from: 900, to: 1000 }, - datasourceUid: 'dsuid', - model: { refId: 'A' }, - queryType: 'query', - }, - ], - }); mocks.api.fetchRulerRules.mockResolvedValue({ [folder.title]: [ { @@ -146,16 +133,8 @@ describe('RuleEditor grafana managed rules', () => { uid, namespace_uid: 'abcd', namespace_id: 1, - condition: 'A', - data: [ - { - refId: 'A', - relativeTimeRange: { from: 900, to: 1000 }, - datasourceUid: 'dsuid', - model: { refId: 'A' }, - queryType: 'query', - }, - ], + condition: 'B', + data: getDefaultQueries(), exec_err_state: GrafanaAlertStateDecision.Error, no_data_state: GrafanaAlertStateDecision.NoData, title: 'my great new rule', @@ -223,16 +202,8 @@ describe('RuleEditor grafana managed rules', () => { for: '5m', grafana_alert: { uid, - condition: 'A', - data: [ - { - refId: 'A', - relativeTimeRange: { from: 900, to: 1000 }, - datasourceUid: 'dsuid', - model: { refId: 'A' }, - queryType: 'query', - }, - ], + condition: 'B', + data: getDefaultQueries(), exec_err_state: GrafanaAlertStateDecision.Error, is_paused: false, no_data_state: 'NoData', diff --git a/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx b/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx index f810ee9497a..99e943fb74f 100644 --- a/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorGrafanaRules.test.tsx @@ -1,9 +1,8 @@ -import { screen, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react'; +import { waitFor, screen, within, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import React from 'react'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; -import { MockDataSourceApi } from 'test/mocks/datasource_srv'; import { byRole } from 'testing-library-selector'; import { setDataSourceSrv } from '@grafana/runtime'; @@ -20,6 +19,7 @@ import { disableRBAC, mockDataSource, MockDataSourceSrv } from './mocks'; import { fetchRulerRulesIfNotFetchedYet } from './state/actions'; import * as config from './utils/config'; import { GRAFANA_RULES_SOURCE_NAME } from './utils/datasource'; +import { getDefaultQueries } from './utils/rule-form'; jest.mock('./components/rule-editor/ExpressionEditor', () => ({ // eslint-disable-next-line react/display-name @@ -74,14 +74,11 @@ describe('RuleEditor grafana managed rules', () => { name: 'Prom', isDefault: true, }, - { alerting: true } + { alerting: false } ), }; - const dsServer = new MockDataSourceSrv(dataSources); - jest.spyOn(dsServer, 'get').mockResolvedValue(new MockDataSourceApi('ds')); - - setDataSourceSrv(dsServer); + setDataSourceSrv(new MockDataSourceSrv(dataSources)); mocks.getAllDataSources.mockReturnValue(Object.values(dataSources)); mocks.api.setRulerRuleGroup.mockResolvedValue(); mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]); @@ -124,7 +121,6 @@ describe('RuleEditor grafana managed rules', () => { rulerApiEnabled: false, }, }); - renderRuleEditor(); await waitForElementToBeRemoved(screen.getAllByTestId('Spinner')); @@ -146,7 +142,6 @@ describe('RuleEditor grafana managed rules', () => { // save and check what was sent to backend await userEvent.click(ui.buttons.save.get()); - // 9seg await waitFor(() => expect(mocks.api.setRulerRuleGroup).toHaveBeenCalled()); // 9seg @@ -154,107 +149,20 @@ describe('RuleEditor grafana managed rules', () => { { dataSourceName: GRAFANA_RULES_SOURCE_NAME, apiVersion: 'legacy' }, 'Folder A', { - name: 'group1', interval: '1m', + name: 'group1', rules: [ { - grafana_alert: { - title: 'my great new rule', - condition: 'C', - no_data_state: 'NoData', - exec_err_state: GrafanaAlertStateDecision.Error, - data: [ - { - refId: 'A', - relativeTimeRange: { - from: 600, - to: 0, - }, - datasourceUid: 'mock-ds-2', - model: { - hide: false, - refId: 'A', - }, - queryType: '', - }, - { - refId: 'B', - datasourceUid: '__expr__', - queryType: '', - model: { - refId: 'B', - hide: false, - type: 'reduce', - datasource: { - uid: '__expr__', - type: '__expr__', - }, - conditions: [ - { - type: 'query', - evaluator: { - params: [], - type: 'gt', - }, - operator: { - type: 'and', - }, - query: { - params: ['B'], - }, - reducer: { - params: [], - type: 'last', - }, - }, - ], - reducer: 'last', - expression: 'A', - }, - }, - { - refId: 'C', - datasourceUid: '__expr__', - queryType: '', - model: { - refId: 'C', - hide: false, - type: 'threshold', - datasource: { - uid: '__expr__', - type: '__expr__', - }, - conditions: [ - { - type: 'query', - evaluator: { - params: [0], - type: 'gt', - }, - operator: { - type: 'and', - }, - query: { - params: ['C'], - }, - reducer: { - params: [], - type: 'last', - }, - }, - ], - expression: 'B', - }, - }, - ], - is_paused: false, - }, + annotations: { description: 'some description' }, + labels: { severity: 'warn' }, for: '5m', - annotations: { - description: 'some description', - }, - labels: { - severity: 'warn', + grafana_alert: { + condition: 'B', + data: getDefaultQueries(), + exec_err_state: GrafanaAlertStateDecision.Error, + is_paused: false, + no_data_state: 'NoData', + title: 'my great new rule', }, }, ], diff --git a/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx b/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx index 946dbe273ea..9573d851405 100644 --- a/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx +++ b/public/app/features/alerting/unified/RuleEditorRecordingRule.test.tsx @@ -1,9 +1,8 @@ -import { screen, waitFor, waitForElementToBeRemoved, within } from '@testing-library/react'; +import { waitFor, screen, within, waitForElementToBeRemoved } from '@testing-library/react'; import userEvent, { PointerEventsCheckLevel } from '@testing-library/user-event'; import React from 'react'; import { renderRuleEditor, ui } from 'test/helpers/alertingRuleEditor'; import { clickSelectOption } from 'test/helpers/selectOptionInTest'; -import { MockDataSourceApi } from 'test/mocks/datasource_srv'; import { byRole, byText } from 'testing-library-selector'; import { setDataSourceSrv } from '@grafana/runtime'; @@ -76,10 +75,7 @@ describe('RuleEditor recording rules', () => { ), }; - const dsServer = new MockDataSourceSrv(dataSources); - jest.spyOn(dsServer, 'get').mockResolvedValue(new MockDataSourceApi('ds')); - - setDataSourceSrv(dsServer); + setDataSourceSrv(new MockDataSourceSrv(dataSources)); mocks.getAllDataSources.mockReturnValue(Object.values(dataSources)); mocks.api.setRulerRuleGroup.mockResolvedValue(); mocks.api.fetchRulerRulesNamespace.mockResolvedValue([]); diff --git a/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx b/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx index a4a98c862bc..0e7a6c485a2 100644 --- a/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/AlertRuleForm.tsx @@ -2,40 +2,23 @@ import { css } from '@emotion/css'; import React, { FC, useEffect, useMemo, useState } from 'react'; import { DeepMap, FieldError, FormProvider, useForm, useFormContext, UseFormWatch } from 'react-hook-form'; import { Link } from 'react-router-dom'; -import { useAsync } from 'react-use'; -import { DataQuery, DataSourceApi, DataSourceJsonData, GrafanaTheme2, UrlQueryMap } from '@grafana/data'; -import { config, logInfo } from '@grafana/runtime'; -import { - Button, - ConfirmModal, - CustomScrollbar, - Field, - HorizontalGroup, - Input, - LoadingPlaceholder, - Spinner, - useStyles2, -} from '@grafana/ui'; +import { GrafanaTheme2 } from '@grafana/data'; +import { logInfo, config } from '@grafana/runtime'; +import { Button, ConfirmModal, CustomScrollbar, Spinner, useStyles2, HorizontalGroup, Field, Input } from '@grafana/ui'; import { useAppNotification } from 'app/core/copy/appNotification'; import { contextSrv } from 'app/core/core'; import { useCleanup } from 'app/core/hooks/useCleanup'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { useDispatch } from 'app/types'; import { RuleWithLocation } from 'app/types/unified-alerting'; -import { AlertQuery, RulerRuleDTO } from 'app/types/unified-alerting-dto'; import { LogMessages, trackNewAlerRuleFormCancelled, trackNewAlerRuleFormError } from '../../Analytics'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { deleteRuleAction, saveRuleFormAction } from '../../state/actions'; import { RuleFormType, RuleFormValues } from '../../types/rule-form'; import { initialAsyncRequestState } from '../../utils/redux'; -import { - getDefaultFormValues, - getDefaultQueriesAsync, - getInitialDefaultQueries, - rulerRuleToFormValues, -} from '../../utils/rule-form'; +import { getDefaultFormValues, getDefaultQueries, rulerRuleToFormValues } from '../../utils/rule-form'; import * as ruleId from '../../utils/rule-id'; import { CloudEvaluationBehavior } from './CloudEvaluationBehavior'; @@ -90,24 +73,6 @@ type Props = { prefill?: Partial; // Existing implies we modify existing rule. Prefill only provides default form values }; -export const useGetDefaults = (queryParams: UrlQueryMap, existing: RuleWithLocation | undefined) => { - const [defaultDsAndQueries, setDefaultDsAndQueries] = useState<{ - queries: AlertQuery[] | null; - ds?: DataSourceApi; - }>({ queries: null }); - - useAsync(async () => { - setDefaultDsAndQueries(await getDefaultQueriesAsync()); - }, [existing]); - - const defaultsInQueryParams: string = queryParams['defaults'] as string; - const defaultsInQueryParamsObject = useMemo( - () => ({ ...(defaultsInQueryParams ? JSON.parse(defaultsInQueryParams) : {}) }), - [defaultsInQueryParams] - ); - return { defaultDsAndQueries, defaultsInQueryParamsObject, defaultsInQueryParams }; -}; - export const AlertRuleForm: FC = ({ existing, prefill }) => { const styles = useStyles2(getStyles); const dispatch = useDispatch(); @@ -119,11 +84,6 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => { const returnTo: string = (queryParams['returnTo'] as string | undefined) ?? '/alerting/list'; const [showDeleteModal, setShowDeleteModal] = useState(false); - const { defaultDsAndQueries, defaultsInQueryParamsObject, defaultsInQueryParams } = useGetDefaults( - queryParams, - existing - ); - const defaultValues: RuleFormValues = useMemo(() => { if (existing) { return rulerRuleToFormValues(existing); @@ -138,13 +98,13 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => { return { ...getDefaultFormValues(), - queries: getInitialDefaultQueries(), + queries: getDefaultQueries(), condition: 'C', - ...defaultsInQueryParamsObject, + ...(queryParams['defaults'] ? JSON.parse(queryParams['defaults'] as string) : {}), type: RuleFormType.grafana, evaluateEvery: evaluateEvery, }; - }, [existing, prefill, evaluateEvery, defaultsInQueryParamsObject]); + }, [existing, prefill, queryParams, evaluateEvery]); const formAPI = useForm({ mode: 'onSubmit', @@ -152,21 +112,7 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => { shouldFocusError: true, }); - const { handleSubmit, watch, reset } = formAPI; - - // only reset once we get some value in defaultDsAndQueries.queries, adding this value. - useEffect(() => { - const shouldReset = !existing && !prefill && defaultDsAndQueries.queries && !Boolean(defaultsInQueryParams); - if (shouldReset) { - reset({ - ...getDefaultFormValues(), - queries: defaultDsAndQueries.queries, - condition: 'C', - ...defaultsInQueryParamsObject, - type: RuleFormType.grafana, - }); - } - }, [defaultDsAndQueries.queries, reset, existing, prefill, defaultsInQueryParamsObject, defaultsInQueryParams]); + const { handleSubmit, watch } = formAPI; const type = watch('type'); const dataSourceName = watch('dataSourceName'); @@ -248,7 +194,7 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => { const evaluateEveryInForm = watch('evaluateEvery'); useEffect(() => setEvaluateEvery(evaluateEveryInForm), [evaluateEveryInForm]); - return defaultDsAndQueries.queries ? ( + return (
e.preventDefault()} className={styles.form}> @@ -301,13 +247,7 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => {
- + {showStep2 && ( <> {type === RuleFormType.grafana ? ( @@ -341,8 +281,6 @@ export const AlertRuleForm: FC = ({ existing, prefill }) => { ) : null} {showEditYaml ? setShowEditYaml(false)} /> : null} - ) : ( - ); }; diff --git a/public/app/features/alerting/unified/components/rule-editor/ExpressionEditor.tsx b/public/app/features/alerting/unified/components/rule-editor/ExpressionEditor.tsx index 28a03209c20..a803455386d 100644 --- a/public/app/features/alerting/unified/components/rule-editor/ExpressionEditor.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/ExpressionEditor.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import { noop } from 'lodash'; -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { useAsync, usePrevious } from 'react-use'; +import React, { FC, useCallback, useMemo } from 'react'; +import { useAsync } from 'react-use'; import { CoreApp, DataQuery, DataSourcePluginContextProvider, GrafanaTheme2, LoadingState } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; @@ -9,8 +9,6 @@ import { Alert, Button, useStyles2 } from '@grafana/ui'; import { LokiQuery } from 'app/plugins/datasource/loki/types'; import { PromQuery } from 'app/plugins/datasource/prometheus/types'; -import { AlertQuery } from '../../../../../types/unified-alerting-dto'; - import { CloudAlertPreview } from './CloudAlertPreview'; import { usePreview } from './PreviewRule'; @@ -19,8 +17,6 @@ export interface ExpressionEditorProps { onChange: (value: string) => void; dataSourceName: string; // will be a prometheus or loki datasource showPreviewAlertsButton: boolean; - asyncDefaultQuery?: AlertQuery; - preservePreviousValue: boolean; } export const ExpressionEditor: FC = ({ @@ -28,31 +24,11 @@ export const ExpressionEditor: FC = ({ onChange, dataSourceName, showPreviewAlertsButton = true, - asyncDefaultQuery, - preservePreviousValue, }) => { const styles = useStyles2(getStyles); const { mapToValue, mapToQuery } = useQueryMappers(dataSourceName); - const [dataQuery, setDataQuery] = useState(mapToQuery({ refId: 'A', hide: false }, value)); - const defaultModel = asyncDefaultQuery?.model; - const previousDataSource = usePrevious(dataSourceName); - - // New alert: update with default query once we have the async default value - useEffect(() => { - const shouldSetDefaultQuery = !preservePreviousValue && defaultModel; - if (shouldSetDefaultQuery) { - setDataQuery((dataQuery) => ({ ...dataQuery, ...{ ...defaultModel } })); - } - }, [defaultModel, preservePreviousValue]); - // when data source is changed - useEffect(() => { - const shouldSetDefaultQuery = - !!previousDataSource && previousDataSource !== dataSourceName && Boolean(dataSourceName) && defaultModel; - if (shouldSetDefaultQuery) { - setDataQuery((dataQuery) => ({ ...dataQuery, ...{ ...defaultModel } })); - } - }, [dataSourceName, defaultModel, previousDataSource]); + const dataQuery = mapToQuery({ refId: 'A', hide: false }, value); const { error, @@ -95,7 +71,7 @@ export const ExpressionEditor: FC = ({ // The preview API returns arrays with empty elements when there are no firing alerts const previewHasAlerts = previewDataFrame && previewDataFrame.fields.some((field) => field.values.length > 0); - return dataQuery ? ( + return ( <> = ({
)} - ) : null; + ); }; const getStyles = (theme: GrafanaTheme2) => ({ diff --git a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx index 51d10edd106..60acdd0011a 100644 --- a/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/QueryRows.tsx @@ -2,16 +2,7 @@ import { omit } from 'lodash'; import React, { PureComponent, useState } from 'react'; import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; -import { - CoreApp, - DataQuery, - DataSourceApi, - DataSourceInstanceSettings, - DataSourceJsonData, - LoadingState, - PanelData, - RelativeTimeRange, -} from '@grafana/data'; +import { DataQuery, DataSourceInstanceSettings, LoadingState, PanelData, RelativeTimeRange } from '@grafana/data'; import { getDataSourceSrv } from '@grafana/runtime'; import { Button, Card, Icon } from '@grafana/ui'; import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow'; @@ -75,30 +66,22 @@ export class QueryRows extends PureComponent { ); }; - onChangeDataSource = async (settings: DataSourceInstanceSettings, index: number) => { + onChangeDataSource = (settings: DataSourceInstanceSettings, index: number) => { const { queries, onQueriesChange } = this.props; - const updatedQueries = await Promise.all( - queries.map(async (item, itemIndex) => { - if (itemIndex !== index) { - return item; - } + const updatedQueries = queries.map((item, itemIndex) => { + if (itemIndex !== index) { + return item; + } - const previousSettings = this.getDataSourceSettings(item); + const previousSettings = this.getDataSourceSettings(item); - // Copy model if changing to a datasource of same type. - if (settings.type === previousSettings?.type) { - return copyModel(item, settings); - } - let ds; - try { - ds = await getDataSourceSrv().get(settings.uid); // get new ds - } catch (e) { - return newModel(item, settings); - } - return newModel(item, settings, ds); - }) - ); + // Copy model if changing to a datasource of same type. + if (settings.type === previousSettings?.type) { + return copyModel(item, settings); + } + return newModel(item, settings); + }); onQueriesChange(updatedQueries); }; @@ -235,34 +218,13 @@ function copyModel(item: AlertQuery, settings: DataSourceInstanceSettings): Omit }; } -function newModel( - item: AlertQuery, - settings: DataSourceInstanceSettings, - ds?: DataSourceApi -): Omit { - if (!ds) { - return { - refId: item.refId, - relativeTimeRange: item.relativeTimeRange, - queryType: '', - datasourceUid: settings.uid, - model: { - refId: item.refId, - hide: false, - datasource: { - type: settings.type, - uid: settings.uid, - }, - }, - }; - } +function newModel(item: AlertQuery, settings: DataSourceInstanceSettings): Omit { return { refId: item.refId, relativeTimeRange: item.relativeTimeRange, queryType: '', datasourceUid: settings.uid, model: { - ...ds?.getDefaultQuery?.(CoreApp.UnifiedAlerting), refId: item.refId, hide: false, datasource: { diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx index 9247392547c..4b426163f84 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/QueryAndExpressionsStep.tsx @@ -1,7 +1,7 @@ import React, { FC, useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'; import { useFormContext } from 'react-hook-form'; -import { DataQuery, DataSourceApi, DataSourceJsonData, LoadingState, PanelData } from '@grafana/data'; +import { LoadingState, PanelData } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Stack } from '@grafana/experimental'; import { config } from '@grafana/runtime'; @@ -35,25 +35,16 @@ import { interface Props { editingExistingRule: boolean; - prefill: boolean; onDataChange: (error: string) => void; - asyncDefaultQueries?: AlertQuery[]; - asyncDataSource?: DataSourceApi; } -export const QueryAndExpressionsStep: FC = ({ - editingExistingRule, - onDataChange, - asyncDefaultQueries, - asyncDataSource, - prefill, -}) => { +export const QueryAndExpressionsStep: FC = ({ editingExistingRule, onDataChange }) => { const runner = useRef(new AlertingQueryRunner()); const { setValue, getValues, watch, - formState: { errors, isDirty }, + formState: { errors }, control, } = useFormContext(); const [panelData, setPanelData] = useState>({}); @@ -63,6 +54,7 @@ export const QueryAndExpressionsStep: FC = ({ panelData: {}, }; const [{ queries }, dispatch] = useReducer(queriesAndExpressionsReducer, initialState); + const [type, condition, dataSourceName] = watch(['type', 'condition', 'dataSourceName']); const isGrafanaManagedType = type === RuleFormType.grafana; @@ -79,15 +71,6 @@ export const QueryAndExpressionsStep: FC = ({ runner.current.run(getValues('queries')); }, [getValues]); - const updateWithDefault = !editingExistingRule && !prefill; - //once default queries is updated - useEffect(() => { - const shouldSetDataQuery = updateWithDefault && !isDirty && asyncDefaultQueries; - if (shouldSetDataQuery) { - dispatch(setDataQueries(asyncDefaultQueries)); - } - }, [asyncDefaultQueries, updateWithDefault, isDirty]); - // whenever we update the queries we have to update the form too useEffect(() => { setValue('queries', queries, { shouldValidate: false }); @@ -193,13 +176,6 @@ export const QueryAndExpressionsStep: FC = ({ } }, [condition, queries, handleSetCondition]); - const defaultQueryAvailable = asyncDataSource && asyncDefaultQueries; - const onAddNewQuery = () => { - if (defaultQueryAvailable) { - dispatch(addNewDataQuery({ ds: asyncDataSource, defaultQuery: asyncDefaultQueries[0]?.model })); - } - }; - return ( @@ -215,8 +191,6 @@ export const QueryAndExpressionsStep: FC = ({ {...field} dataSourceName={dataSourceName} showPreviewAlertsButton={!isRecordingRuleType} - asyncDefaultQuery={asyncDefaultQueries?.[0]} - preservePreviousValue={!updateWithDefault} /> ); }} @@ -265,9 +239,11 @@ export const QueryAndExpressionsStep: FC = ({