mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
34a831c83e
commit
2a2689a7c2
@ -9,7 +9,7 @@ import { Button, Card, Modal, RadioButtonGroup, useStyles2 } from '@grafana/ui';
|
|||||||
import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
|
import { TestTemplateAlert } from 'app/plugins/datasource/alertmanager/types';
|
||||||
|
|
||||||
import { KeyValueField } from '../../../api/templateApi';
|
import { KeyValueField } from '../../../api/templateApi';
|
||||||
import AnnotationsField from '../../rule-editor/AnnotationsField';
|
import AnnotationsStep from '../../rule-editor/AnnotationsStep';
|
||||||
import LabelsField from '../../rule-editor/LabelsField';
|
import LabelsField from '../../rule-editor/LabelsField';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -99,7 +99,7 @@ export const GenerateAlertDataModal = ({ isOpen, onDismiss, onAccept }: Props) =
|
|||||||
<Card>
|
<Card>
|
||||||
<Stack direction="column" gap={1}>
|
<Stack direction="column" gap={1}>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<AnnotationsField />
|
<AnnotationsStep />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<LabelsField />
|
<LabelsField />
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useState } from 'react';
|
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 { 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 { TestReceiversAlert } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { Annotations, Labels } from 'app/types/unified-alerting-dto';
|
import { Annotations, Labels } from 'app/types/unified-alerting-dto';
|
||||||
|
|
||||||
import { defaultAnnotations } from '../../../utils/constants';
|
import { defaultAnnotations } from '../../../utils/constants';
|
||||||
import AnnotationsField from '../../rule-editor/AnnotationsField';
|
import AnnotationsStep from '../../rule-editor/AnnotationsStep';
|
||||||
import LabelsField from '../../rule-editor/LabelsField';
|
import LabelsField from '../../rule-editor/LabelsField';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -90,7 +90,7 @@ export const TestContactPointModal = ({ isOpen, onDismiss, onTest }: Props) => {
|
|||||||
use custom templates and messages.
|
use custom templates and messages.
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<AnnotationsField />
|
<AnnotationsStep />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
<LabelsField />
|
<LabelsField />
|
||||||
|
@ -30,11 +30,12 @@ import {
|
|||||||
} from '../../utils/rule-form';
|
} from '../../utils/rule-form';
|
||||||
import * as ruleId from '../../utils/rule-id';
|
import * as ruleId from '../../utils/rule-id';
|
||||||
|
|
||||||
|
import AnnotationsStep from './AnnotationsStep';
|
||||||
import { CloudEvaluationBehavior } from './CloudEvaluationBehavior';
|
import { CloudEvaluationBehavior } from './CloudEvaluationBehavior';
|
||||||
import { DetailsStep } from './DetailsStep';
|
|
||||||
import { GrafanaEvaluationBehavior } from './GrafanaEvaluationBehavior';
|
import { GrafanaEvaluationBehavior } from './GrafanaEvaluationBehavior';
|
||||||
import { GrafanaRuleInspector } from './GrafanaRuleInspector';
|
import { GrafanaRuleInspector } from './GrafanaRuleInspector';
|
||||||
import { NotificationsStep } from './NotificationsStep';
|
import { NotificationsStep } from './NotificationsStep';
|
||||||
|
import { RecordingRulesNameSpaceAndGroupStep } from './RecordingRulesNameSpaceAndGroupStep';
|
||||||
import { RuleEditorSection } from './RuleEditorSection';
|
import { RuleEditorSection } from './RuleEditorSection';
|
||||||
import { RuleInspector } from './RuleInspector';
|
import { RuleInspector } from './RuleInspector';
|
||||||
import { QueryAndExpressionsStep } from './query-and-alert-condition/QueryAndExpressionsStep';
|
import { QueryAndExpressionsStep } from './query-and-alert-condition/QueryAndExpressionsStep';
|
||||||
@ -130,7 +131,7 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
|||||||
const type = watch('type');
|
const type = watch('type');
|
||||||
const dataSourceName = watch('dataSourceName');
|
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;
|
const submitState = useUnifiedAlertingSelector((state) => state.ruleForm.saveRule) || initialAsyncRequestState;
|
||||||
useCleanup((state) => (state.unifiedAlerting.ruleForm.saveRule = initialAsyncRequestState));
|
useCleanup((state) => (state.unifiedAlerting.ruleForm.saveRule = initialAsyncRequestState));
|
||||||
@ -254,20 +255,30 @@ export const AlertRuleForm = ({ existing, prefill }: Props) => {
|
|||||||
<div className={styles.contentOuter}>
|
<div className={styles.contentOuter}>
|
||||||
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
|
<CustomScrollbar autoHeightMin="100%" hideHorizontalTrack={true}>
|
||||||
<div className={styles.contentInner}>
|
<div className={styles.contentInner}>
|
||||||
|
{/* Step 1 */}
|
||||||
<AlertRuleNameInput />
|
<AlertRuleNameInput />
|
||||||
|
{/* Step 2 */}
|
||||||
<QueryAndExpressionsStep editingExistingRule={!!existing} onDataChange={checkAlertCondition} />
|
<QueryAndExpressionsStep editingExistingRule={!!existing} onDataChange={checkAlertCondition} />
|
||||||
{showStep2 && (
|
{/* Step 3-4-5 */}
|
||||||
|
{showDataSourceDependantStep && (
|
||||||
<>
|
<>
|
||||||
{type === RuleFormType.grafana ? (
|
{/* Step 3 */}
|
||||||
|
{type === RuleFormType.grafana && (
|
||||||
<GrafanaEvaluationBehavior
|
<GrafanaEvaluationBehavior
|
||||||
evaluateEvery={evaluateEvery}
|
evaluateEvery={evaluateEvery}
|
||||||
setEvaluateEvery={setEvaluateEvery}
|
setEvaluateEvery={setEvaluateEvery}
|
||||||
existing={Boolean(existing)}
|
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} />
|
<NotificationsStep alertUid={uidFromParams} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -21,7 +21,7 @@ import { getDefaultFormValues } from '../../utils/rule-form';
|
|||||||
|
|
||||||
import 'whatwg-fetch';
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
import AnnotationsField from './AnnotationsField';
|
import AnnotationsStep from './AnnotationsStep';
|
||||||
|
|
||||||
// To get anything displayed inside the Autosize component we need to mock it
|
// To get anything displayed inside the Autosize component we need to mock it
|
||||||
// Ref https://github.com/bvaughn/react-window/issues/454#issuecomment-646031139
|
// Ref https://github.com/bvaughn/react-window/issues/454#issuecomment-646031139
|
||||||
@ -65,7 +65,7 @@ function FormWrapper({ formValues }: { formValues?: Partial<RuleFormValues> }) {
|
|||||||
return (
|
return (
|
||||||
<TestProvider store={store}>
|
<TestProvider store={store}>
|
||||||
<FormProvider {...formApi}>
|
<FormProvider {...formApi}>
|
||||||
<AnnotationsField />
|
<AnnotationsStep />
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
</TestProvider>
|
</TestProvider>
|
||||||
);
|
);
|
||||||
|
@ -16,8 +16,10 @@ import { Annotation, annotationLabels } from '../../utils/constants';
|
|||||||
import AnnotationHeaderField from './AnnotationHeaderField';
|
import AnnotationHeaderField from './AnnotationHeaderField';
|
||||||
import DashboardAnnotationField from './DashboardAnnotationField';
|
import DashboardAnnotationField from './DashboardAnnotationField';
|
||||||
import { DashboardPicker, PanelDTO } from './DashboardPicker';
|
import { DashboardPicker, PanelDTO } from './DashboardPicker';
|
||||||
|
import { NeedHelpInfo } from './NeedHelpInfo';
|
||||||
|
import { RuleEditorSection } from './RuleEditorSection';
|
||||||
|
|
||||||
const AnnotationsField = () => {
|
const AnnotationsStep = () => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [showPanelSelector, setShowPanelSelector] = useToggle(false);
|
const [showPanelSelector, setShowPanelSelector] = useToggle(false);
|
||||||
|
|
||||||
@ -90,8 +92,27 @@ const AnnotationsField = () => {
|
|||||||
setShowPanelSelector(true);
|
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 (
|
return (
|
||||||
<>
|
<RuleEditorSection stepNo={4} title="Add annotations" description={getAnnotationsSectionDescription()}>
|
||||||
<div className={styles.flexColumn}>
|
<div className={styles.flexColumn}>
|
||||||
{fields.map((annotationField, index: number) => {
|
{fields.map((annotationField, index: number) => {
|
||||||
const isUrl = annotations[index]?.key?.toLocaleLowerCase().endsWith('url');
|
const isUrl = annotations[index]?.key?.toLocaleLowerCase().endsWith('url');
|
||||||
@ -186,7 +207,7 @@ const AnnotationsField = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</RuleEditorSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -242,4 +263,4 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default AnnotationsField;
|
export default AnnotationsStep;
|
@ -24,11 +24,6 @@ export const CloudEvaluationBehavior = () => {
|
|||||||
const type = watch('type');
|
const type = watch('type');
|
||||||
const dataSourceName = watch('dataSourceName');
|
const dataSourceName = watch('dataSourceName');
|
||||||
|
|
||||||
// cloud recording rules do not have alert conditions
|
|
||||||
if (type === RuleFormType.cloudRecording) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RuleEditorSection stepNo={3} title="Set alert evaluation behavior">
|
<RuleEditorSection stepNo={3} title="Set alert evaluation behavior">
|
||||||
<Field
|
<Field
|
||||||
|
@ -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;
|
|
||||||
`,
|
|
||||||
});
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user