Alerting: Refactor AlertRuleForm and fix annotations step description for cloud rules (#74168)

Refactor AlertRuleForm and fix annotations step description for cloud rules
This commit is contained in:
Sonia Aguilar 2023-08-31 16:51:06 +02:00 committed by GitHub
parent 34a831c83e
commit 2a2689a7c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 132 deletions

View File

@ -9,7 +9,7 @@ import { Button, Card, Modal, RadioButtonGroup, useStyles2 } from '@grafana/ui';
import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
import { KeyValueField } from '../../../api/templateApi';
import AnnotationsField from '../../rule-editor/AnnotationsField';
import AnnotationsStep from '../../rule-editor/AnnotationsStep';
import LabelsField from '../../rule-editor/LabelsField';
interface Props {
@ -99,7 +99,7 @@ export const GenerateAlertDataModal = ({ isOpen, onDismiss, onAccept }: Props) =
<Card>
<Stack direction="column" gap={1}>
<div className={styles.section}>
<AnnotationsField />
<AnnotationsStep />
</div>
<div className={styles.section}>
<LabelsField />

View File

@ -1,14 +1,14 @@
import { css } from '@emotion/css';
import React, { useState } from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import { FormProvider, useForm } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
import { Modal, Button, Label, useStyles2, RadioButtonGroup } from '@grafana/ui';
import { Button, Label, Modal, RadioButtonGroup, useStyles2 } from '@grafana/ui';
import { TestReceiversAlert } from 'app/plugins/datasource/alertmanager/types';
import { Annotations, Labels } from 'app/types/unified-alerting-dto';
import { defaultAnnotations } from '../../../utils/constants';
import AnnotationsField from '../../rule-editor/AnnotationsField';
import AnnotationsStep from '../../rule-editor/AnnotationsStep';
import LabelsField from '../../rule-editor/LabelsField';
interface Props {
@ -90,7 +90,7 @@ export const TestContactPointModal = ({ isOpen, onDismiss, onTest }: Props) => {
use custom templates and messages.
</div>
<div className={styles.section}>
<AnnotationsField />
<AnnotationsStep />
</div>
<div className={styles.section}>
<LabelsField />

View File

@ -30,11 +30,12 @@ import {
} from '../../utils/rule-form';
import * as ruleId from '../../utils/rule-id';
import AnnotationsStep from './AnnotationsStep';
import { CloudEvaluationBehavior } from './CloudEvaluationBehavior';
import { DetailsStep } from './DetailsStep';
import { GrafanaEvaluationBehavior } from './GrafanaEvaluationBehavior';
import { GrafanaRuleInspector } from './GrafanaRuleInspector';
import { NotificationsStep } from './NotificationsStep';
import { RecordingRulesNameSpaceAndGroupStep } from './RecordingRulesNameSpaceAndGroupStep';
import { RuleEditorSection } from './RuleEditorSection';
import { RuleInspector } from './RuleInspector';
import { QueryAndExpressionsStep } from './query-and-alert-condition/QueryAndExpressionsStep';
@ -130,7 +131,7 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
const type = watch('type');
const dataSourceName = watch('dataSourceName');
const showStep2 = Boolean(type && (type === RuleFormType.grafana || !!dataSourceName));
const showDataSourceDependantStep = Boolean(type && (type === RuleFormType.grafana || !!dataSourceName));
const submitState = useUnifiedAlertingSelector((state) => state.ruleForm.saveRule) || initialAsyncRequestState;
useCleanup((state) => (state.unifiedAlerting.ruleForm.saveRule = initialAsyncRequestState));
@ -254,20 +255,30 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
<div className={styles.contentOuter}>
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
<div className={styles.contentInner}>
{/* Step 1 */}
<AlertRuleNameInput />
{/* Step 2 */}
<QueryAndExpressionsStep editingExistingRule={!!existing} onDataChange={checkAlertCondition} />
{showStep2 && (
{/* Step 3-4-5 */}
{showDataSourceDependantStep && (
<>
{type === RuleFormType.grafana ? (
{/* Step 3 */}
{type === RuleFormType.grafana && (
<GrafanaEvaluationBehavior
evaluateEvery={evaluateEvery}
setEvaluateEvery={setEvaluateEvery}
existing={Boolean(existing)}
/>
) : (
<CloudEvaluationBehavior />
)}
<DetailsStep />
{type === RuleFormType.cloudAlerting && <CloudEvaluationBehavior />}
{type === RuleFormType.cloudRecording && <RecordingRulesNameSpaceAndGroupStep />}
{/* Step 4 & 5 */}
{/* Annotations only for cloud and Grafana */}
{type !== RuleFormType.cloudRecording && <AnnotationsStep />}
{/* Notifications step*/}
<NotificationsStep alertUid={uidFromParams} />
</>
)}

View File

@ -21,7 +21,7 @@ import { getDefaultFormValues } from '../../utils/rule-form';
import 'whatwg-fetch';
import AnnotationsField from './AnnotationsField';
import AnnotationsStep from './AnnotationsStep';
// To get anything displayed inside the Autosize component we need to mock it
// Ref https://github.com/bvaughn/react-window/issues/454#issuecomment-646031139
@ -65,7 +65,7 @@ function FormWrapper({ formValues }: { formValues?: Partial<RuleFormValues> }) {
return (
<TestProvider store={store}>
<FormProvider {...formApi}>
<AnnotationsField />
<AnnotationsStep />
</FormProvider>
</TestProvider>
);

View File

@ -16,8 +16,10 @@ import { Annotation, annotationLabels } from '../../utils/constants';
import AnnotationHeaderField from './AnnotationHeaderField';
import DashboardAnnotationField from './DashboardAnnotationField';
import { DashboardPicker, PanelDTO } from './DashboardPicker';
import { NeedHelpInfo } from './NeedHelpInfo';
import { RuleEditorSection } from './RuleEditorSection';
const AnnotationsField = () => {
const AnnotationsStep = () => {
const styles = useStyles2(getStyles);
const [showPanelSelector, setShowPanelSelector] = useToggle(false);
@ -90,8 +92,27 @@ const AnnotationsField = () => {
setShowPanelSelector(true);
};
function getAnnotationsSectionDescription() {
const docsLink =
'https://grafana.com/docs/grafana/latest/alerting/fundamentals/annotation-label/variables-label-annotation';
return (
<Stack gap={0.5}>
Add annotations to provide more context in your alert notifications.
<NeedHelpInfo
contentText={`Annotations add metadata to provide more information on the alert in your alert notifications.
For example, add a Summary annotation to tell you which value caused the alert to fire or which server it happened on.
Annotations can contain a combination of text and template code.`}
externalLink={docsLink}
linkText={`Read about annotations`}
title="Annotations"
/>
</Stack>
);
}
return (
<>
<RuleEditorSection stepNo={4} title="Add annotations" description={getAnnotationsSectionDescription()}>
<div className={styles.flexColumn}>
{fields.map((annotationField, index: number) => {
const isUrl = annotations[index]?.key?.toLocaleLowerCase().endsWith('url');
@ -186,7 +207,7 @@ const AnnotationsField = () => {
/>
)}
</div>
</>
</RuleEditorSection>
);
};
@ -242,4 +263,4 @@ const getStyles = (theme: GrafanaTheme2) => ({
`,
});
export default AnnotationsField;
export default AnnotationsStep;

View File

@ -24,11 +24,6 @@ export const CloudEvaluationBehavior = () => {
const type = watch('type');
const dataSourceName = watch('dataSourceName');
// cloud recording rules do not have alert conditions
if (type === RuleFormType.cloudRecording) {
return null;
}
return (
<RuleEditorSection stepNo={3} title="Set alert evaluation behavior">
<Field

View File

@ -1,108 +0,0 @@
import { css } from '@emotion/css';
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { GrafanaTheme2 } from '@grafana/data';
import { Stack } from '@grafana/experimental';
import { useStyles2 } from '@grafana/ui';
import { RuleFormType, RuleFormValues } from '../../types/rule-form';
import AnnotationsField from './AnnotationsField';
import { GroupAndNamespaceFields } from './GroupAndNamespaceFields';
import { NeedHelpInfo } from './NeedHelpInfo';
import { RuleEditorSection } from './RuleEditorSection';
function getDescription(ruleType: RuleFormType | undefined, styles: { [key: string]: string }) {
const annotationsText = 'Add annotations to provide more context in your alert notifications.';
if (ruleType === RuleFormType.cloudRecording) {
return 'Select the Namespace and Group for your recording rule.';
}
const docsLink =
'https://grafana.com/docs/grafana/latest/alerting/fundamentals/annotation-label/variables-label-annotation';
const textToRender =
ruleType === RuleFormType.grafana
? ` ${annotationsText} `
: ruleType === RuleFormType.cloudAlerting
? `Select the Namespace and evaluation group for your alert. ${annotationsText} `
: '';
return (
<Stack gap={0.5}>
{`${textToRender}`}
<NeedHelpInfo
contentText={`Annotations add metadata to provide more information on the alert in your alert notifications.
For example, add a Summary annotation to tell you which value caused the alert to fire or which server it happened on.
Annotations can contain a combination of text and template code.`}
externalLink={docsLink}
linkText={`Read about annotations`}
title="Annotations"
/>
</Stack>
);
}
export function DetailsStep() {
const { watch } = useFormContext<RuleFormValues & { location?: string }>();
const styles = useStyles2(getStyles);
const ruleFormType = watch('type');
const dataSourceName = watch('dataSourceName');
const type = watch('type');
return (
<RuleEditorSection
stepNo={type === RuleFormType.cloudRecording ? 3 : 4}
title={type === RuleFormType.cloudRecording ? 'Add namespace and group' : 'Add annotations'}
description={getDescription(type, styles)}
>
{ruleFormType === RuleFormType.cloudRecording && dataSourceName && (
<GroupAndNamespaceFields rulesSourceName={dataSourceName} />
)}
{type !== RuleFormType.cloudRecording && <AnnotationsField />}
</RuleEditorSection>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
needHelpText: css`
color: ${theme.colors.text.primary};
font-size: ${theme.typography.size.sm};
margin-bottom: ${theme.spacing(0.5)};
cursor: pointer;
text-underline-position: under;
`,
needHelpTooltip: css`
max-width: 300px;
font-size: ${theme.typography.size.sm};
margin-left: 5px;
div {
margin-top: 5px;
margin-bottom: 5px;
}
`,
tooltipHeader: css`
color: ${theme.colors.text.primary};
font-weight: bold;
`,
tooltipLink: css`
color: ${theme.colors.text.link};
cursor: pointer;
&:hover {
text-decoration: underline;
}
`,
underline: css`
text-decoration: underline;
`,
});

View File

@ -0,0 +1,27 @@
import React from 'react';
import { useFormContext } from 'react-hook-form';
import { RuleFormValues } from '../../types/rule-form';
import { GroupAndNamespaceFields } from './GroupAndNamespaceFields';
import { RuleEditorSection } from './RuleEditorSection';
export function RecordingRulesNameSpaceAndGroupStep() {
const { watch } = useFormContext<RuleFormValues & { location?: string }>();
const dataSourceName = watch('dataSourceName');
if (!dataSourceName) {
return null;
}
return (
<RuleEditorSection
stepNo={3}
title={'Add namespace and group'}
description="Select the Namespace and Group for your recording rule."
>
<GroupAndNamespaceFields rulesSourceName={dataSourceName} />
</RuleEditorSection>
);
}