mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: added possibility to preview grafana managed alert rules. (#34600)
* starting to add eval logic. * wip * first version of test rule. * reverted file. * add info colum to result to show error or (with CC evalmatches) * fix labels in evalmatch * fix be test * refactored using observables. * moved widht/height div to outside panel rendere. * adding docs api level. * adding container styles to error div. * increasing size of preview. Co-authored-by: kyle <kyle@grafana.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import { timeOptions, timeValidationPattern } from '../../utils/time';
|
||||
import { ConditionField } from './ConditionField';
|
||||
import { GrafanaAlertStatePicker } from './GrafanaAlertStatePicker';
|
||||
import { RuleEditorSection } from './RuleEditorSection';
|
||||
import { PreviewRule } from './PreviewRule';
|
||||
|
||||
const MIN_TIME_RANGE_STEP_S = 10; // 10 seconds
|
||||
|
||||
@@ -142,6 +143,7 @@ export const ConditionsStep: FC = () => {
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
<PreviewRule />
|
||||
</RuleEditorSection>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { useFormContext } from 'react-hook-form';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
import { useMountedState } from 'react-use';
|
||||
import { Button, HorizontalGroup, useStyles2 } from '@grafana/ui';
|
||||
import { dateTimeFormatISO, GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { RuleFormType } from '../../types/rule-form';
|
||||
import { PreviewRuleRequest, PreviewRuleResponse } from '../../types/preview';
|
||||
import { previewAlertRule } from '../../api/preview';
|
||||
import { PreviewRuleResult } from './PreviewRuleResult';
|
||||
|
||||
const fields: string[] = ['type', 'dataSourceName', 'condition', 'queries', 'expression'];
|
||||
|
||||
export function PreviewRule(): React.ReactElement | null {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [preview, onPreview] = usePreview();
|
||||
const { getValues } = useFormContext();
|
||||
const [type] = getValues(fields);
|
||||
|
||||
if (type === RuleFormType.cloud) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<HorizontalGroup>
|
||||
<Button type="button" variant="primary" onClick={onPreview}>
|
||||
Preview your alert
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
<PreviewRuleResult preview={preview} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function usePreview(): [PreviewRuleResponse | undefined, () => void] {
|
||||
const [preview, setPreview] = useState<PreviewRuleResponse | undefined>();
|
||||
const { getValues } = useFormContext();
|
||||
const isMounted = useMountedState();
|
||||
|
||||
const onPreview = useCallback(() => {
|
||||
const values = getValues(fields);
|
||||
const request = createPreviewRequest(values);
|
||||
|
||||
previewAlertRule(request)
|
||||
.pipe(takeWhile((response) => !isCompleted(response), true))
|
||||
.subscribe((response) => {
|
||||
if (!isMounted()) {
|
||||
return;
|
||||
}
|
||||
setPreview(response);
|
||||
});
|
||||
}, [getValues, isMounted]);
|
||||
|
||||
return [preview, onPreview];
|
||||
}
|
||||
|
||||
function createPreviewRequest(values: any[]): PreviewRuleRequest {
|
||||
const [type, dataSourceName, condition, queries, expression] = values;
|
||||
|
||||
switch (type) {
|
||||
case RuleFormType.cloud:
|
||||
return {
|
||||
dataSourceName,
|
||||
expr: expression,
|
||||
};
|
||||
|
||||
case RuleFormType.grafana:
|
||||
return {
|
||||
grafana_condition: {
|
||||
condition,
|
||||
data: queries,
|
||||
now: dateTimeFormatISO(Date.now()),
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
throw new Error(`Alert type ${type} not supported by preview.`);
|
||||
}
|
||||
}
|
||||
|
||||
function isCompleted(response: PreviewRuleResponse): boolean {
|
||||
switch (response.data.state) {
|
||||
case LoadingState.Done:
|
||||
case LoadingState.Error:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
container: css`
|
||||
margin-top: ${theme.spacing(2)};
|
||||
`,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import React from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { PanelRenderer } from '@grafana/runtime';
|
||||
import { GrafanaTheme2, LoadingState } from '@grafana/data';
|
||||
import { PreviewRuleResponse } from '../../types/preview';
|
||||
import { RuleFormType } from '../../types/rule-form';
|
||||
|
||||
type Props = {
|
||||
preview: PreviewRuleResponse | undefined;
|
||||
};
|
||||
|
||||
export function PreviewRuleResult(props: Props): React.ReactElement | null {
|
||||
const { preview } = props;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
if (!preview) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { data, ruleType } = preview;
|
||||
|
||||
if (data.state === LoadingState.Loading) {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<span>Loading preview...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.state === LoadingState.Error) {
|
||||
return <div className={styles.container}>{data.error ?? 'Failed to preview alert rule'}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<span>
|
||||
Preview based on the result of running the query, for this moment.{' '}
|
||||
{ruleType === RuleFormType.grafana ? 'Configuration for `no data` and `error handling` is not applied.' : null}
|
||||
</span>
|
||||
<div className={styles.table}>
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width: `${width}px`, height: `${height}px` }}>
|
||||
<PanelRenderer title="" width={width} height={height} pluginId="table" data={data} />
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaTheme2) {
|
||||
return {
|
||||
container: css`
|
||||
margin: ${theme.spacing(2)} 0;
|
||||
`,
|
||||
table: css`
|
||||
flex: 1 1 auto;
|
||||
height: 135px;
|
||||
margin-top: ${theme.spacing(2)};
|
||||
border: 1px solid ${theme.colors.border.medium};
|
||||
border-radius: ${theme.shape.borderRadius(1)};
|
||||
`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user