Add Alerting menu in getPanelMenu

This commit is contained in:
Sonia Aguilar 2023-10-16 13:31:42 +02:00
parent a6f111e23b
commit a05b358f43
2 changed files with 140 additions and 2 deletions

View File

@ -12,8 +12,10 @@ import {
} from '@grafana/data'; } from '@grafana/data';
import { AngularComponent, getPluginLinkExtensions } from '@grafana/runtime'; import { AngularComponent, getPluginLinkExtensions } from '@grafana/runtime';
import config from 'app/core/config'; import config from 'app/core/config';
import { grantUserPermissions } from 'app/features/alerting/unified/mocks';
import * as actions from 'app/features/explore/state/main'; import * as actions from 'app/features/explore/state/main';
import { setStore } from 'app/store/store'; import { setStore } from 'app/store/store';
import { AccessControlAction } from 'app/types';
import { PanelModel } from '../state'; import { PanelModel } from '../state';
import { createDashboardModelFixture } from '../state/__fixtures__/dashboardFixtures'; import { createDashboardModelFixture } from '../state/__fixtures__/dashboardFixtures';
@ -23,6 +25,7 @@ import { getPanelMenu } from './getPanelMenu';
jest.mock('app/core/services/context_srv', () => ({ jest.mock('app/core/services/context_srv', () => ({
contextSrv: { contextSrv: {
hasAccessToExplore: () => true, hasAccessToExplore: () => true,
hasPermission: jest.fn(),
}, },
})); }));
@ -38,6 +41,8 @@ describe('getPanelMenu()', () => {
beforeEach(() => { beforeEach(() => {
getPluginLinkExtensionsMock.mockRestore(); getPluginLinkExtensionsMock.mockRestore();
getPluginLinkExtensionsMock.mockReturnValue({ extensions: [] }); getPluginLinkExtensionsMock.mockReturnValue({ extensions: [] });
grantUserPermissions([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleUpdate]);
config.unifiedAlertingEnabled = false;
}); });
it('should return the correct panel menu items', () => { it('should return the correct panel menu items', () => {
@ -619,4 +624,62 @@ describe('getPanelMenu()', () => {
expect(windowOpen).toHaveBeenLastCalledWith(`${testSubUrl}${testUrl}`); expect(windowOpen).toHaveBeenLastCalledWith(`${testSubUrl}${testUrl}`);
}); });
}); });
describe('Alerting menu', () => {
it('should render alerting Menu with correct sub menu if user has permissions to read and update alerts ', () => {
const panel = new PanelModel({});
const dashboard = createDashboardModelFixture({});
config.unifiedAlertingEnabled = true;
grantUserPermissions([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleUpdate]);
const menuItems = getPanelMenu(dashboard, panel);
const alertingSubMenu = menuItems.find((i) => i.text === 'Alerting')?.subMenu;
expect(alertingSubMenu).toEqual(
expect.arrayContaining([
expect.objectContaining({
text: 'View all alert rules',
}),
expect.objectContaining({
text: 'Create alert rule from this panel',
}),
])
);
});
it('should not render create alert submenu item in the Alerting subMenu, if user does not have permissions to update alerts ', () => {
const panel = new PanelModel({});
const dashboard = createDashboardModelFixture({});
grantUserPermissions([AccessControlAction.AlertingRuleRead]);
config.unifiedAlertingEnabled = true;
const menuItems = getPanelMenu(dashboard, panel);
const alertingSubMenu = menuItems.find((i) => i.text === 'Alerting')?.subMenu;
expect(alertingSubMenu).toEqual(
expect.arrayContaining([
expect.objectContaining({
text: 'View all alert rules',
}),
expect.not.objectContaining({
text: 'Create alert rule from this panel',
}),
])
);
});
it('should not render Alerting in menu, if user does not have permissions to read update alerts ', () => {
const panel = new PanelModel({});
const dashboard = createDashboardModelFixture({});
grantUserPermissions([]);
config.unifiedAlertingEnabled = true;
const menuItems = getPanelMenu(dashboard, panel);
const alertingSubMenu = menuItems.find((i) => i.text === 'Alerting')?.subMenu;
expect(alertingSubMenu).toBeUndefined();
});
});
}); });

View File

