import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useObservable } from 'react-use'; import { css } from '@emotion/css'; import { GrafanaTheme2, LoadingState, PanelData } from '@grafana/data'; import { withErrorBoundary, useStyles2, Alert, LoadingPlaceholder, PanelChromeLoadingIndicator, Icon, } from '@grafana/ui'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { AlertingQueryRunner } from './state/AlertingQueryRunner'; import { useCombinedRule } from './hooks/useCombinedRule'; import { alertRuleToQueries } from './utils/query'; import { RuleState } from './components/rules/RuleState'; import { getRulesSourceByName } from './utils/datasource'; import { DetailsField } from './components/DetailsField'; import { RuleHealth } from './components/rules/RuleHealth'; import { RuleViewerVisualization } from './components/rule-viewer/RuleViewerVisualization'; import { RuleDetailsActionButtons } from './components/rules/RuleDetailsActionButtons'; import { RuleDetailsMatchingInstances } from './components/rules/RuleDetailsMatchingInstances'; import { RuleDetailsDataSources } from './components/rules/RuleDetailsDataSources'; import { RuleViewerLayout, RuleViewerLayoutContent } from './components/rule-viewer/RuleViewerLayout'; import { AlertLabels } from './components/AlertLabels'; import { RuleDetailsExpression } from './components/rules/RuleDetailsExpression'; import { RuleDetailsAnnotations } from './components/rules/RuleDetailsAnnotations'; import * as ruleId from './utils/rule-id'; import { AlertQuery } from '../../../types/unified-alerting-dto'; type RuleViewerProps = GrafanaRouteComponentProps<{ id?: string; sourceName?: string }>; const errorMessage = 'Could not find data source for rule'; const errorTitle = 'Could not view rule'; const pageTitle = 'Alerting / View rule'; export function RuleViewer({ match }: RuleViewerProps) { const styles = useStyles2(getStyles); const { id, sourceName } = match.params; const identifier = ruleId.tryParse(id, true); const { loading, error, result: rule } = useCombinedRule(identifier, sourceName); const runner = useMemo(() => new AlertingQueryRunner(), []); const data = useObservable(runner.get()); const queries2 = useMemo(() => alertRuleToQueries(rule), [rule]); const [queries, setQueries] = useState([]); const onRunQueries = useCallback(() => { if (queries.length > 0) { runner.run(queries); } }, [queries, runner]); useEffect(() => { setQueries(queries2); }, [queries2]); useEffect(() => { onRunQueries(); }, [onRunQueries]); useEffect(() => { return () => runner.destroy(); }, [runner]); const onChangeQuery = useCallback((query: AlertQuery) => { setQueries((queries) => queries.map((q) => { if (q.refId === query.refId) { return query; } return q; }) ); }, []); if (!sourceName) { return (
{errorMessage}
); } const rulesSource = getRulesSourceByName(sourceName); if (loading) { return ( ); } if (error || !rulesSource) { return (
{error?.message ?? errorMessage}
{!!error?.stack && error.stack}
); } if (!rule) { return ( Rule could not be found. ); } const annotations = Object.entries(rule.annotations).filter(([_, value]) => !!value.trim()); return (

{rule.name}

{rule.promRule && ( )} {!!rule.labels && !!Object.keys(rule.labels).length && ( )}
{`${rule.namespace.name} / ${rule.group.name}`}
{data && Object.keys(data).length > 0 && ( <>
Query results runner.cancel()} />
{queries.map((query) => { return (
); })}
)}
); } function isLoading(data: Record): boolean { return !!Object.values(data).find((d) => d.state === LoadingState.Loading); } const getStyles = (theme: GrafanaTheme2) => { return { errorMessage: css` white-space: pre-wrap; `, queries: css` height: 100%; width: 100%; `, queriesTitle: css` padding: ${theme.spacing(2, 0.5)}; font-size: ${theme.typography.h5.fontSize}; font-weight: ${theme.typography.fontWeightBold}; font-family: ${theme.typography.h5.fontFamily}; `, query: css` border-bottom: 1px solid ${theme.colors.border.medium}; padding: ${theme.spacing(2)}; `, details: css` display: flex; flex-direction: row; `, leftSide: css` flex: 1; `, rightSide: css` padding-left: 90px; width: 300px; `, }; }; export default withErrorBoundary(RuleViewer, { style: 'page' });