mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add export mute timings feature to the UI. (#79395)
* Add export for all muteTimings and a single muteTiming * Add test * Fix snapshot * Fix test * Add mute timing name in file name when exporting single mute timing
This commit is contained in:
parent
377262c283
commit
106903b549
@ -239,5 +239,21 @@ export const alertRuleApi = alertingApi.injectEndpoints({
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
exportMuteTimings: build.query<string, { format: ExportFormats }>({
|
||||||
|
query: ({ format }) => ({
|
||||||
|
url: `/api/v1/provisioning/mute-timings/export/`,
|
||||||
|
params: { format: format },
|
||||||
|
responseType: 'text',
|
||||||
|
}),
|
||||||
|
keepUnusedDataFor: 0,
|
||||||
|
}),
|
||||||
|
exportMuteTiming: build.query<string, { format: ExportFormats; muteTiming: string }>({
|
||||||
|
query: ({ format, muteTiming }) => ({
|
||||||
|
url: `/api/v1/provisioning/mute-timings/${muteTiming}/export/`,
|
||||||
|
params: { format: format },
|
||||||
|
responseType: 'text',
|
||||||
|
}),
|
||||||
|
keepUnusedDataFor: 0,
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
|
import { LoadingPlaceholder } from '@grafana/ui';
|
||||||
|
|
||||||
|
import { alertRuleApi } from '../../api/alertRuleApi';
|
||||||
|
|
||||||
|
import { FileExportPreview } from './FileExportPreview';
|
||||||
|
import { GrafanaExportDrawer } from './GrafanaExportDrawer';
|
||||||
|
import { allGrafanaExportProviders, ExportFormats } from './providers';
|
||||||
|
interface MuteTimingsExporterPreviewProps {
|
||||||
|
exportFormat: ExportFormats;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GrafanaMuteTimingsExporterPreview = ({ exportFormat, onClose }: MuteTimingsExporterPreviewProps) => {
|
||||||
|
const { currentData: muteTimingsDefinition = '', isFetching } = alertRuleApi.useExportMuteTimingsQuery({
|
||||||
|
format: exportFormat,
|
||||||
|
});
|
||||||
|
const downloadFileName = `mute-timings-${new Date().getTime()}`;
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return <LoadingPlaceholder text="Loading...." />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FileExportPreview
|
||||||
|
format={exportFormat}
|
||||||
|
textDefinition={muteTimingsDefinition}
|
||||||
|
downloadFileName={downloadFileName}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
interface GrafanaMuteTimingExporterPreviewProps extends MuteTimingsExporterPreviewProps {
|
||||||
|
muteTimingName: string;
|
||||||
|
}
|
||||||
|
const GrafanaMuteTimingExporterPreview = ({
|
||||||
|
exportFormat,
|
||||||
|
onClose,
|
||||||
|
muteTimingName,
|
||||||
|
}: GrafanaMuteTimingExporterPreviewProps) => {
|
||||||
|
const { currentData: muteTimingsDefinition = '', isFetching } = alertRuleApi.useExportMuteTimingQuery({
|
||||||
|
format: exportFormat,
|
||||||
|
muteTiming: muteTimingName,
|
||||||
|
});
|
||||||
|
const downloadFileName = `mute-timing-${muteTimingName}-${new Date().getTime()}`;
|
||||||
|
|
||||||
|
if (isFetching) {
|
||||||
|
return <LoadingPlaceholder text="Loading...." />;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<FileExportPreview
|
||||||
|
format={exportFormat}
|
||||||
|
textDefinition={muteTimingsDefinition}
|
||||||
|
downloadFileName={downloadFileName}
|
||||||
|
onClose={onClose}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
interface GrafanaMuteTimingsExporterProps {
|
||||||
|
onClose: () => void;
|
||||||
|
muteTimingName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GrafanaMuteTimingsExporter = ({ onClose, muteTimingName }: GrafanaMuteTimingsExporterProps) => {
|
||||||
|
const [activeTab, setActiveTab] = useState<ExportFormats>('yaml');
|
||||||
|
return (
|
||||||
|
<GrafanaExportDrawer
|
||||||
|
activeTab={activeTab}
|
||||||
|
onTabChange={setActiveTab}
|
||||||
|
onClose={onClose}
|
||||||
|
formatProviders={Object.values(allGrafanaExportProviders)}
|
||||||
|
>
|
||||||
|
{muteTimingName ? (
|
||||||
|
<GrafanaMuteTimingExporterPreview exportFormat={activeTab} onClose={onClose} muteTimingName={muteTimingName} />
|
||||||
|
) : (
|
||||||
|
<GrafanaMuteTimingsExporterPreview exportFormat={activeTab} onClose={onClose} />
|
||||||
|
)}
|
||||||
|
</GrafanaExportDrawer>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,62 @@
|
|||||||
|
import { render, waitFor } from '@testing-library/react';
|
||||||
|
import React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { locationService } from '@grafana/runtime';
|
||||||
|
import { configureStore } from 'app/store/configureStore';
|
||||||
|
import { AccessControlAction } from 'app/types';
|
||||||
|
|
||||||
|
import { grantUserPermissions } from '../../mocks';
|
||||||
|
import { AlertmanagerProvider } from '../../state/AlertmanagerContext';
|
||||||
|
import { GRAFANA_DATASOURCE_NAME } from '../../utils/datasource';
|
||||||
|
|
||||||
|
import { MuteTimingsTable } from './MuteTimingsTable';
|
||||||
|
|
||||||
|
jest.mock('app/types', () => ({
|
||||||
|
...jest.requireActual('app/types'),
|
||||||
|
useDispatch: () => jest.fn(),
|
||||||
|
}));
|
||||||
|
const renderWithProvider = (alertManagerSource?: string) => {
|
||||||
|
const store = configureStore();
|
||||||
|
|
||||||
|
return render(
|
||||||
|
<Provider store={store}>
|
||||||
|
<Router history={locationService.getHistory()}>
|
||||||
|
<AlertmanagerProvider accessType={'notification'} alertmanagerSourceName={alertManagerSource}>
|
||||||
|
<MuteTimingsTable alertManagerSourceName={alertManagerSource ?? GRAFANA_DATASOURCE_NAME} />
|
||||||
|
</AlertmanagerProvider>
|
||||||
|
</Router>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('MuteTimingsTable', () => {
|
||||||
|
it(' shows export button when allowed and supported', async () => {
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingNotificationsRead,
|
||||||
|
AccessControlAction.AlertingNotificationsWrite,
|
||||||
|
]);
|
||||||
|
const { findByRole } = renderWithProvider();
|
||||||
|
expect(await findByRole('button', { name: /export all/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
it('It does not show export button when not allowed ', async () => {
|
||||||
|
// when not allowed
|
||||||
|
grantUserPermissions([]);
|
||||||
|
const { queryByRole } = renderWithProvider();
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(queryByRole('button', { name: /export all/i })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('It does not show export button when not supported ', async () => {
|
||||||
|
// when not supported
|
||||||
|
grantUserPermissions([
|
||||||
|
AccessControlAction.AlertingNotificationsRead,
|
||||||
|
AccessControlAction.AlertingNotificationsWrite,
|
||||||
|
]);
|
||||||
|
const { queryByRole } = renderWithProvider('potato');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(queryByRole('button', { name: /export all/i })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,8 +1,9 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
|
import { useToggle } from 'react-use';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { IconButton, LinkButton, Link, useStyles2, ConfirmModal, Stack } from '@grafana/ui';
|
import { Button, ConfirmModal, IconButton, Link, LinkButton, Menu, Stack, useStyles2 } from '@grafana/ui';
|
||||||
import { MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
import { MuteTimeInterval } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { useDispatch } from 'app/types/store';
|
import { useDispatch } from 'app/types/store';
|
||||||
|
|
||||||
@ -11,20 +12,56 @@ import { AlertmanagerAction, useAlertmanagerAbilities, useAlertmanagerAbility }
|
|||||||
import { useAlertmanagerConfig } from '../../hooks/useAlertmanagerConfig';
|
import { useAlertmanagerConfig } from '../../hooks/useAlertmanagerConfig';
|
||||||
import { deleteMuteTimingAction } from '../../state/actions';
|
import { deleteMuteTimingAction } from '../../state/actions';
|
||||||
import { makeAMLink } from '../../utils/misc';
|
import { makeAMLink } from '../../utils/misc';
|
||||||
import { DynamicTable, DynamicTableItemProps, DynamicTableColumnProps } from '../DynamicTable';
|
import { DynamicTable, DynamicTableColumnProps, DynamicTableItemProps } from '../DynamicTable';
|
||||||
import { EmptyAreaWithCTA } from '../EmptyAreaWithCTA';
|
import { EmptyAreaWithCTA } from '../EmptyAreaWithCTA';
|
||||||
import { ProvisioningBadge } from '../Provisioning';
|
import { ProvisioningBadge } from '../Provisioning';
|
||||||
import { Spacer } from '../Spacer';
|
import { Spacer } from '../Spacer';
|
||||||
|
import { GrafanaMuteTimingsExporter } from '../export/GrafanaMuteTimingsExporter';
|
||||||
|
|
||||||
import { renderTimeIntervals } from './util';
|
import { renderTimeIntervals } from './util';
|
||||||
|
|
||||||
interface Props {
|
const ALL_MUTE_TIMINGS = Symbol('all mute timings');
|
||||||
|
|
||||||
|
type ExportProps = [JSX.Element | null, (muteTiming: string | typeof ALL_MUTE_TIMINGS) => void];
|
||||||
|
|
||||||
|
const useExportMuteTiming = (): ExportProps => {
|
||||||
|
const [muteTimingName, setMuteTimingName] = useState<string | typeof ALL_MUTE_TIMINGS | null>(null);
|
||||||
|
const [isExportDrawerOpen, toggleShowExportDrawer] = useToggle(false);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
setMuteTimingName(null);
|
||||||
|
toggleShowExportDrawer(false);
|
||||||
|
}, [toggleShowExportDrawer]);
|
||||||
|
|
||||||
|
const handleOpen = (receiverName: string | typeof ALL_MUTE_TIMINGS) => {
|
||||||
|
setMuteTimingName(receiverName);
|
||||||
|
toggleShowExportDrawer(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawer = useMemo(() => {
|
||||||
|
if (!muteTimingName || !isExportDrawerOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (muteTimingName === ALL_MUTE_TIMINGS) {
|
||||||
|
// use this drawer when we want to export all mute timings
|
||||||
|
return <GrafanaMuteTimingsExporter onClose={handleClose} />;
|
||||||
|
} else {
|
||||||
|
// use this one for exporting a single mute timing
|
||||||
|
return <GrafanaMuteTimingsExporter muteTimingName={muteTimingName} onClose={handleClose} />;
|
||||||
|
}
|
||||||
|
}, [isExportDrawerOpen, handleClose, muteTimingName]);
|
||||||
|
|
||||||
|
return [drawer, handleOpen];
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MuteTimingsTableProps {
|
||||||
alertManagerSourceName: string;
|
alertManagerSourceName: string;
|
||||||
muteTimingNames?: string[];
|
muteTimingNames?: string[];
|
||||||
hideActions?: boolean;
|
hideActions?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hideActions }: Props) => {
|
export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hideActions }: MuteTimingsTableProps) => {
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
@ -53,9 +90,14 @@ export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hide
|
|||||||
});
|
});
|
||||||
}, [config?.mute_time_intervals, config?.muteTimeProvenances, muteTimingNames]);
|
}, [config?.mute_time_intervals, config?.muteTimeProvenances, muteTimingNames]);
|
||||||
|
|
||||||
const columns = useColumns(alertManagerSourceName, hideActions, setMuteTimingName);
|
|
||||||
const [_, allowedToCreateMuteTiming] = useAlertmanagerAbility(AlertmanagerAction.CreateMuteTiming);
|
const [_, allowedToCreateMuteTiming] = useAlertmanagerAbility(AlertmanagerAction.CreateMuteTiming);
|
||||||
|
|
||||||
|
const [ExportDrawer, showExportDrawer] = useExportMuteTiming();
|
||||||
|
const [exportMuteTimingsSupported, exportMuteTimingsAllowed] = useAlertmanagerAbility(
|
||||||
|
AlertmanagerAction.ExportMuteTimings
|
||||||
|
);
|
||||||
|
const columns = useColumns(alertManagerSourceName, hideActions, setMuteTimingName, showExportDrawer);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={styles.container}>
|
||||||
<Stack direction="row" alignItems="center">
|
<Stack direction="row" alignItems="center">
|
||||||
@ -67,7 +109,7 @@ export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hide
|
|||||||
{!hideActions && items.length > 0 && (
|
{!hideActions && items.length > 0 && (
|
||||||
<Authorize actions={[AlertmanagerAction.CreateMuteTiming]}>
|
<Authorize actions={[AlertmanagerAction.CreateMuteTiming]}>
|
||||||
<LinkButton
|
<LinkButton
|
||||||
className={styles.addMuteButton}
|
className={styles.muteTimingsButtons}
|
||||||
icon="plus"
|
icon="plus"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
href={makeAMLink('alerting/routes/mute-timing/new', alertManagerSourceName)}
|
href={makeAMLink('alerting/routes/mute-timing/new', alertManagerSourceName)}
|
||||||
@ -76,6 +118,18 @@ export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hide
|
|||||||
</LinkButton>
|
</LinkButton>
|
||||||
</Authorize>
|
</Authorize>
|
||||||
)}
|
)}
|
||||||
|
{exportMuteTimingsSupported && (
|
||||||
|
<Button
|
||||||
|
icon="download-alt"
|
||||||
|
className={styles.muteTimingsButtons}
|
||||||
|
variant="secondary"
|
||||||
|
aria-label="export all"
|
||||||
|
disabled={!exportMuteTimingsAllowed}
|
||||||
|
onClick={() => showExportDrawer(ALL_MUTE_TIMINGS)}
|
||||||
|
>
|
||||||
|
Export all
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
{items.length > 0 ? (
|
{items.length > 0 ? (
|
||||||
<DynamicTable items={items} cols={columns} pagination={{ itemsPerPage: 25 }} />
|
<DynamicTable items={items} cols={columns} pagination={{ itemsPerPage: 25 }} />
|
||||||
@ -104,17 +158,27 @@ export const MuteTimingsTable = ({ alertManagerSourceName, muteTimingNames, hide
|
|||||||
onDismiss={() => setMuteTimingName('')}
|
onDismiss={() => setMuteTimingName('')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{ExportDrawer}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function useColumns(alertManagerSourceName: string, hideActions = false, setMuteTimingName: (name: string) => void) {
|
function useColumns(
|
||||||
|
alertManagerSourceName: string,
|
||||||
|
hideActions = false,
|
||||||
|
setMuteTimingName: (name: string) => void,
|
||||||
|
openExportDrawer: (muteTiming: string | typeof ALL_MUTE_TIMINGS) => void
|
||||||
|
) {
|
||||||
const [[_editSupported, allowedToEdit], [_deleteSupported, allowedToDelete]] = useAlertmanagerAbilities([
|
const [[_editSupported, allowedToEdit], [_deleteSupported, allowedToDelete]] = useAlertmanagerAbilities([
|
||||||
AlertmanagerAction.UpdateMuteTiming,
|
AlertmanagerAction.UpdateMuteTiming,
|
||||||
AlertmanagerAction.DeleteMuteTiming,
|
AlertmanagerAction.DeleteMuteTiming,
|
||||||
]);
|
]);
|
||||||
const showActions = !hideActions && (allowedToEdit || allowedToDelete);
|
const showActions = !hideActions && (allowedToEdit || allowedToDelete);
|
||||||
|
|
||||||
|
// const [ExportDrawer, openExportDrawer] = useExportMuteTiming();
|
||||||
|
// const [_, openExportDrawer] = useExportMuteTiming();
|
||||||
|
const [exportSupported, exportAllowed] = useAlertmanagerAbility(AlertmanagerAction.ExportMuteTimings);
|
||||||
|
|
||||||
return useMemo((): Array<DynamicTableColumnProps<MuteTimeInterval>> => {
|
return useMemo((): Array<DynamicTableColumnProps<MuteTimeInterval>> => {
|
||||||
const columns: Array<DynamicTableColumnProps<MuteTimeInterval>> = [
|
const columns: Array<DynamicTableColumnProps<MuteTimeInterval>> = [
|
||||||
{
|
{
|
||||||
@ -176,11 +240,32 @@ function useColumns(alertManagerSourceName: string, hideActions = false, setMute
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
size: '80px',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (exportSupported) {
|
||||||
|
columns.push({
|
||||||
|
id: 'actions',
|
||||||
|
label: '',
|
||||||
|
renderCell: function renderActions({ data }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Menu.Item
|
||||||
|
icon="download-alt"
|
||||||
|
label="Export"
|
||||||
|
ariaLabel="export"
|
||||||
|
disabled={!exportAllowed}
|
||||||
|
data-testid="export"
|
||||||
|
onClick={() => openExportDrawer(data.name)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
size: '100px',
|
size: '100px',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return columns;
|
return columns;
|
||||||
}, [alertManagerSourceName, setMuteTimingName, showActions]);
|
}, [alertManagerSourceName, setMuteTimingName, showActions, exportSupported, exportAllowed, openExportDrawer]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
@ -188,7 +273,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column nowrap;
|
flex-flow: column nowrap;
|
||||||
`,
|
`,
|
||||||
addMuteButton: css`
|
muteTimingsButtons: css`
|
||||||
margin-bottom: ${theme.spacing(2)};
|
margin-bottom: ${theme.spacing(2)};
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
`,
|
`,
|
||||||
|
@ -54,6 +54,10 @@ exports[`alertmanager abilities should report Create / Update / Delete actions a
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
],
|
],
|
||||||
|
"export-mute-timings": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
],
|
||||||
"export-notification-policies": [
|
"export-notification-policies": [
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
@ -155,6 +159,10 @@ exports[`alertmanager abilities should report everything except exporting for Mi
|
|||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
"export-mute-timings": [
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
],
|
||||||
"export-notification-policies": [
|
"export-notification-policies": [
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
@ -256,6 +264,10 @@ exports[`alertmanager abilities should report everything is supported for builti
|
|||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
],
|
],
|
||||||
|
"export-mute-timings": [
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
],
|
||||||
"export-notification-policies": [
|
"export-notification-policies": [
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
|
@ -56,6 +56,7 @@ export enum AlertmanagerAction {
|
|||||||
CreateMuteTiming = 'create-mute-timing',
|
CreateMuteTiming = 'create-mute-timing',
|
||||||
UpdateMuteTiming = 'update-mute-timing',
|
UpdateMuteTiming = 'update-mute-timing',
|
||||||
DeleteMuteTiming = 'delete-mute-timing',
|
DeleteMuteTiming = 'delete-mute-timing',
|
||||||
|
ExportMuteTimings = 'export-mute-timings',
|
||||||
}
|
}
|
||||||
|
|
||||||
// this enum lists all of the available actions we can take on a single alert rule
|
// this enum lists all of the available actions we can take on a single alert rule
|
||||||
@ -235,6 +236,7 @@ export function useAllAlertmanagerAbilities(): Abilities<AlertmanagerAction> {
|
|||||||
[AlertmanagerAction.ViewMuteTiming]: toAbility(AlwaysSupported, notificationsPermissions.read),
|
[AlertmanagerAction.ViewMuteTiming]: toAbility(AlwaysSupported, notificationsPermissions.read),
|
||||||
[AlertmanagerAction.UpdateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.update),
|
[AlertmanagerAction.UpdateMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.update),
|
||||||
[AlertmanagerAction.DeleteMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.delete),
|
[AlertmanagerAction.DeleteMuteTiming]: toAbility(hasConfigurationAPI, notificationsPermissions.delete),
|
||||||
|
[AlertmanagerAction.ExportMuteTimings]: toAbility(isGrafanaFlavoredAlertmanager, notificationsPermissions.read),
|
||||||
};
|
};
|
||||||
|
|
||||||
return abilities;
|
return abilities;
|
||||||
|
Loading…
Reference in New Issue
Block a user