Alerting: Add feature toggle and extension point (#110141)

* Add feature toggle and extension point

* Update ff name for enrichment per rule

* update translations

* rename to addRuleFormEnrichmentSection

* pr feedback

* prettier

* update translations

* revert wrong change in tsconfig
This commit is contained in:
Sonia Aguilar
2025-08-28 10:30:28 +02:00
committed by GitHub
parent 4d55358fd2
commit 98bd10965b
8 changed files with 132 additions and 44 deletions
@@ -748,6 +748,11 @@ export interface FeatureToggles {
*/
alertingAIGenTemplates?: boolean;
/**
* Enable enrichment per rule in the alerting UI.
* @default false
*/
alertingEnrichmentPerRule?: boolean;
/**
* Enable AI-analyze central state history.
* @default false
*/
+9
View File
@@ -1284,6 +1284,15 @@ var (
HideFromDocs: true,
Expression: "false",
},
{
Name: "alertingEnrichmentPerRule",
Description: "Enable enrichment per rule in the alerting UI.",
Stage: FeatureStageExperimental,
Owner: grafanaAlertingSquad,
HideFromAdminPage: true,
HideFromDocs: true,
Expression: "false",
},
{
Name: "alertingAIAnalyzeCentralStateHistory",
Description: "Enable AI-analyze central state history.",
+1
View File
@@ -167,6 +167,7 @@ alertingAIGenAlertRules,experimental,@grafana/alerting-squad,false,false,false
alertingAIFeedback,experimental,@grafana/alerting-squad,false,false,false
alertingAIImproveAlertRules,experimental,@grafana/alerting-squad,false,false,false
alertingAIGenTemplates,experimental,@grafana/alerting-squad,false,false,false
alertingEnrichmentPerRule,experimental,@grafana/alerting-squad,false,false,false
alertingAIAnalyzeCentralStateHistory,experimental,@grafana/alerting-squad,false,false,false
alertingNotificationsStepMode,GA,@grafana/alerting-squad,false,false,true
feedbackButton,experimental,@grafana/grafana-operator-experience-squad,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
167 alertingAIFeedback experimental @grafana/alerting-squad false false false
168 alertingAIImproveAlertRules experimental @grafana/alerting-squad false false false
169 alertingAIGenTemplates experimental @grafana/alerting-squad false false false
170 alertingEnrichmentPerRule experimental @grafana/alerting-squad false false false
171 alertingAIAnalyzeCentralStateHistory experimental @grafana/alerting-squad false false false
172 alertingNotificationsStepMode GA @grafana/alerting-squad false false true
173 feedbackButton experimental @grafana/grafana-operator-experience-squad false false false
+4
View File
@@ -679,6 +679,10 @@ const (
// Enable AI-generated alerting templates.
FlagAlertingAIGenTemplates = "alertingAIGenTemplates"
// FlagAlertingEnrichmentPerRule
// Enable enrichment per rule in the alerting UI.
FlagAlertingEnrichmentPerRule = "alertingEnrichmentPerRule"
// FlagAlertingAIAnalyzeCentralStateHistory
// Enable AI-analyze central state history.
FlagAlertingAIAnalyzeCentralStateHistory = "alertingAIAnalyzeCentralStateHistory"
+31
View File
@@ -214,6 +214,21 @@
"hideFromDocs": true
}
},
{
"metadata": {
"name": "alertingEnrichmentPerRule",
"resourceVersion": "1756206837948",
"creationTimestamp": "2025-08-26T11:13:57Z"
},
"spec": {
"description": "Enable enrichment per rule in the alerting UI.",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad",
"hideFromAdminPage": true,
"hideFromDocs": true,
"expression": "false"
}
},
{
"metadata": {
"name": "alertingFilterV2",
@@ -408,6 +423,22 @@
"expression": "false"
}
},
{
"metadata": {
"name": "alertingRuleNotificationMessageSectionExtension",
"resourceVersion": "1756193222535",
"creationTimestamp": "2025-08-26T07:27:02Z",
"deletionTimestamp": "2025-08-26T11:13:57Z"
},
"spec": {
"description": "Enable rule notification message section extension.",
"stage": "experimental",
"codeowner": "@grafana/alerting-squad",
"hideFromAdminPage": true,
"hideFromDocs": true,
"expression": "false"
}
},
{
"metadata": {
"name": "alertingRulePermanentlyDelete",
@@ -19,6 +19,7 @@ import DashboardAnnotationField from './DashboardAnnotationField';
import { DashboardPicker, PanelDTO, getVisualPanels } from './DashboardPicker';
import { NeedHelpInfo } from './NeedHelpInfo';
import { RuleEditorSection } from './RuleEditorSection';
import { NotificationMessageSectionExtension } from './alert-rule-form/extensions/NotificationMessageSectionExtension';
import { useDashboardQuery } from './useDashboardQuery';
const AnnotationsStep = () => {
@@ -165,51 +166,48 @@ const AnnotationsStep = () => {
onDeleteClick={handleDeleteDashboardAnnotation}
/>
)}
{
<div className={styles.annotationValueContainer}>
<Field
hidden={
annotationField.key === Annotation.dashboardUID || annotationField.key === Annotation.panelID
<div className={styles.annotationValueContainer}>
<Field
hidden={
annotationField.key === Annotation.dashboardUID || annotationField.key === Annotation.panelID
}
className={cx(styles.flexRowItemMargin, styles.field)}
invalid={!!errors.annotations?.[index]?.value?.message}
error={errors.annotations?.[index]?.value?.message}
noMargin
>
<ValueInputComponent
data-testid={`annotation-value-${index}`}
id={`annotation-${index}`}
className={cx(styles.annotationValueInput, { [styles.textarea]: !isUrl })}
{...register(`annotations.${index}.value`)}
placeholder={
isUrl
? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'https://'
: (annotationField.key &&
t('alerting.annotations-step.placeholder-value-input', 'Enter a {{key}}...', {
key: annotationField.key,
})) ||
t(
'alerting.annotations-step.placeholder-value-input-default',
'Enter custom annotation content...'
)
}
className={cx(styles.flexRowItemMargin, styles.field)}
invalid={!!errors.annotations?.[index]?.value?.message}
error={errors.annotations?.[index]?.value?.message}
noMargin
>
<ValueInputComponent
data-testid={`annotation-value-${index}`}
id={`annotation-${index}`}
className={cx(styles.annotationValueInput, { [styles.textarea]: !isUrl })}
{...register(`annotations.${index}.value`)}
placeholder={
isUrl
? // eslint-disable-next-line @grafana/i18n/no-untranslated-strings
'https://'
: (annotationField.key &&
t('alerting.annotations-step.placeholder-value-input', 'Enter a {{key}}...', {
key: annotationField.key,
})) ||
t(
'alerting.annotations-step.placeholder-value-input-default',
'Enter custom annotation content...'
)
}
defaultValue={annotationField.value}
/>
</Field>
{!annotationLabels[annotation] && (
<Button
type="button"
className={styles.deleteAnnotationButton}
aria-label={t('alerting.annotations-step.aria-label-delete-annotation', 'delete annotation')}
icon="trash-alt"
variant="secondary"
onClick={() => remove(index)}
/>
)}
</div>
}
defaultValue={annotationField.value}
/>
</Field>
{!annotationLabels[annotation] && (
<Button
type="button"
className={styles.deleteAnnotationButton}
aria-label={t('alerting.annotations-step.aria-label-delete-annotation', 'delete annotation')}
icon="trash-alt"
variant="secondary"
onClick={() => remove(index)}
/>
)}
</div>
</div>
</div>
);
@@ -242,6 +240,7 @@ const AnnotationsStep = () => {
onDismiss={() => setShowPanelSelector(false)}
/>
)}
<NotificationMessageSectionExtension />
</Stack>
</RuleEditorSection>
);
@@ -0,0 +1,34 @@
import { ComponentType } from 'react';
import { t } from '@grafana/i18n';
import { withErrorBoundary } from '@grafana/ui';
import { logError } from '../../../../Analytics';
export interface NotificationMessageSectionExtensionProps {}
// Internal variable to store the extension component, for now only one component is supported
let InternalNotificationMessageSectionExtension: ComponentType<NotificationMessageSectionExtensionProps> | null = null;
// This component is used to render the notification message section extension.
export const NotificationMessageSectionExtension: ComponentType<NotificationMessageSectionExtensionProps> = (props) => {
if (!InternalNotificationMessageSectionExtension) {
return null;
}
// Wrap the component with error boundary
const WrappedComponent = withErrorBoundary(InternalNotificationMessageSectionExtension, {
title: t(
'alerting.enrichment.error-boundary.notification-message-section-extension',
'Notification Message Section Extension failed to load'
),
style: 'alertbox',
errorLogger: logError,
});
return <WrappedComponent {...props} />;
};
export function addRuleFormEnrichmentSection(component: ComponentType<NotificationMessageSectionExtensionProps>) {
InternalNotificationMessageSectionExtension = component;
}
+5
View File
@@ -1118,6 +1118,11 @@
"new-alert-rule": "New alert rule",
"new-recording-rule": "New recording rule"
},
"enrichment": {
"error-boundary": {
"notification-message-section-extension": "Notification Message Section Extension failed to load"
}
},
"error-modal": {
"failed-to-update-your-configuration": "Failed to update your configuration:",
"title-something-went-wrong": "Something went wrong"