Alerting: Remove unused NGAlerting components (#34568)

This commit is contained in:
Peter Holmberg 2021-05-24 14:20:11 +02:00 committed by GitHub
parent 6970c9ebfd
commit a0b78313f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 18 additions and 928 deletions

View File

@ -25,7 +25,6 @@ const setup = (propOverrides?: object) => {
togglePauseAlertRule: jest.fn(),
search: '',
isLoading: false,
ngAlertDefinitions: [],
};
Object.assign(props, propOverrides);

View File

@ -5,7 +5,7 @@ import Page from 'app/core/components/Page/Page';
import AlertRuleItem from './AlertRuleItem';
import appEvents from 'app/core/app_events';
import { getNavModel } from 'app/core/selectors/navModel';
import { AlertDefinition, AlertRule, StoreState } from 'app/types';
import { AlertRule, StoreState } from 'app/types';
import { getAlertRulesAsync, togglePauseAlertRule } from './state/actions';
import { getAlertRuleItems, getSearchQuery } from './state/selectors';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
@ -13,7 +13,6 @@ import { SelectableValue } from '@grafana/data';
import { config, locationService } from '@grafana/runtime';
import { setSearchQuery } from './state/reducers';
import { Button, LinkButton, Select, VerticalGroup } from '@grafana/ui';
import { AlertDefinitionItem } from './components/AlertDefinitionItem';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { ShowModalReactEvent } from '../../types/events';
import { AlertHowToModal } from './AlertHowToModal';
@ -24,7 +23,6 @@ function mapStateToProps(state: StoreState) {
alertRules: getAlertRuleItems(state),
search: getSearchQuery(state.alertRules),
isLoading: state.alertRules.isLoading,
ngAlertDefinitions: state.alertDefinition.alertDefinitions,
};
}
@ -125,23 +123,13 @@ export class AlertRuleListUnconnected extends PureComponent<Props> {
</Button>
</div>
<VerticalGroup spacing="none">
{alertRules.map((rule, index) => {
// Alert definition has "title" as name property.
if (rule.hasOwnProperty('name')) {
return (
<AlertRuleItem
rule={rule as AlertRule}
key={rule.id}
search={search}
onTogglePause={() => this.onTogglePause(rule as AlertRule)}
/>
);
}
{alertRules.map((rule) => {
return (
<AlertDefinitionItem
key={`${rule.id}-${index}`}
alertDefinition={rule as AlertDefinition}
<AlertRuleItem
rule={rule as AlertRule}
key={rule.id}
search={search}
onTogglePause={() => this.onTogglePause(rule as AlertRule)}
/>
);
})}

View File

@ -1,179 +0,0 @@
import React, { FormEvent, PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect, ConnectedProps } from 'react-redux';
import { css } from '@emotion/css';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { locationService } from '@grafana/runtime';
import { PageToolbar, stylesFactory, ToolbarButton, withTheme2, Themeable2 } from '@grafana/ui';
import { config } from 'app/core/config';
import { SplitPaneWrapper } from 'app/core/components/SplitPaneWrapper/SplitPaneWrapper';
import { AlertingQueryEditor } from './components/AlertingQueryEditor';
import { AlertDefinitionOptions } from './components/AlertDefinitionOptions';
import { AlertingQueryPreview } from './components/AlertingQueryPreview';
import {
cleanUpDefinitionState,
createAlertDefinition,
evaluateAlertDefinition,
evaluateNotSavedAlertDefinition,
getAlertDefinition,
updateAlertDefinition,
updateAlertDefinitionOption,
updateAlertDefinitionUiState,
} from './state/actions';
import { StoreState } from 'app/types';
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
import { GrafanaQuery } from '../../types/unified-alerting-dto';
function mapStateToProps(state: StoreState, props: RouteProps) {
return {
uiState: state.alertDefinition.uiState,
getInstances: state.alertDefinition.getInstances,
alertDefinition: state.alertDefinition.alertDefinition,
pageId: props.match.params.id as string,
};
}
const mapDispatchToProps = {
updateAlertDefinitionUiState,
updateAlertDefinitionOption,
evaluateAlertDefinition,
updateAlertDefinition,
createAlertDefinition,
getAlertDefinition,
evaluateNotSavedAlertDefinition,
cleanUpDefinitionState,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
interface RouteProps extends GrafanaRouteComponentProps<{ id: string }> {}
interface OwnProps extends Themeable2 {
saveDefinition: typeof createAlertDefinition | typeof updateAlertDefinition;
}
type Props = OwnProps & ConnectedProps<typeof connector>;
class UnthemedNextGenAlertingPage extends PureComponent<Props> {
componentDidMount() {
const { getAlertDefinition, pageId } = this.props;
if (pageId) {
getAlertDefinition(pageId);
}
}
componentWillUnmount() {
this.props.cleanUpDefinitionState();
}
onChangeAlertOption = (event: FormEvent<HTMLElement>) => {
const formEvent = event as FormEvent<HTMLFormElement>;
this.props.updateAlertDefinitionOption({ [formEvent.currentTarget.name]: formEvent.currentTarget.value });
};
onChangeInterval = (interval: SelectableValue<number>) => {
this.props.updateAlertDefinitionOption({
intervalSeconds: interval.value,
});
};
onConditionChange = (condition: SelectableValue<string>) => {
this.props.updateAlertDefinitionOption({
condition: condition.value,
});
};
onSaveAlert = () => {
const { alertDefinition, createAlertDefinition, updateAlertDefinition } = this.props;
if (alertDefinition.uid) {
updateAlertDefinition();
} else {
createAlertDefinition();
}
};
onDiscard = () => {
locationService.replace(`${config.appSubUrl}/alerting/ng/list`);
};
onTest = () => {
const { alertDefinition, evaluateAlertDefinition, evaluateNotSavedAlertDefinition } = this.props;
if (alertDefinition.uid) {
evaluateAlertDefinition();
} else {
evaluateNotSavedAlertDefinition();
}
};
renderToolbarActions() {
return [
<ToolbarButton variant="destructive" key="discard" onClick={this.onDiscard}>
Discard
</ToolbarButton>,
<ToolbarButton key="test" onClick={this.onTest}>
Test
</ToolbarButton>,
<ToolbarButton variant="primary" key="save" onClick={this.onSaveAlert}>
Save
</ToolbarButton>,
];
}
render() {
const { alertDefinition, uiState, updateAlertDefinitionUiState, getInstances, theme } = this.props;
const styles = getStyles(theme);
return (
<div className={styles.wrapper}>
<PageToolbar title="Alert editor" pageIcon="bell">
{this.renderToolbarActions()}
</PageToolbar>
<div className={styles.splitPanesWrapper}>
<SplitPaneWrapper
leftPaneComponents={[
<AlertingQueryPreview key="queryPreview" getInstances={getInstances} queries={[]} onTest={this.onTest} />,
<AlertingQueryEditor key="queryEditor" value={[] as GrafanaQuery[]} onChange={() => {}} />,
]}
uiState={uiState}
updateUiState={updateAlertDefinitionUiState}
rightPaneComponents={
<AlertDefinitionOptions
alertDefinition={alertDefinition}
onChange={this.onChangeAlertOption}
onIntervalChange={this.onChangeInterval}
onConditionChange={this.onConditionChange}
/>
}
/>
</div>
</div>
);
}
}
const NextGenAlertingPageUnconnected = withTheme2(UnthemedNextGenAlertingPage);
export default hot(module)(connector(NextGenAlertingPageUnconnected));
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
wrapper: css`
width: calc(100% - 55px);
height: 100%;
position: fixed;
top: 0;
bottom: 0;
background: ${theme.colors.background.canvas};
display: flex;
flex-direction: column;
`,
splitPanesWrapper: css`
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
position: relative;
`,
}));

View File

@ -1,55 +0,0 @@
import React, { FC } from 'react';
// @ts-ignore
import Highlighter from 'react-highlight-words';
import { FeatureState } from '@grafana/data';
import { Card, FeatureBadge, Icon, LinkButton } from '@grafana/ui';
import { AlertDefinition } from 'app/types';
import { config } from '@grafana/runtime';
interface Props {
alertDefinition: AlertDefinition;
search: string;
}
export const AlertDefinitionItem: FC<Props> = ({ alertDefinition, search }) => {
return (
<Card heading={CardTitle(alertDefinition.title, search)}>
<Card.Figure>
<Icon size="xl" name="question-circle" className="alert-rule-item__icon" />
</Card.Figure>
<Card.Meta>
<span key="state">
<span key="text">{alertDefinition.description}</span>
</span>
</Card.Meta>
<Card.Actions>
{[
<LinkButton
key="edit"
variant="secondary"
href={
config.featureToggles.ngalert
? `/alerting/ng/${alertDefinition.uid}/edit`
: `/alerting/${alertDefinition.uid}/edit`
}
icon="cog"
>
Edit alert
</LinkButton>,
]}
</Card.Actions>
</Card>
);
};
const CardTitle = (title: string, search: string) => (
<div style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Highlighter
key={title}
highlightClassName="highlight-search-match"
textToHighlight={title}
searchWords={[search]}
/>
<FeatureBadge featureState={FeatureState.beta} />
</div>
);

View File

@ -1,83 +0,0 @@
import React, { FC, FormEvent } from 'react';
import { css } from '@emotion/css';
import { GrafanaTheme, SelectableValue } from '@grafana/data';
import { Field, Input, Select, Tab, TabContent, TabsBar, TextArea, useStyles } from '@grafana/ui';
import { AlertDefinition } from 'app/types';
const intervalOptions: Array<SelectableValue<number>> = [
{ value: 60, label: '1m' },
{ value: 300, label: '5m' },
{ value: 600, label: '10m' },
];
interface Props {
alertDefinition: AlertDefinition;
onChange: (event: FormEvent<HTMLElement>) => void;
onIntervalChange: (interval: SelectableValue<number>) => void;
onConditionChange: (refId: SelectableValue<string>) => void;
}
export const AlertDefinitionOptions: FC<Props> = ({ alertDefinition, onChange, onIntervalChange }) => {
const styles = useStyles(getStyles);
return (
<div className={styles.wrapper}>
<TabsBar>
<Tab label="Alert definition" active={true} />
</TabsBar>
<TabContent className={styles.container}>
<Field label="Title">
<Input width={25} name="title" value={alertDefinition.title} onChange={onChange} />
</Field>
<Field label="Description" description="What does the alert do? Why was it created?">
<TextArea
rows={5}
width={25}
name="description"
value={alertDefinition.description}
onChange={onChange}
readOnly={true}
/>
</Field>
<Field label="Evaluate">
<div className={styles.optionRow}>
<span className={styles.optionName}>Every</span>
<Select
onChange={onIntervalChange}
value={intervalOptions.find((i) => i.value === alertDefinition.intervalSeconds)}
options={intervalOptions}
width={10}
/>
</div>
</Field>
<Field label="Conditions">
<div />
</Field>
</TabContent>
</div>
);
};
const getStyles = (theme: GrafanaTheme) => {
return {
wrapper: css`
padding-top: ${theme.spacing.md};
height: 100%;
`,
container: css`
padding: ${theme.spacing.md};
background-color: ${theme.colors.panelBg};
height: 100%;
border-left: 1px solid ${theme.colors.border1};
`,
optionRow: css`
display: flex;
align-items: baseline;
`,
optionName: css`
font-size: ${theme.typography.size.md};
color: ${theme.colors.formInputText};
margin-right: ${theme.spacing.sm};
`,
};
};

View File

@ -1,118 +0,0 @@
import React, { FC, useState } from 'react';
import { css } from '@emotion/css';
import AutoSizer from 'react-virtualized-auto-sizer';
import { DataFrame, DataQuery, GrafanaTheme, PanelData } from '@grafana/data';
import { Icon, Tab, TabContent, TabsBar, useStyles } from '@grafana/ui';
import { PreviewQueryTab } from './PreviewQueryTab';
import { PreviewInstancesTab } from './PreviewInstancesTab';
import { EmptyState } from './EmptyState';
enum Tabs {
Query = 'query',
Instances = 'instances',
}
const tabs = [
{ id: Tabs.Query, text: 'Query result' },
{ id: Tabs.Instances, text: 'Alerting instances' },
];
interface Props {
getInstances: () => DataFrame[];
queries: DataQuery[];
onTest: () => void;
}
export const AlertingQueryPreview: FC<Props> = ({ getInstances, onTest, queries }) => {
const [activeTab, setActiveTab] = useState<string>(Tabs.Query);
const styles = useStyles(getStyles);
let data = {} as PanelData;
const instances = getInstances();
return (
<div className={styles.wrapper}>
<TabsBar>
{tabs.map((tab, index) => {
return (
<Tab
key={`${tab.id}-${index}`}
label={tab.text}
onChangeTab={() => setActiveTab(tab.id)}
active={activeTab === tab.id}
/>
);
})}
</TabsBar>
<TabContent className={styles.tabContent}>
{data &&
(data.state === 'Error' ? (
<EmptyState title="There was an error :(">
<div>{data.error?.data?.error}</div>
</EmptyState>
) : (
<QueriesAndInstances
instances={instances}
onTest={onTest}
data={data}
activeTab={activeTab}
queries={queries}
/>
))}
</TabContent>
</div>
);
};
interface PreviewProps {
queries: DataQuery[];
instances: DataFrame[];
onTest: () => void;
data: PanelData;
activeTab: string;
}
const QueriesAndInstances: FC<PreviewProps> = ({ queries, instances, onTest, data, activeTab }) => {
if (queries.length === 0) {
return (
<EmptyState title="No queries added.">
<div>Start adding queries to this alert and a visualisation for your queries will appear here.</div>
<div>
Learn more about how to create alert definitions <Icon name="external-link-alt" />
</div>
</EmptyState>
);
}
return (
<AutoSizer style={{ width: '100%', height: '100%' }}>
{({ width, height }) => {
switch (activeTab) {
case Tabs.Instances:
return <PreviewInstancesTab instances={instances} width={width} height={height} onTest={onTest} />;
case Tabs.Query:
default:
return <PreviewQueryTab data={data} width={width} height={height} />;
}
}}
</AutoSizer>
);
};
const getStyles = (theme: GrafanaTheme) => {
return {
wrapper: css`
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
padding: ${theme.spacing.md} 0 0 ${theme.spacing.md};
`,
tabContent: css`
background: ${theme.colors.panelBg};
height: 100%;
`,
};
};

View File

@ -1,37 +0,0 @@
import React, { FC, ReactNode } from 'react';
import { css } from '@emotion/css';
import { useStyles } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
interface Props {
title: string;
children: ReactNode | ReactNode[];
}
export const EmptyState: FC<Props> = ({ children, title }) => {
const styles = useStyles(getStyles);
return (
<div className={styles.emptyState}>
<h4 className={styles.emptyStateHeader}>{title}</h4>
{children}
</div>
);
};
const getStyles = (theme: GrafanaTheme) => {
return {
emptyState: css`
color: ${theme.colors.textSemiWeak};
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
`,
emptyStateHeader: css`
color: ${theme.colors.textSemiWeak};
`,
};
};

View File

@ -1,23 +0,0 @@
import React, { FC } from 'react';
import { DataFrame } from '@grafana/data';
import { Button, Table } from '@grafana/ui';
import { EmptyState } from './EmptyState';
interface Props {
instances: DataFrame[];
width: number;
height: number;
onTest: () => void;
}
export const PreviewInstancesTab: FC<Props> = ({ instances, onTest, height, width }) => {
if (!instances.length) {
return (
<EmptyState title="You havent tested your alert yet.">
<div>In order to see your instances, you need to test your alert first.</div>
<Button onClick={onTest}>Test alert now</Button>
</EmptyState>
);
}
return <Table data={instances[0]} height={height} width={width} />;
};

View File

@ -1,74 +0,0 @@
import React, { FC, useMemo, useState } from 'react';
import { css } from '@emotion/css';
import { getFrameDisplayName, GrafanaTheme, PanelData, SelectableValue } from '@grafana/data';
import { Button, Select, stylesFactory, Table, useTheme } from '@grafana/ui';
import { EmptyState } from './EmptyState';
interface Props {
data: PanelData;
width: number;
height: number;
}
export const PreviewQueryTab: FC<Props> = ({ data, height, width }) => {
const [currentSeries, setSeries] = useState<number>(0);
const theme = useTheme();
const styles = getStyles(theme, height);
const series = useMemo<Array<SelectableValue<number>>>(() => {
if (data?.series) {
return data.series.map((frame, index) => ({ value: index, label: getFrameDisplayName(frame) }));
}
return [];
}, [data]);
// Select padding
const padding = 16;
if (!data) {
return (
<EmptyState title="Run queries to view data.">
<Button>Run queries</Button>
</EmptyState>
);
}
if (!data.series) {
return null;
}
if (data.series.length > 1) {
return (
<div className={styles.wrapper}>
<div style={{ height: height - theme.spacing.formInputHeight - 16 }}>
<Table
data={data.series[currentSeries]}
height={height - theme.spacing.formInputHeight - padding}
width={width}
/>
</div>
<div className={styles.selectWrapper}>
<Select
onChange={(selectedValue) => setSeries(selectedValue.value!)}
options={series}
value={currentSeries}
/>
</div>
</div>
);
}
return <Table data={data.series[0]} height={height} width={width} />;
};
const getStyles = stylesFactory((theme: GrafanaTheme, height: number) => {
return {
wrapper: css`
label: preview-wrapper;
height: ${height}px;
`,
selectWrapper: css`
padding: ${theme.spacing.md};
`,
};
});

View File

@ -1,50 +1,13 @@
import {
AppEvents,
applyFieldOverrides,
dataFrameFromJSON,
DataFrameJSON,
DataQuery,
DataSourceApi,
} from '@grafana/data';
import { config, getBackendSrv, getDataSourceSrv, locationService } from '@grafana/runtime';
import { AppEvents } from '@grafana/data';
import { getBackendSrv, locationService } from '@grafana/runtime';
import { appEvents } from 'app/core/core';
import store from 'app/core/store';
import {
ALERT_DEFINITION_UI_STATE_STORAGE_KEY,
cleanUpState,
loadAlertRules,
loadedAlertRules,
notificationChannelLoaded,
setAlertDefinition,
setAlertDefinitions,
setInstanceData,
setNotificationChannels,
setUiState,
updateAlertDefinitionOptions,
} from './reducers';
import {
AlertDefinition,
AlertDefinitionState,
AlertDefinitionUiState,
AlertRuleDTO,
NotifierDTO,
QueryGroupDataSource,
QueryGroupOptions,
ThunkResult,
} from 'app/types';
import { ExpressionDatasourceID } from '../../expressions/ExpressionDatasource';
import { isExpressionQuery } from 'app/features/expressions/guards';
import { loadAlertRules, loadedAlertRules, notificationChannelLoaded, setNotificationChannels } from './reducers';
import { AlertRuleDTO, NotifierDTO, ThunkResult } from 'app/types';
export function getAlertRulesAsync(options: { state: string }): ThunkResult<void> {
return async (dispatch) => {
dispatch(loadAlertRules());
const rules: AlertRuleDTO[] = await getBackendSrv().get('/api/alerts', options);
if (config.featureToggles.ngalert) {
const ngAlertDefinitions = await getBackendSrv().get('/api/alert-definitions');
dispatch(setAlertDefinitions(ngAlertDefinitions.results));
}
dispatch(loadedAlertRules(rules));
};
}
@ -109,156 +72,3 @@ export function loadNotificationChannel(id: number): ThunkResult<void> {
dispatch(notificationChannelLoaded(notificationChannel));
};
}
export function getAlertDefinition(id: string): ThunkResult<void> {
return async (dispatch) => {
const alertDefinition = await getBackendSrv().get(`/api/alert-definitions/${id}`);
dispatch(setAlertDefinition(alertDefinition));
};
}
export function createAlertDefinition(): ThunkResult<void> {
return async (dispatch, getStore) => {
const alertDefinition = await buildAlertDefinition(getStore().alertDefinition);
await getBackendSrv().post(`/api/alert-definitions`, alertDefinition);
appEvents.emit(AppEvents.alertSuccess, ['Alert definition created']);
locationService.push('/alerting/ng/list');
};
}
export function updateAlertDefinition(): ThunkResult<void> {
return async (dispatch, getStore) => {
const alertDefinition = await buildAlertDefinition(getStore().alertDefinition);
const updatedAlertDefinition = await getBackendSrv().put(
`/api/alert-definitions/${alertDefinition.uid}`,
alertDefinition
);
appEvents.emit(AppEvents.alertSuccess, ['Alert definition updated']);
dispatch(setAlertDefinition(updatedAlertDefinition));
};
}
export function updateAlertDefinitionUiState(uiState: Partial<AlertDefinitionUiState>): ThunkResult<void> {
return (dispatch, getStore) => {
const nextState = { ...getStore().alertDefinition.uiState, ...uiState };
dispatch(setUiState(nextState));
try {
store.setObject(ALERT_DEFINITION_UI_STATE_STORAGE_KEY, nextState);
} catch (error) {
console.error(error);
}
};
}
export function updateAlertDefinitionOption(alertDefinition: Partial<AlertDefinition>): ThunkResult<void> {
return (dispatch) => {
dispatch(updateAlertDefinitionOptions(alertDefinition));
};
}
export function evaluateAlertDefinition(): ThunkResult<void> {
return async (dispatch, getStore) => {
const { alertDefinition } = getStore().alertDefinition;
const response: { instances: DataFrameJSON[] } = await getBackendSrv().get(
`/api/alert-definitions/eval/${alertDefinition.uid}`
);
const handledResponse = handleJSONResponse(response.instances);
dispatch(setInstanceData(handledResponse));
appEvents.emit(AppEvents.alertSuccess, ['Alert definition tested successfully']);
};
}
export function evaluateNotSavedAlertDefinition(): ThunkResult<void> {
return async (dispatch, getStore) => {
const { alertDefinition } = getStore().alertDefinition;
const defaultDataSource = await getDataSourceSrv().get(null);
const response: { instances: DataFrameJSON[] } = await getBackendSrv().post('/api/alert-definitions/eval', {
condition: alertDefinition.condition,
data: buildDataQueryModel({} as QueryGroupOptions, defaultDataSource),
});
const handledResponse = handleJSONResponse(response.instances);
dispatch(setInstanceData(handledResponse));
appEvents.emit(AppEvents.alertSuccess, ['Alert definition tested successfully']);
};
}
export function cleanUpDefinitionState(): ThunkResult<void> {
return (dispatch) => {
dispatch(cleanUpState(undefined));
};
}
async function buildAlertDefinition(state: AlertDefinitionState) {
const queryOptions = {} as QueryGroupOptions;
const currentAlertDefinition = state.alertDefinition;
const defaultDataSource = await getDataSourceSrv().get(null);
return {
...currentAlertDefinition,
data: buildDataQueryModel(queryOptions, defaultDataSource),
};
}
function handleJSONResponse(frames: DataFrameJSON[]) {
const dataFrames = frames.map((instance) => {
return dataFrameFromJSON(instance);
});
return applyFieldOverrides({
data: dataFrames,
fieldConfig: {
defaults: {},
overrides: [],
},
replaceVariables: (value: any) => value,
theme: config.theme2,
});
}
function buildDataQueryModel(queryOptions: QueryGroupOptions, defaultDataSource: DataSourceApi) {
return queryOptions.queries.map((query: DataQuery) => {
if (isExpressionQuery(query)) {
const dataSource: QueryGroupDataSource = {
name: ExpressionDatasourceID,
uid: ExpressionDatasourceID,
};
return {
model: {
...query,
type: query.type,
datasource: dataSource.name,
datasourceUid: dataSource.uid,
},
refId: query.refId,
};
}
const dataSourceSetting = getDataSourceSrv().getInstanceSettings(query.datasource);
const dataSource: QueryGroupDataSource = {
name: dataSourceSetting?.name ?? defaultDataSource.name,
uid: dataSourceSetting?.uid ?? defaultDataSource.uid,
};
return {
model: {
...query,
type: query.queryType,
datasource: dataSource.name,
datasourceUid: dataSource.uid,
},
refId: query.refId,
relativeTimeRange: {
from: 600,
to: 0,
},
};
});
}

View File

@ -1,11 +1,7 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { DataFrame, dateTime } from '@grafana/data';
import { dateTime } from '@grafana/data';
import alertDef from './alertDef';
import {
AlertDefinition,
AlertDefinitionDTO,
AlertDefinitionState,
AlertDefinitionUiState,
AlertRule,
AlertRuleDTO,
AlertRulesState,
@ -13,12 +9,8 @@ import {
NotificationChannelState,
NotifierDTO,
} from 'app/types';
import store from 'app/core/store';
import unifiedAlertingReducer from '../unified/state/reducers';
export const ALERT_DEFINITION_UI_STATE_STORAGE_KEY = 'grafana.alerting.alertDefinition.ui';
const DEFAULT_ALERT_DEFINITION_UI_STATE: AlertDefinitionUiState = { rightPaneSize: 400, topPaneSize: 0.45 };
export const initialState: AlertRulesState = {
items: [],
searchQuery: '',
@ -31,24 +23,6 @@ export const initialChannelState: NotificationChannelState = {
notifiers: [],
};
export const initialAlertDefinitionState: AlertDefinitionState = {
alertDefinition: {
id: 0,
uid: '',
title: '',
description: '',
condition: '',
data: [],
intervalSeconds: 60,
},
uiState: { ...store.getObject(ALERT_DEFINITION_UI_STATE_STORAGE_KEY, DEFAULT_ALERT_DEFINITION_UI_STATE) },
data: [],
alertDefinitions: [] as AlertDefinition[],
/* These are functions as they are mutated later on and redux toolkit will Object.freeze state so
* we need to store these using functions instead */
getInstances: () => [] as DataFrame[],
};
function convertToAlertRule(dto: AlertRuleDTO, state: string): AlertRule {
const stateModel = alertDef.getStateDisplayModel(state);
@ -135,41 +109,6 @@ const notificationChannelSlice = createSlice({
},
});
const alertDefinitionSlice = createSlice({
name: 'alertDefinition',
initialState: initialAlertDefinitionState,
reducers: {
setAlertDefinition: (state: AlertDefinitionState, action: PayloadAction<AlertDefinitionDTO>) => {
state.alertDefinition.title = action.payload.title;
state.alertDefinition.id = action.payload.id;
state.alertDefinition.uid = action.payload.uid;
state.alertDefinition.condition = action.payload.condition;
state.alertDefinition.intervalSeconds = action.payload.intervalSeconds;
state.alertDefinition.data = action.payload.data;
state.alertDefinition.description = action.payload.description;
},
updateAlertDefinitionOptions: (state: AlertDefinitionState, action: PayloadAction<Partial<AlertDefinition>>) => {
state.alertDefinition = { ...state.alertDefinition, ...action.payload };
},
setUiState: (state: AlertDefinitionState, action: PayloadAction<AlertDefinitionUiState>) => {
state.uiState = { ...state.uiState, ...action.payload };
},
setAlertDefinitions: (state: AlertDefinitionState, action: PayloadAction<AlertDefinition[]>) => {
state.alertDefinitions = action.payload;
},
setInstanceData: (state: AlertDefinitionState, action: PayloadAction<DataFrame[]>) => {
state.getInstances = () => action.payload;
},
cleanUpState: (state: AlertDefinitionState, action: PayloadAction<undefined>) => {
state.alertDefinitions = initialAlertDefinitionState.alertDefinitions;
state.alertDefinition = initialAlertDefinitionState.alertDefinition;
state.data = initialAlertDefinitionState.data;
state.getInstances = initialAlertDefinitionState.getInstances;
state.uiState = initialAlertDefinitionState.uiState;
},
},
});
export const { loadAlertRules, loadedAlertRules, setSearchQuery } = alertRulesSlice.actions;
export const {
@ -178,23 +117,12 @@ export const {
resetSecureField,
} = notificationChannelSlice.actions;
export const {
setUiState,
updateAlertDefinitionOptions,
setAlertDefinitions,
setAlertDefinition,
setInstanceData,
cleanUpState,
} = alertDefinitionSlice.actions;
export const alertRulesReducer = alertRulesSlice.reducer;
export const notificationChannelReducer = notificationChannelSlice.reducer;
export const alertDefinitionsReducer = alertDefinitionSlice.reducer;
export default {
alertRules: alertRulesReducer,
notificationChannel: notificationChannelReducer,
alertDefinition: alertDefinitionsReducer,
unifiedAlerting: unifiedAlertingReducer,
};

View File

@ -1,27 +1,13 @@
import { AlertDefinition, AlertRule, AlertRulesState, NotificationChannelState, StoreState } from 'app/types';
import { config } from '@grafana/runtime';
import { AlertRule, AlertRulesState, NotificationChannelState, StoreState } from 'app/types';
export const getSearchQuery = (state: AlertRulesState) => state.searchQuery;
export const getAlertRuleItems = (state: StoreState) => {
export const getAlertRuleItems = (state: StoreState): AlertRule[] => {
const regex = new RegExp(state.alertRules.searchQuery, 'i');
const result: Array<AlertRule | AlertDefinition> = [];
result.push(
...state.alertRules.items.filter((item) => {
return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info!);
})
);
if (config.featureToggles.ngalert) {
result.push(
...state.alertDefinition.alertDefinitions.filter((item) => {
return regex.test(item.title);
})
);
}
return result;
return state.alertRules.items.filter((item) => {
return regex.test(item.name) || regex.test(item.stateText) || regex.test(item.info!);
});
};
export const getNotificationChannel = (state: NotificationChannelState, channelId: number) => {

View File

@ -439,13 +439,6 @@ export function getAppRoutes(): RouteDescriptor[] {
() => import(/* webpackChunkName: "AlertingRuleForm"*/ 'app/features/alerting/unified/RuleEditor')
),
},
{
path: '/alerting/ng/new',
pageClass: 'page-alerting',
component: SafeDynamicImport(
() => import(/* webpackChunkName: "NgAlertingPage"*/ 'app/features/alerting/NextGenAlertingPage')
),
},
{
path: '/alerting/:id/edit',
pageClass: 'page-alerting',
@ -453,13 +446,6 @@ export function getAppRoutes(): RouteDescriptor[] {
() => import(/* webpackChunkName: "AlertingRuleForm"*/ 'app/features/alerting/unified/RuleEditor')
),
},
{
path: '/alerting/ng/:id/edit',
pageClass: 'page-alerting',
component: SafeDynamicImport(
() => import(/* webpackChunkName: "NgAlertingPage"*/ 'app/features/alerting/NextGenAlertingPage')
),
},
{
path: '/playlists',
component: SafeDynamicImport(

View File

@ -1,5 +1,4 @@
import { DataFrame, DataQuery, PanelData, SelectableValue, TimeRange } from '@grafana/data';
import { ExpressionQuery } from '../features/expressions/types';
import { SelectableValue } from '@grafana/data';
export interface AlertRuleDTO {
id: number;
@ -155,42 +154,6 @@ export interface AlertNotification {
type: string;
}
export interface AlertDefinitionState {
uiState: AlertDefinitionUiState;
alertDefinition: AlertDefinition;
data: PanelData[];
alertDefinitions: AlertDefinition[];
getInstances: () => DataFrame[];
}
export interface AlertDefinition {
id: number;
uid: string;
title: string;
description: string;
condition: string;
data: any[];
intervalSeconds: number;
}
export interface AlertDefinitionDTO extends AlertDefinition {
queryType: string;
refId: string;
relativeTimeRange: TimeRange;
orgId: number;
updated: string;
version: number;
}
export interface AlertDefinitionQueryModel {
model: DataQuery | ExpressionQuery;
}
export interface AlertDefinitionUiState {
rightPaneSize: number;
topPaneSize: number;
}
export interface AnnotationItemDTO {
id: number;
alertId: number;

View File

@ -1,7 +1,7 @@
import { ThunkAction, ThunkDispatch as GenericThunkDispatch } from 'redux-thunk';
import { PayloadAction } from '@reduxjs/toolkit';
import { NavIndex } from '@grafana/data';
import { AlertDefinitionState, AlertRulesState, NotificationChannelState } from './alerting';
import { AlertRulesState, NotificationChannelState } from './alerting';
import { UnifiedAlertingState } from '../features/alerting/unified/state/reducers';
import { TeamsState, TeamState } from './teams';
import { FolderState } from './folders';
@ -41,7 +41,6 @@ export interface StoreState {
templating: TemplatingState;
importDashboard: ImportDashboardState;
notificationChannel: NotificationChannelState;
alertDefinition: AlertDefinitionState;
unifiedAlerting: UnifiedAlertingState;
}