diff --git a/.betterer.results b/.betterer.results index f83e79923d8..1e927cde187 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1697,9 +1697,6 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], [0, 0, 0, "No untranslated strings. Wrap text with ", "11"] ], - "public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] - ], "public/app/features/alerting/unified/components/extensions/AlertInstanceExtensionPointMenu.tsx:5381": [ [0, 0, 0, "Do not re-export imported variable (\`app/features/explore/extensions/ToolbarExtensionPointMenu\`)", "0"] ], @@ -1935,8 +1932,7 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "14"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] ], "public/app/features/alerting/unified/components/rule-editor/NeedHelpInfo.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] @@ -2035,16 +2031,13 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] ], "public/app/features/alerting/unified/components/rule-editor/labels/LabelsFieldInForm.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"] + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"] ], "public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationPolicyMatchers.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], diff --git a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md index 79b8d8af77d..56ab6e024e2 100644 --- a/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md +++ b/docs/sources/alerting/alerting-rules/create-mimir-loki-managed-recording-rule.md @@ -114,7 +114,7 @@ For more information, refer to [Metrics and labels](https://prometheus.io/docs/c #### Define recording rule -Define a query to get the data you want to measure and set the condition. +Define a query to get the data you want to measure and set the recording rule output. 1. Select a data source. 1. From the **Options** dropdown, specify a time range. @@ -141,9 +141,9 @@ It does not support absolute time ranges: `2021-12-02 00:00:00 to 2021-12-05 23: 3. To add a recovery threshold, turn the **Custom recovery threshold** toggle on and fill in a value for when your recording rule should stop meeting the condition. - You can only add one recovery threshold in a query and it must be the recording rule condition. + You can only add one recovery threshold in a query and it must be the recording rule output. -4. Click **Set as recording rule condition** on the query or expression you want to set as your rule condition. +4. Click **Set as recording rule output** on the query or expression you want to set as your rule output. #### Set evaluation behavior diff --git a/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.test.tsx b/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.test.tsx index b1cdd614864..3b76b141690 100644 --- a/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.test.tsx +++ b/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.test.tsx @@ -1,16 +1,29 @@ -import { screen, render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; +import { FormProvider, useForm } from 'react-hook-form'; + +import { RuleFormValues } from '../../types/rule-form'; +import { getDefaultFormValues } from '../../utils/rule-form'; import { ExpressionStatusIndicator } from './ExpressionStatusIndicator'; +function FormWrapper({ isCondition }: { isCondition: boolean }) { + const formApi = useForm({ defaultValues: { ...getDefaultFormValues() } }); + + return ( + + + + ); +} describe('ExpressionStatusIndicator', () => { it('should render one element if condition', () => { - render(); + render(); expect(screen.getByText('Alert condition')).toBeInTheDocument(); }); it('should render one element if not condition', () => { - render(); + render(); expect(screen.queryByRole('button', { name: 'Alert condition' })).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: 'Set as alert condition' })).toBeInTheDocument(); diff --git a/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.tsx b/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.tsx index e147d477907..65a7cac211f 100644 --- a/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.tsx +++ b/public/app/features/alerting/unified/components/expressions/ExpressionStatusIndicator.tsx @@ -1,8 +1,12 @@ import { css } from '@emotion/css'; +import { useFormContext } from 'react-hook-form'; import { GrafanaTheme2 } from '@grafana/data'; import { Badge, clearButtonStyles, useStyles2 } from '@grafana/ui'; +import { RuleFormValues } from '../../types/rule-form'; +import { isGrafanaRecordingRuleByType } from '../../utils/rules'; + interface AlertConditionProps { isCondition?: boolean; onSetCondition?: () => void; @@ -10,9 +14,14 @@ interface AlertConditionProps { export const ExpressionStatusIndicator = ({ isCondition, onSetCondition }: AlertConditionProps) => { const styles = useStyles2(getStyles); + const { watch } = useFormContext(); + const type = watch('type'); + const isGrafanaRecordingRule = type ? isGrafanaRecordingRuleByType(type) : false; + const conditionText = isGrafanaRecordingRule ? 'Recording rule output' : 'Alert condition'; + const makeConditionText = isGrafanaRecordingRule ? 'Set as recording rule output' : 'Set as alert condition'; if (isCondition) { - return ; + return ; } else { return ( ); } diff --git a/public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.tsx b/public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.tsx index aea869c59bc..a8aeccbd94f 100644 --- a/public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/FolderAndGroup.tsx @@ -1,13 +1,14 @@ import { css } from '@emotion/css'; import { debounce, take, uniqueId } from 'lodash'; -import { useCallback, useMemo, useState } from 'react'; import * as React from 'react'; -import { FormProvider, useForm, useFormContext, Controller } from 'react-hook-form'; +import { useCallback, useMemo, useState } from 'react'; +import { Controller, FormProvider, useForm, useFormContext } from 'react-hook-form'; import { AppEvents, GrafanaTheme2, SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { AsyncSelect, Box, Button, Field, Input, Label, Modal, Stack, Text, useStyles2 } from '@grafana/ui'; import appEvents from 'app/core/app_events'; +import { t } from 'app/core/internationalization'; import { contextSrv } from 'app/core/services/context_srv'; import { createFolder } from 'app/features/manage-dashboards/state/actions'; import { AccessControlAction } from 'app/types'; @@ -17,7 +18,7 @@ import { alertRuleApi } from '../../api/alertRuleApi'; import { GRAFANA_RULER_CONFIG } from '../../api/featureDiscoveryApi'; import { RuleFormValues } from '../../types/rule-form'; import { DEFAULT_GROUP_EVALUATION_INTERVAL } from '../../utils/rule-form'; -import { isGrafanaRulerRule } from '../../utils/rules'; +import { isGrafanaRecordingRuleByType, isGrafanaRulerRule } from '../../utils/rules'; import { ProvisioningBadge } from '../Provisioning'; import { evaluateEveryValidationOptions } from '../rules/EditRuleGroupModal'; @@ -98,8 +99,8 @@ export function FolderAndGroup({ const styles = useStyles2(getStyles); - const folder = watch('folder'); - const group = watch('group'); + const [folder, group, type] = watch(['folder', 'group', 'type']); + const isGrafanaRecordingRule = type ? isGrafanaRecordingRuleByType(type) : false; const { groupOptions, loading } = useFolderGroupOptions(folder?.uid ?? '', enableProvisionedGroups); @@ -139,6 +140,10 @@ export function FolderAndGroup({ const defaultGroupValue = group ? { value: group, label: group } : undefined; + const evaluationDesc = isGrafanaRecordingRule + ? t('alerting.folderAndGroup.evaluation.text.recording', 'Define how often the recording rule is evaluated.') + : t('alerting.folderAndGroup.evaluation.text.alerting', 'Define how often the alert rule is evaluated.'); + return (
@@ -207,7 +212,7 @@ export function FolderAndGroup({ g.name === groupName)?.rules) ?? []; @@ -380,6 +386,16 @@ function EvaluationGroupCreationModal({ setValue('evaluateEvery', interval, { shouldValidate: true }); }; + const modalTitle = isGrafanaRecordingRule + ? t( + 'alerting.folderAndGroup.evaluation.modal.text.recording', + 'Create a new evaluation group to use for this recording rule.' + ) + : t( + 'alerting.folderAndGroup.evaluation.modal.text.alerting', + 'Create a new evaluation group to use for this alert rule.' + ); + return ( -
Create a new evaluation group to use for this alert rule.
+
{modalTitle}
onSubmit())}> diff --git a/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx b/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx index 0cd1cb939f0..24f13f06bbc 100644 --- a/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/GrafanaEvaluationBehavior.tsx @@ -5,7 +5,7 @@ import { Controller, RegisterOptions, useFormContext } from 'react-hook-form'; import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Field, Icon, IconButton, Input, Label, Stack, Switch, Text, Tooltip, useStyles2 } from '@grafana/ui'; import { Trans, t } from 'app/core/internationalization'; -import { isGrafanaAlertingRuleByType } from 'app/features/alerting/unified/utils/rules'; +import { isGrafanaAlertingRuleByType, isGrafanaRecordingRuleByType } from 'app/features/alerting/unified/utils/rules'; import { CombinedRuleGroup, CombinedRuleNamespace } from '../../../../../types/unified-alerting'; import { LogMessages, logInfo } from '../../Analytics'; @@ -231,15 +231,21 @@ function NeedHelpInfoForConfigureNoDataError() { ); } -function getDescription() { +function getDescription(isGrafanaRecordingRule: boolean) { const docsLink = 'https://grafana.com/docs/grafana/latest/alerting/fundamentals/alert-rules/rule-evaluation/'; return ( - - Define how the alert rule is evaluated. - + {isGrafanaRecordingRule ? ( + + Define how the recording rule is evaluated. + + ) : ( + + Define how the alert rule is evaluated. + + )} + diff --git a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx index 3a780463010..baff0818378 100644 --- a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx @@ -73,9 +73,9 @@ export const NotificationsStep = ({ alertUid }: NotificationsStepProps) => { title={isRecordingRuleByType(type) ? 'Add labels' : 'Configure labels and notifications'} description={ - {type === RuleFormType.cloudRecording ? ( + {isRecordingRuleByType(type) ? ( - Add labels to help you better manage your recording rules + Add labels to help you better manage your recording rules. ) : ( shouldAllowSimplifiedRouting && ( diff --git a/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.tsx b/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.tsx index 1b2d0ae849d..604f7f69157 100644 --- a/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/labels/LabelsField.tsx @@ -4,12 +4,14 @@ import { Controller, FormProvider, useFieldArray, useForm, useFormContext } from import { GrafanaTheme2, SelectableValue } from '@grafana/data'; import { Button, Field, InlineLabel, Input, LoadingPlaceholder, Space, Stack, Text, useStyles2 } from '@grafana/ui'; +import { t } from 'app/core/internationalization'; import { labelsApi } from '../../../api/labelsApi'; import { usePluginBridge } from '../../../hooks/usePluginBridge'; import { SupportedPlugin } from '../../../types/pluginBridges'; -import { RuleFormValues } from '../../../types/rule-form'; +import { RuleFormType, RuleFormValues } from '../../../types/rule-form'; import { isPrivateLabelKey } from '../../../utils/labels'; +import { isRecordingRuleByType } from '../../../utils/rules'; import AlertLabelDropdown from '../../AlertLabelDropdown'; import { AlertLabels } from '../../AlertLabels'; import { NeedHelpInfo } from '../NeedHelpInfo'; @@ -64,6 +66,8 @@ export interface LabelsSubFormProps { export function LabelsSubForm({ dataSourceName, onClose, initialLabels }: LabelsSubFormProps) { const styles = useStyles2(getStyles); + const { watch } = useFormContext(); + const type = watch('type') ?? RuleFormType.grafana; const onSave = (labels: LabelsSubformValues) => { onClose(labels.labelsInSubform); @@ -81,7 +85,7 @@ export function LabelsSubForm({ dataSourceName, onClose, initialLabels }: Labels - Add labels to your rule for searching, silencing, or routing to a notification policy. + {getLabelText(type)} @@ -393,13 +397,16 @@ export const LabelsWithoutSuggestions: FC = () => { }; function LabelsField() { + const { watch } = useFormContext(); + const type = watch('type') ?? RuleFormType.grafana; + return (
Labels - Add labels to your rule for searching, silencing, or routing to a notification policy. + {getLabelText(type)} Labels - Add labels to your rule for searching, silencing, or routing to a notification policy. + {text}