Alerting: Enrichment per rule wip-2 (#110437)
* Add feature toggle and extension point * Update ff name for enrichment per rule * update translations * wip * remove wrong duplication after merging from main * manage enrichments per rule drawer: add ruleUid to extension component * remove folder * move drawer to the list page * update translations * remove unused import
This commit is contained in:
@@ -19,7 +19,6 @@ 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 = () => {
|
||||
@@ -212,6 +211,7 @@ const AnnotationsStep = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
<Stack direction="row" gap={1}>
|
||||
<div className={styles.addAnnotationsButtonContainer}>
|
||||
<Button
|
||||
@@ -240,7 +240,6 @@ const AnnotationsStep = () => {
|
||||
onDismiss={() => setShowPanelSelector(false)}
|
||||
/>
|
||||
)}
|
||||
<NotificationMessageSectionExtension />
|
||||
</Stack>
|
||||
</RuleEditorSection>
|
||||
);
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
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;
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { ComponentType, memo } from 'react';
|
||||
|
||||
import { t } from '@grafana/i18n';
|
||||
import { withErrorBoundary } from '@grafana/ui';
|
||||
|
||||
import { logError } from '../../../Analytics';
|
||||
|
||||
export interface EnrichmentDrawerExtensionProps {
|
||||
ruleUid: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
// Internal variable to store the extension component, for now only one component is supported
|
||||
let InternalEnrichmentDrawerExtension: ComponentType<EnrichmentDrawerExtensionProps> | null = null;
|
||||
|
||||
// This component is used to render the enrichment drawer extension.
|
||||
const EnrichmentDrawerExtensionComponent: ComponentType<EnrichmentDrawerExtensionProps> = (props) => {
|
||||
if (!InternalEnrichmentDrawerExtension) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Wrap the component with error boundary
|
||||
const WrappedComponent = withErrorBoundary(InternalEnrichmentDrawerExtension, {
|
||||
title: t(
|
||||
'alerting.enrichment.error-boundary.enrichment-drawer-extension',
|
||||
'Enrichment Drawer Extension failed to load'
|
||||
),
|
||||
style: 'alertbox',
|
||||
errorLogger: logError,
|
||||
});
|
||||
|
||||
return <WrappedComponent {...props} />;
|
||||
};
|
||||
|
||||
export const EnrichmentDrawerExtension = memo(EnrichmentDrawerExtensionComponent, (prevProps, nextProps) => {
|
||||
// Only re-render if ruleUid changes
|
||||
return prevProps.ruleUid === nextProps.ruleUid;
|
||||
});
|
||||
|
||||
export function addEnrichmentDrawerExtension(component: ComponentType<EnrichmentDrawerExtensionProps>) {
|
||||
InternalEnrichmentDrawerExtension = component;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { PropsOf } from '@emotion/react';
|
||||
|
||||
import { AppEvents } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { Button, ComponentSize, Dropdown, Menu } from '@grafana/ui';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import MenuItemPauseRule from 'app/features/alerting/unified/components/MenuItemPauseRule';
|
||||
@@ -14,7 +15,9 @@ import {
|
||||
AlertRuleAction,
|
||||
skipToken,
|
||||
useGrafanaPromRuleAbilities,
|
||||
useGrafanaPromRuleAbility,
|
||||
useRulerRuleAbilities,
|
||||
useRulerRuleAbility,
|
||||
} from '../../hooks/useAbilities';
|
||||
import { createShareLink, isLocalDevEnv, isOpenSourceEdition } from '../../utils/misc';
|
||||
import * as ruleId from '../../utils/rule-id';
|
||||
@@ -34,6 +37,7 @@ interface Props {
|
||||
identifier: RuleIdentifier;
|
||||
groupIdentifier: RuleGroupIdentifierV2;
|
||||
handleSilence: () => void;
|
||||
handleManageEnrichments?: () => void;
|
||||
handleDelete: (identifier: EditableRuleIdentifier, groupIdentifier: RuleGroupIdentifierV2) => void;
|
||||
handleDuplicateRule: (identifier: RuleIdentifier) => void;
|
||||
onPauseChange?: () => void;
|
||||
@@ -53,6 +57,7 @@ const AlertRuleMenu = ({
|
||||
identifier,
|
||||
groupIdentifier,
|
||||
handleSilence,
|
||||
handleManageEnrichments,
|
||||
handleDelete,
|
||||
handleDuplicateRule,
|
||||
onPauseChange,
|
||||
@@ -84,6 +89,16 @@ const AlertRuleMenu = ({
|
||||
AlertRuleAction.ModifyExport,
|
||||
]);
|
||||
|
||||
const [editRuleSupported, editRuleAllowed] = useRulerRuleAbility(rulerRule, groupIdentifier, AlertRuleAction.Update);
|
||||
// If the consumer of this component comes from the alert list view, we need to use promRule to check abilities and permissions,
|
||||
// as we have removed all requests to the ruler API in the list view.
|
||||
const [grafanaEditRuleSupported, grafanaEditRuleAllowed] = useGrafanaPromRuleAbility(
|
||||
prometheusRuleType.grafana.rule(promRule) ? promRule : skipToken,
|
||||
AlertRuleAction.Update
|
||||
);
|
||||
|
||||
const canEditRule = (editRuleSupported && editRuleAllowed) || (grafanaEditRuleSupported && grafanaEditRuleAllowed);
|
||||
|
||||
const [pauseSupported, pauseAllowed] = rulerPauseAbility;
|
||||
const [grafanaPauseSupported, grafanaPauseAllowed] = grafanaPauseAbility;
|
||||
const canPause = (pauseSupported && pauseAllowed) || (grafanaPauseSupported && grafanaPauseAllowed);
|
||||
@@ -131,8 +146,23 @@ const AlertRuleMenu = ({
|
||||
(rulerRuleType.grafana.rule(rulerRule) && isPausedRule(rulerRule)) ||
|
||||
(prometheusRuleType.grafana.rule(promRule) && promRule.isPaused);
|
||||
|
||||
// todo: make this new menu item for enrichments an extension of the alertrulemenu items. For first iteration, we'll keep it here.
|
||||
const canManageEnrichments =
|
||||
canEditRule &&
|
||||
ruleUid &&
|
||||
handleManageEnrichments &&
|
||||
config.featureToggles.alertingEnrichmentPerRule &&
|
||||
config.featureToggles.alertEnrichment;
|
||||
|
||||
const menuItems = (
|
||||
<>
|
||||
{canManageEnrichments && (
|
||||
<Menu.Item
|
||||
label={t('alerting.alert-menu.manage-enrichments', 'Manage enrichments')}
|
||||
icon="edit"
|
||||
onClick={handleManageEnrichments}
|
||||
/>
|
||||
)}
|
||||
{canPause && ruleUid && groupIdentifier.groupOrigin === 'grafana' && (
|
||||
<MenuItemPauseRule
|
||||
uid={ruleUid}
|
||||
|
||||
@@ -19,6 +19,7 @@ import { createViewLink } from '../../utils/misc';
|
||||
import * as ruleId from '../../utils/rule-id';
|
||||
import { getRuleUID, prometheusRuleType, rulerRuleType } from '../../utils/rules';
|
||||
import { createRelativeUrl } from '../../utils/url';
|
||||
import { EnrichmentDrawerExtension } from '../rule-list/extensions/EnrichmentDrawerExtension';
|
||||
|
||||
import { RedirectToCloneRule } from './CloneRule';
|
||||
|
||||
@@ -45,6 +46,7 @@ export const RuleActionsButtons = ({ compact, showViewButton, rule, rulesSource
|
||||
const [deleteModal, showDeleteModal] = useDeleteModal(redirectToListView);
|
||||
|
||||
const [showSilenceDrawer, setShowSilenceDrawer] = useState<boolean>(false);
|
||||
const [showEnrichmentDrawer, setShowEnrichmentDrawer] = useState<boolean>(false);
|
||||
|
||||
const [redirectToClone, setRedirectToClone] = useState<
|
||||
{ identifier: RuleIdentifier; isProvisioned: boolean } | undefined
|
||||
@@ -126,6 +128,7 @@ export const RuleActionsButtons = ({ compact, showViewButton, rule, rulesSource
|
||||
}
|
||||
}}
|
||||
handleSilence={() => setShowSilenceDrawer(true)}
|
||||
handleManageEnrichments={() => setShowEnrichmentDrawer(true)}
|
||||
handleDuplicateRule={() => setRedirectToClone({ identifier, isProvisioned })}
|
||||
onPauseChange={() => {
|
||||
// Uses INSTANCES_DISPLAY_LIMIT + 1 here as exporting LIMIT_ALERTS from RuleList has the side effect
|
||||
@@ -142,6 +145,9 @@ export const RuleActionsButtons = ({ compact, showViewButton, rule, rulesSource
|
||||
{silenceableRule && showSilenceDrawer && (
|
||||
<SilenceGrafanaRuleDrawer ruleUid={ruleUid} onClose={() => setShowSilenceDrawer(false)} />
|
||||
)}
|
||||
{ruleUid && showEnrichmentDrawer && (
|
||||
<EnrichmentDrawerExtension ruleUid={ruleUid} onClose={() => setShowEnrichmentDrawer(false)} />
|
||||
)}
|
||||
{redirectToClone?.identifier && (
|
||||
<RedirectToCloneRule
|
||||
identifier={redirectToClone.identifier}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { RequireAtLeastOne } from 'type-fest';
|
||||
|
||||
import { Trans, t } from '@grafana/i18n';
|
||||
import { LinkButton, Stack } from '@grafana/ui';
|
||||
import { EnrichmentDrawerExtension } from 'app/features/alerting/unified/components/rule-list/extensions/EnrichmentDrawerExtension';
|
||||
import AlertRuleMenu from 'app/features/alerting/unified/components/rule-viewer/AlertRuleMenu';
|
||||
import { useDeleteModal } from 'app/features/alerting/unified/components/rule-viewer/DeleteModal';
|
||||
import { RedirectToCloneRule } from 'app/features/alerting/unified/components/rules/CloneRule';
|
||||
@@ -50,6 +51,7 @@ export function RuleActionsButtons({ compact, rule, promRule, groupIdentifier }:
|
||||
const [deleteModal, showDeleteModal] = useDeleteModal(redirectToListView);
|
||||
|
||||
const [showSilenceDrawer, setShowSilenceDrawer] = useState<boolean>(false);
|
||||
const [showEnrichmentDrawer, setShowEnrichmentDrawer] = useState<boolean>(false);
|
||||
|
||||
const [redirectToClone, setRedirectToClone] = useState<
|
||||
{ identifier: RuleIdentifier; isProvisioned: boolean } | undefined
|
||||
@@ -111,12 +113,16 @@ export function RuleActionsButtons({ compact, rule, promRule, groupIdentifier }:
|
||||
identifier={identifier}
|
||||
handleDelete={(identifier, groupIdentifier) => showDeleteModal(identifier, groupIdentifier)}
|
||||
handleSilence={() => setShowSilenceDrawer(true)}
|
||||
handleManageEnrichments={() => setShowEnrichmentDrawer(true)}
|
||||
handleDuplicateRule={() => setRedirectToClone({ identifier, isProvisioned })}
|
||||
/>
|
||||
{deleteModal}
|
||||
{silenceableRule && showSilenceDrawer && (
|
||||
<SilenceGrafanaRuleDrawer ruleUid={ruleUid} onClose={() => setShowSilenceDrawer(false)} />
|
||||
)}
|
||||
{ruleUid && showEnrichmentDrawer && (
|
||||
<EnrichmentDrawerExtension ruleUid={ruleUid} onClose={() => setShowEnrichmentDrawer(false)} />
|
||||
)}
|
||||
{redirectToClone?.identifier && (
|
||||
<RedirectToCloneRule
|
||||
identifier={redirectToClone.identifier}
|
||||
|
||||
@@ -504,6 +504,7 @@
|
||||
"copy-link": "Copy link",
|
||||
"duplicate": "Duplicate",
|
||||
"export": "Export",
|
||||
"manage-enrichments": "Manage enrichments",
|
||||
"silence-notifications": "Silence notifications",
|
||||
"with-modifications": "With modifications"
|
||||
},
|
||||
@@ -1121,7 +1122,7 @@
|
||||
},
|
||||
"enrichment": {
|
||||
"error-boundary": {
|
||||
"notification-message-section-extension": "Notification Message Section Extension failed to load",
|
||||
"enrichment-drawer-extension": "Enrichment Drawer Extension failed to load",
|
||||
"rule-viewer-section-extension": "Rule Viewer Enrichment Section failed to load"
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user