mirror of https://github.com/grafana/grafana.git
Alerting: Add extension point for rule view page enrichment section (#110498)
* add enrichment to rule page view WIP * add Alert enrichment tab to rule view page * fix to view dummy component * move rule view enrichment tab to enterprise * remove better file changes * remove console log * add test for enrichment tab * run yarn i18n-extract * update directory structure * remove .betterer.results changes * Convert React.createElement call to JSX syntax * revert removed lines * revert removed lines * revert removed lines * fix failing test * fix lint error --------- Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com>
This commit is contained in:
parent
4e05bb36f2
commit
53cd0882ed
|
@ -1723,11 +1723,6 @@
|
|||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/alerting/unified/components/rule-viewer/RuleViewer.tsx": {
|
||||
"react-hooks/rules-of-hooks": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 4
|
||||
|
|
|
@ -32,6 +32,7 @@ import { PromAlertingRuleState, PromRuleType } from 'app/types/unified-alerting-
|
|||
|
||||
import { logError } from '../../Analytics';
|
||||
import { defaultPageNav } from '../../RuleViewer';
|
||||
import { useRuleViewExtensionsNav } from '../../enterprise-components/rule-view-page/navigation';
|
||||
import { shouldUseAlertingListViewV2, shouldUsePrometheusRulesPrimary } from '../../featureToggles';
|
||||
import { isError, useAsync } from '../../hooks/useAsync';
|
||||
import { useRuleLocation } from '../../hooks/useCombinedRule';
|
||||
|
@ -71,6 +72,7 @@ import { History } from './tabs/History';
|
|||
import { InstancesList } from './tabs/Instances';
|
||||
import { QueryResults } from './tabs/Query';
|
||||
import { Routing } from './tabs/Routing';
|
||||
import { RulePageEnrichmentSectionExtension } from './tabs/extensions/RuleViewerExtension';
|
||||
|
||||
export enum ActiveTab {
|
||||
Query = 'query',
|
||||
|
@ -79,6 +81,7 @@ export enum ActiveTab {
|
|||
Routing = 'routing',
|
||||
Details = 'details',
|
||||
VersionHistory = 'version-history',
|
||||
Enrichment = 'enrichment',
|
||||
}
|
||||
|
||||
const prometheusRulesPrimary = shouldUsePrometheusRulesPrimary();
|
||||
|
@ -87,6 +90,7 @@ const alertingListViewV2 = shouldUseAlertingListViewV2();
|
|||
const RuleViewer = () => {
|
||||
const { rule, identifier } = useAlertRule();
|
||||
const { pageNav, activeTab } = usePageNav(rule);
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// GMA /api/v1/rules endpoint is strongly consistent, so we don't need to check for consistency
|
||||
const shouldUseConsistencyCheck = isGrafanaRuleIdentifier(identifier)
|
||||
|
@ -128,7 +132,7 @@ const RuleViewer = () => {
|
|||
/>
|
||||
)}
|
||||
actions={<RuleActionsButtons rule={rule} rulesSource={rule.namespace.rulesSource} />}
|
||||
info={createMetadata(rule)}
|
||||
info={createMetadata(rule, styles)}
|
||||
subTitle={
|
||||
<Stack direction="column">
|
||||
{summary}
|
||||
|
@ -171,6 +175,7 @@ const RuleViewer = () => {
|
|||
{activeTab === ActiveTab.VersionHistory && rulerRuleType.grafana.rule(rule.rulerRule) && (
|
||||
<AlertVersionHistory rule={rule.rulerRule} />
|
||||
)}
|
||||
{activeTab === ActiveTab.Enrichment && <RulePageEnrichmentSectionExtension />}
|
||||
</TabContent>
|
||||
</Stack>
|
||||
{duplicateRuleIdentifier && (
|
||||
|
@ -185,7 +190,7 @@ const RuleViewer = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const createMetadata = (rule: CombinedRule): PageInfoItem[] => {
|
||||
const createMetadata = (rule: CombinedRule, styles: ReturnType<typeof getStyles>): PageInfoItem[] => {
|
||||
const { labels, annotations, group, rulerRule } = rule;
|
||||
const metadata: PageInfoItem[] = [];
|
||||
|
||||
|
@ -198,7 +203,6 @@ const createMetadata = (rule: CombinedRule): PageInfoItem[] => {
|
|||
const hasLabels = labelsSize(labels) > 0;
|
||||
|
||||
const interval = group.interval;
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
// if the alert rule uses simplified routing, we'll show a link to the contact point
|
||||
if (rulerRuleType.grafana.alertingRule(rulerRule)) {
|
||||
|
@ -438,6 +442,12 @@ function usePageNav(rule: CombinedRule) {
|
|||
|
||||
const groupDetailsUrl = groups.detailsPageLink(dataSourceUID, namespaceString, groupName);
|
||||
|
||||
const setActiveTabFromString = (tab: string) => {
|
||||
if (isValidTab(tab)) {
|
||||
setActiveTab(tab);
|
||||
}
|
||||
};
|
||||
|
||||
const pageNav: NavModelItem = {
|
||||
...defaultPageNav,
|
||||
text: rule.name,
|
||||
|
@ -483,6 +493,8 @@ function usePageNav(rule: CombinedRule) {
|
|||
},
|
||||
hideFromTabs: !isGrafanaAlertRule && !isGrafanaRecordingRule,
|
||||
},
|
||||
// Enterprise extensions can append additional tabs here
|
||||
...useRuleViewExtensionsNav(activeTab, setActiveTabFromString),
|
||||
],
|
||||
parentItem: {
|
||||
text: groupName,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import { ComponentType } from 'react';
|
||||
|
||||
import { t } from '@grafana/i18n';
|
||||
import { withErrorBoundary } from '@grafana/ui';
|
||||
|
||||
import { logError } from '../../../../Analytics';
|
||||
|
||||
export interface RuleViewerExtensionProps {}
|
||||
|
||||
let InternalRulePageEnrichmentSection: ComponentType<RuleViewerExtensionProps> | null = null;
|
||||
|
||||
export const RulePageEnrichmentSectionExtension: ComponentType<RuleViewerExtensionProps> = (props) => {
|
||||
if (!InternalRulePageEnrichmentSection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const WrappedComponent = withErrorBoundary(InternalRulePageEnrichmentSection, {
|
||||
title: t(
|
||||
'alerting.enrichment.error-boundary.rule-viewer-section-extension',
|
||||
'Rule Viewer Enrichment Section failed to load'
|
||||
),
|
||||
style: 'alertbox',
|
||||
errorLogger: logError,
|
||||
});
|
||||
|
||||
return <WrappedComponent {...props} />;
|
||||
};
|
||||
|
||||
export function addRulePageEnrichmentSection(component: ComponentType<RuleViewerExtensionProps>) {
|
||||
InternalRulePageEnrichmentSection = component;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
import { css } from '@emotion/css';
|
||||
|
||||
import { FeatureState, NavModelItem } from '@grafana/data';
|
||||
import { t } from '@grafana/i18n';
|
||||
import { FeatureBadge, useStyles2 } from '@grafana/ui';
|
||||
|
||||
type SetActiveTab = (tab: string) => void;
|
||||
|
||||
type RuleViewTabBuilderArgs = {
|
||||
activeTab: string;
|
||||
setActiveTab: SetActiveTab;
|
||||
};
|
||||
|
||||
type RuleViewTabBuilder = (args: RuleViewTabBuilderArgs) => NavModelItem;
|
||||
|
||||
const ruleViewTabBuilders: RuleViewTabBuilder[] = [];
|
||||
|
||||
export function registerRuleViewTab(builder: RuleViewTabBuilder) {
|
||||
ruleViewTabBuilders.push(builder);
|
||||
}
|
||||
|
||||
export function getRuleViewExtensionTabs(args: RuleViewTabBuilderArgs): NavModelItem[] {
|
||||
return ruleViewTabBuilders.map((builder) => builder(args));
|
||||
}
|
||||
|
||||
export function addEnrichmentSection() {
|
||||
registerRuleViewTab(({ activeTab, setActiveTab }) => {
|
||||
const tabId = 'enrichment';
|
||||
return {
|
||||
text: t('alerting.use-page-nav.page-nav.text.enrichment', 'Alert enrichment'),
|
||||
active: activeTab === tabId,
|
||||
onClick: () => setActiveTab(tabId),
|
||||
tabSuffix: () => <EnrichmentTabSuffix />,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// ONLY FOR TESTS: resets the registered tabs between tests
|
||||
export function __clearRuleViewTabsForTests() {
|
||||
ruleViewTabBuilders.splice(0, ruleViewTabBuilders.length);
|
||||
}
|
||||
|
||||
function getStyles() {
|
||||
return {
|
||||
tabSuffix: css({
|
||||
marginLeft: 8,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function EnrichmentTabSuffix() {
|
||||
const styles = useStyles2(getStyles);
|
||||
return (
|
||||
<span className={styles.tabSuffix}>
|
||||
<FeatureBadge featureState={FeatureState.new} />
|
||||
</span>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { beforeEach, describe, expect, it } from '@jest/globals';
|
||||
|
||||
import { __clearRuleViewTabsForTests, addEnrichmentSection, getRuleViewExtensionTabs } from './extensions';
|
||||
|
||||
describe('rule-view-page navigation', () => {
|
||||
beforeEach(() => {
|
||||
__clearRuleViewTabsForTests();
|
||||
});
|
||||
|
||||
it('does not include Alert enrichment tab when not registered', () => {
|
||||
const tabs = getRuleViewExtensionTabs({ activeTab: 'query', setActiveTab: () => {} });
|
||||
const hasEnrichment = tabs.some((t) => t.text === 'Alert enrichment');
|
||||
expect(hasEnrichment).toBe(false);
|
||||
});
|
||||
|
||||
it('includes Alert enrichment tab when registered (enterprise + toggle on)', () => {
|
||||
addEnrichmentSection();
|
||||
const tabs = getRuleViewExtensionTabs({ activeTab: 'query', setActiveTab: () => {} });
|
||||
const enrichment = tabs.find((t) => t.text === 'Alert enrichment');
|
||||
expect(enrichment).toBeTruthy();
|
||||
expect(enrichment!.active).toBe(false);
|
||||
});
|
||||
|
||||
it('marks Alert enrichment tab active when selected', () => {
|
||||
addEnrichmentSection();
|
||||
const tabs = getRuleViewExtensionTabs({ activeTab: 'enrichment', setActiveTab: () => {} });
|
||||
const enrichment = tabs.find((t) => t.text === 'Alert enrichment');
|
||||
expect(enrichment).toBeTruthy();
|
||||
expect(enrichment!.active).toBe(true);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,7 @@
|
|||
import { NavModelItem } from '@grafana/data';
|
||||
|
||||
import { getRuleViewExtensionTabs } from './extensions';
|
||||
|
||||
export function useRuleViewExtensionsNav(activeTab: string, setActiveTab: (tab: string) => void): NavModelItem[] {
|
||||
return getRuleViewExtensionTabs({ activeTab, setActiveTab });
|
||||
}
|
|
@ -1121,7 +1121,8 @@
|
|||
},
|
||||
"enrichment": {
|
||||
"error-boundary": {
|
||||
"notification-message-section-extension": "Notification Message Section Extension failed to load"
|
||||
"notification-message-section-extension": "Notification Message Section Extension failed to load",
|
||||
"rule-viewer-section-extension": "Rule Viewer Enrichment Section failed to load"
|
||||
}
|
||||
},
|
||||
"error-modal": {
|
||||
|
@ -3042,6 +3043,7 @@
|
|||
"page-nav": {
|
||||
"text": {
|
||||
"details": "Details",
|
||||
"enrichment": "Alert enrichment",
|
||||
"history": "History",
|
||||
"instances": "Instances",
|
||||
"query-and-conditions": "Query and conditions",
|
||||
|
|
Loading…
Reference in New Issue