From 529f564bd4ae9d912ef7d1449e8cb42eb3bac54c Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 22 Jan 2021 16:08:54 +0100 Subject: [PATCH] AlertingNG: Enable UI to Save Alert Definitions (#30394) * transform state to what the api expects * add expr prop to dataquery * Add evalutate field * add refid picker to options * minor fix to enable save * fix import * more fixes after merge * use default datasource if not changed * replace name with title * Change name in ui as well * remove not used loadDataSources function * prettier fixes * look up datasource * correct datasource per query model * revert dataquery change, use expressionid const * fix for type * fix faulty const * description readonly --- packages/grafana-data/src/types/datasource.ts | 6 + .../features/alerting/NextGenAlertingPage.tsx | 35 ++++- .../components/AlertDefinitionOptions.tsx | 127 ++++++++++-------- public/app/features/alerting/state/actions.ts | 55 ++++++-- .../app/features/alerting/state/reducers.ts | 5 +- public/app/types/alerting.ts | 5 +- public/app/types/query.ts | 5 +- 7 files changed, 159 insertions(+), 79 deletions(-) diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index fa2f2ef2a68..c5a8719ba85 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -186,6 +186,11 @@ export abstract class DataSourceApi< */ readonly type: string; + /** + * Set in constructor + */ + readonly uid: string; + /** * min interval range */ @@ -196,6 +201,7 @@ export abstract class DataSourceApi< this.id = instanceSettings.id; this.type = instanceSettings.type; this.meta = {} as DataSourcePluginMeta; + this.uid = instanceSettings.uid; } /** diff --git a/public/app/features/alerting/NextGenAlertingPage.tsx b/public/app/features/alerting/NextGenAlertingPage.tsx index 881d4bce459..089ed5d01d5 100644 --- a/public/app/features/alerting/NextGenAlertingPage.tsx +++ b/public/app/features/alerting/NextGenAlertingPage.tsx @@ -1,11 +1,12 @@ import React, { FormEvent, PureComponent } from 'react'; import { hot } from 'react-hot-loader'; -import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'; +import { MapDispatchToProps, MapStateToProps } from 'react-redux'; import { css } from 'emotion'; -import { GrafanaTheme } from '@grafana/data'; +import { GrafanaTheme, SelectableValue } from '@grafana/data'; import { Button, Icon, stylesFactory } from '@grafana/ui'; import { PageToolbar } from 'app/core/components/PageToolbar/PageToolbar'; import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper'; +import { connectWithCleanUp } from 'app/core/components/connectWithCleanUp'; import AlertingQueryEditor from './components/AlertingQueryEditor'; import { AlertDefinitionOptions } from './components/AlertDefinitionOptions'; import { AlertingQueryPreview } from './components/AlertingQueryPreview'; @@ -15,7 +16,13 @@ import { updateAlertDefinitionUiState, loadNotificationTypes, } from './state/actions'; -import { AlertDefinition, AlertDefinitionUiState, NotificationChannelType, StoreState } from '../../types'; +import { + AlertDefinition, + AlertDefinitionUiState, + NotificationChannelType, + QueryGroupOptions, + StoreState, +} from '../../types'; import { config } from 'app/core/config'; import { PanelQueryRunner } from '../query/state/PanelQueryRunner'; @@ -27,6 +34,7 @@ interface ConnectedProps { uiState: AlertDefinitionUiState; notificationChannelTypes: NotificationChannelType[]; queryRunner: PanelQueryRunner; + queryOptions: QueryGroupOptions; } interface DispatchProps { @@ -51,6 +59,18 @@ class NextGenAlertingPage extends PureComponent { this.props.updateAlertDefinitionOption({ [event.currentTarget.name]: event.currentTarget.value }); }; + onChangeInterval = (interval: SelectableValue) => { + this.props.updateAlertDefinitionOption({ + interval: interval.value, + }); + }; + + onConditionChange = (condition: SelectableValue) => { + this.props.updateAlertDefinitionOption({ + condition: { ...this.props.alertDefinition.condition, refId: condition.value! }, + }); + }; + onSaveAlert = () => { const { createAlertDefinition } = this.props; @@ -82,6 +102,7 @@ class NextGenAlertingPage extends PureComponent { uiState, updateAlertDefinitionUiState, queryRunner, + queryOptions, } = this.props; const styles = getStyles(config.theme); @@ -106,6 +127,9 @@ class NextGenAlertingPage extends PureComponent { alertDefinition={alertDefinition} onChange={this.onChangeAlertOption} notificationChannelTypes={notificationChannelTypes} + onIntervalChange={this.onChangeInterval} + onConditionChange={this.onConditionChange} + queryOptions={queryOptions} /> } /> @@ -119,6 +143,7 @@ const mapStateToProps: MapStateToProps = ( return { uiState: state.alertDefinition.uiState, alertDefinition: state.alertDefinition.alertDefinition, + queryOptions: state.alertDefinition.queryOptions, notificationChannelTypes: state.notificationChannel.notificationChannelTypes, queryRunner: state.alertDefinition.queryRunner, }; @@ -131,7 +156,9 @@ const mapDispatchToProps: MapDispatchToProps = { loadNotificationTypes, }; -export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(NextGenAlertingPage)); +export default hot(module)( + connectWithCleanUp(mapStateToProps, mapDispatchToProps, (state) => state.alertDefinition)(NextGenAlertingPage) +); const getStyles = stylesFactory((theme: GrafanaTheme) => ({ wrapper: css` diff --git a/public/app/features/alerting/components/AlertDefinitionOptions.tsx b/public/app/features/alerting/components/AlertDefinitionOptions.tsx index 6df1ed8beb6..cadf51ed10d 100644 --- a/public/app/features/alerting/components/AlertDefinitionOptions.tsx +++ b/public/app/features/alerting/components/AlertDefinitionOptions.tsx @@ -1,79 +1,94 @@ -import React, { FC, FormEvent, useState } from 'react'; +import React, { FC, FormEvent, useMemo } from 'react'; import { css } from 'emotion'; -import { GrafanaTheme } from '@grafana/data'; -import { Field, Input, Tab, TabContent, TabsBar, TextArea, useStyles } from '@grafana/ui'; -import { AlertDefinition, NotificationChannelType } from 'app/types'; +import { GrafanaTheme, SelectableValue } from '@grafana/data'; +import { Field, Input, Select, TextArea, useStyles } from '@grafana/ui'; +import { AlertDefinition, NotificationChannelType, QueryGroupOptions } from 'app/types'; interface Props { alertDefinition: AlertDefinition; notificationChannelTypes: NotificationChannelType[]; onChange: (event: FormEvent) => void; + onIntervalChange: (interval: SelectableValue) => void; + onConditionChange: (refId: SelectableValue) => void; + queryOptions: QueryGroupOptions; } -enum Tabs { - Alert = 'alert', - Panel = 'panel', -} - -const tabs = [ - { id: Tabs.Alert, text: 'Alert definition' }, - { id: Tabs.Panel, text: 'Panel' }, -]; - -export const AlertDefinitionOptions: FC = ({ alertDefinition, onChange }) => { +export const AlertDefinitionOptions: FC = ({ + alertDefinition, + onChange, + onIntervalChange, + onConditionChange, + queryOptions, +}) => { const styles = useStyles(getStyles); - const [activeTab, setActiveTab] = useState(Tabs.Alert); + const refIds = useMemo(() => queryOptions.queries.map((q) => ({ value: q.refId, label: q.refId })), [ + queryOptions.queries, + ]); return ( -
- - {tabs.map((tab, index) => ( - setActiveTab(tab.id)} +
+
+

Alert definition

+ + + + +