@ -3,14 +3,18 @@ import {
PanelMenuItem, PanelMenuItem,
PluginExtensionLink, PluginExtensionLink,
PluginExtensionPoints, PluginExtensionPoints,
urlUtil,
type PluginExtensionPanelContext, type PluginExtensionPanelContext,
} from '@grafana/data'; } from '@grafana/data';
import { AngularComponent, locationService, reportInteraction, getPluginLinkExtensions } from '@grafana/runtime'; import { AngularComponent, getPluginLinkExtensions, locationService, reportInteraction } from '@grafana/runtime';
import { PanelCtrl } from 'app/angular/panel/panel_ctrl'; import { PanelCtrl } from 'app/angular/panel/panel_ctrl';
import config from 'app/core/config'; import config, { getConfig } from 'app/core/config';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
import { contextSrv } from 'app/core/services/context_srv'; import { contextSrv } from 'app/core/services/context_srv';
import { getExploreUrl } from 'app/core/utils/explore'; import { getExploreUrl } from 'app/core/utils/explore';
import { getRulesPermissions } from 'app/features/alerting/unified/utils/access-control';
import { GRAFANA_RULES_SOURCE_NAME } from 'app/features/alerting/unified/utils/datasource';
import { panelToRuleFormValues } from 'app/features/alerting/unified/utils/rule-form';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { import {
@ -202,6 +206,77 @@ export function getPanelMenu(
subMenu: inspectMenu, subMenu: inspectMenu,
}); });
const createAlert = async () => {
const formValues = await panelToRuleFormValues(panel, dashboard);
const ruleFormUrl = urlUtil.renderUrl('/alerting/new', {
defaults: JSON.stringify(formValues),
returnTo: location.pathname + location.search,
});
locationService.push(ruleFormUrl);
};
const navigateToAlertListView = async () => {
const alertListUrl = urlUtil.renderUrl('/alerting/list', {
returnTo: location.pathname + location.search,
});
locationService.push(alertListUrl);
};
const onNavigateToAlertListView = (event: React.MouseEvent) => {
event.preventDefault();
navigateToAlertListView();
reportInteraction('dashboards_panelheader_menu', { item: 'create-alert' });
};
const onCreateAlert = (event: React.MouseEvent) => {
event.preventDefault();
createAlert();
reportInteraction('dashboards_panelheader_menu', { item: 'create-alert' });
};
const { unifiedAlertingEnabled } = getConfig();
const hasRuleReadPermissions = contextSrv.hasPermission(getRulesPermissions(GRAFANA_RULES_SOURCE_NAME).read);
const hasRuleUpdatePermissions = contextSrv.hasPermission(getRulesPermissions(GRAFANA_RULES_SOURCE_NAME).update);
const isAlertingAvailableForRead = unifiedAlertingEnabled && hasRuleReadPermissions;
const alertingMenuAvailable = isAlertingAvailableForRead;
if (alertingMenuAvailable) {
// prepare submenu depending on permissions
const subMenu: PanelMenuItem[] = [];
if (hasRuleUpdatePermissions) {
subMenu.push({
text: t('panel.header-menu.create-alert', `Create alert rule from this panel`),
onClick: (e: React.MouseEvent) => onCreateAlert(e),
});
}
subMenu.push({
text: t('panel.header-menu.view-alerts', `View all alert rules`),
onClick: (e: React.MouseEvent) => onNavigateToAlertListView(e),
});
menu.push({
type: 'submenu',
text: t('panel.header-menu.alerting', `Alerting`),
iconClassName: 'bell',
onClick: (e: React.MouseEvent<HTMLElement>) => {
const currentTarget = e.currentTarget;
const target = e.target;
if (
target === currentTarget ||
(target instanceof HTMLElement && target.closest('[role="menuitem"]') === currentTarget)
) {
onInspectPanel();
}
},
shortcut: 'a',
subMenu: subMenu,
});
}
const subMenu: PanelMenuItem[] = []; const subMenu: PanelMenuItem[] = [];
const canEdit = dashboard.canEditPanel(panel); const canEdit = dashboard.canEditPanel(panel);
if (!(panel.isViewing || panel.isEditing)) { if (!(panel.isViewing || panel.isEditing)) {