import { css } from '@emotion/css'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useObservable } from 'react-use'; import { GrafanaTheme2, LoadingState, PanelData } from '@grafana/data'; import { Alert, Button, Icon, LoadingPlaceholder, PanelChromeLoadingIndicator, useStyles2, VerticalGroup, withErrorBoundary, } from '@grafana/ui'; import { GrafanaRouteComponentProps } from 'app/core/navigation/types'; import { AlertQuery } from '../../../types/unified-alerting-dto'; import { AlertLabels } from './components/AlertLabels'; import { DetailsField } from './components/DetailsField'; import { RuleViewerLayout, RuleViewerLayoutContent } from './components/rule-viewer/RuleViewerLayout'; import { RuleViewerVisualization } from './components/rule-viewer/RuleViewerVisualization'; import { RuleDetailsActionButtons } from './components/rules/RuleDetailsActionButtons'; import { RuleDetailsAnnotations } from './components/rules/RuleDetailsAnnotations'; import { RuleDetailsDataSources } from './components/rules/RuleDetailsDataSources'; import { RuleDetailsExpression } from './components/rules/RuleDetailsExpression'; import { RuleDetailsFederatedSources } from './components/rules/RuleDetailsFederatedSources'; import { RuleDetailsMatchingInstances } from './components/rules/RuleDetailsMatchingInstances'; import { RuleHealth } from './components/rules/RuleHealth'; import { RuleState } from './components/rules/RuleState'; import { useAlertQueriesStatus } from './hooks/useAlertQueriesStatus'; import { useCombinedRule } from './hooks/useCombinedRule'; import { AlertingQueryRunner } from './state/AlertingQueryRunner'; import { getRulesSourceByName } from './utils/datasource'; import { alertRuleToQueries } from './utils/query'; import * as ruleId from './utils/rule-id'; import { isFederatedRuleGroup } from './utils/rules'; 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 } = match.params; const identifier = ruleId.tryParse(id, true); const { loading, error, result: rule } = useCombinedRule(identifier, identifier?.ruleSourceName); const runner = useMemo(() => new AlertingQueryRunner(), []); const data = useObservable(runner.get()); const queries2 = useMemo(() => alertRuleToQueries(rule), [rule]); const [queries, setQueries] = useState([]); const { allDataSourcesAvailable } = useAlertQueriesStatus(queries2); const onRunQueries = useCallback(() => { if (queries.length > 0 && allDataSourcesAvailable) { runner.run(queries); } }, [queries, runner, allDataSourcesAvailable]); useEffect(() => { setQueries(queries2); }, [queries2]); useEffect(() => { if (allDataSourcesAvailable) { onRunQueries(); } }, [onRunQueries, allDataSourcesAvailable]); 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 (!identifier?.ruleSourceName) { return (
{errorMessage}
); } const rulesSource = getRulesSourceByName(identifier.ruleSourceName); 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()); const isFederatedRule = isFederatedRuleGroup(rule.group); return ( {isFederatedRule && ( Federated rule groups are currently an experimental feature. )}

{rule.name}

{rule.promRule && ( )} {!!rule.labels && !!Object.keys(rule.labels).length && ( )}
{isFederatedRule && } {`${rule.namespace.name} / ${rule.group.name}`}
{!isFederatedRule && data && Object.keys(data).length > 0 && ( <>
Query results runner.cancel()} />
{queries.map((query) => { return (
); })}
)} {!isFederatedRule && !allDataSourcesAvailable && ( Cannot display the query preview. Some of the data sources used in the queries are not available. )}
); } 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)}; `, queryWarning: css` margin: ${theme.spacing(4, 0)}; `, details: css` display: flex; flex-direction: row; `, leftSide: css` flex: 1; `, rightSide: css` padding-left: 90px; width: 300px; `, }; }; export default withErrorBoundary(RuleViewer, { style: 'page' });