1import React, { useCallback, useState } from 'react'; 2import { css } from '@emotion/css'; 3import { useFormContext } from 'react-hook-form'; 4import { takeWhile } from 'rxjs/operators'; 5import { useMountedState } from 'react-use'; 6import { Button, HorizontalGroup, useStyles2 } from '@grafana/ui'; 7import { dateTimeFormatISO, GrafanaTheme2, LoadingState } from '@grafana/data'; 8import { RuleFormType } from '../../types/rule-form'; 9import { PreviewRuleRequest, PreviewRuleResponse } from '../../types/preview'; 10import { previewAlertRule } from '../../api/preview'; 11import { PreviewRuleResult } from './PreviewRuleResult'; 12 13const fields: string[] = ['type', 'dataSourceName', 'condition', 'queries', 'expression']; 14 15export function PreviewRule(): React.ReactElement | null { 16 const styles = useStyles2(getStyles); 17 const [preview, onPreview] = usePreview(); 18 const { watch } = useFormContext(); 19 const [type, condition] = watch(['type', 'condition']); 20 21 if (type === RuleFormType.cloudRecording || type === RuleFormType.cloudAlerting) { 22 return null; 23 } 24 25 return ( 26 <div className={styles.container}> 27 <HorizontalGroup> 28 <Button disabled={!condition} type="button" variant="primary" onClick={onPreview}> 29 Preview alerts 30 </Button> 31 </HorizontalGroup> 32 <PreviewRuleResult preview={preview} /> 33 </div> 34 ); 35} 36 37function usePreview(): [PreviewRuleResponse | undefined, () => void] { 38 const [preview, setPreview] = useState<PreviewRuleResponse | undefined>(); 39 const { getValues } = useFormContext(); 40 const isMounted = useMountedState(); 41 42 const onPreview = useCallback(() => { 43 const values = getValues(fields); 44 const request = createPreviewRequest(values); 45 46 previewAlertRule(request) 47 .pipe(takeWhile((response) => !isCompleted(response), true)) 48 .subscribe((response) => { 49 if (!isMounted()) { 50 return; 51 } 52 setPreview(response); 53 }); 54 }, [getValues, isMounted]); 55 56 return [preview, onPreview]; 57} 58 59function createPreviewRequest(values: any[]): PreviewRuleRequest { 60 const [type, dataSourceName, condition, queries, expression] = values; 61 62 switch (type) { 63 case RuleFormType.cloudAlerting: 64 return { 65 dataSourceName, 66 expr: expression, 67 }; 68 69 case RuleFormType.grafana: 70 return { 71 grafana_condition: { 72 condition, 73 data: queries, 74 now: dateTimeFormatISO(Date.now()), 75 }, 76 }; 77 78 default: 79 throw new Error(`Alert type ${type} not supported by preview.`); 80 } 81} 82 83function isCompleted(response: PreviewRuleResponse): boolean { 84 switch (response.data.state) { 85 case LoadingState.Done: 86 case LoadingState.Error: 87 return true; 88 default: 89 return false; 90 } 91} 92 93function getStyles(theme: GrafanaTheme2) { 94 return { 95 container: css` 96 margin-top: ${theme.spacing(2)}; 97 max-width: ${theme.breakpoints.values.xxl}px; 98 `, 99 }; 100} 101